diff --git a/.github/workflows/continuous-integration.yaml b/.github/workflows/continuous-integration.yaml new file mode 100644 index 00000000..50c693af --- /dev/null +++ b/.github/workflows/continuous-integration.yaml @@ -0,0 +1,70 @@ +name: Continuous Integration + +on: + push: + paths-ignore: + - "test-devices/**" + - "reference/**" + pull_request: + paths-ignore: + - "test-devices/**" + - "reference/**" + + +env: + MVN_DEFAULT_ARGS: -B -V -ntp -e -Djansi.passthrough=true -Dstyle.color=always + +jobs: + + test_os: + name: OS ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macOS-latest, windows-latest] + runs-on: ${{ matrix.os }} + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v7 + - name: Setup Java + uses: actions/setup-java@v5 + with: + distribution: 'zulu' + java-version: '25' + - name: Configure unit test GPG key + run: | + echo -n "$UNIT_TEST_SIGNING_KEY" | base64 --decode | gpg --import + env: + UNIT_TEST_SIGNING_KEY: ${{ vars.UNIT_TEST_SIGNING_KEY }} + - name: Build java-does-usb + run: ./mvnw $MVN_DEFAULT_ARGS -DskipTests clean install javadoc:javadoc + working-directory: ./java-does-usb + - name: Determine java-does-usb version + id: libver + run: echo "version=$(./mvnw -q -ntp help:evaluate -Dexpression=project.version -DforceStdout)" >> "$GITHUB_OUTPUT" + working-directory: ./java-does-usb + # Examples pin the latest released version so they double as user-facing documentation; + # override that pin here to compile them against the HEAD version just installed above. + - name: Example "bulk_transfer" + run: ./mvnw $MVN_DEFAULT_ARGS -Djava-does-usb.version=${{ steps.libver.outputs.version }} clean compile + working-directory: ./examples/bulk_transfer + - name: Example "enumerate" + run: ./mvnw $MVN_DEFAULT_ARGS -Djava-does-usb.version=${{ steps.libver.outputs.version }} clean compile + working-directory: ./examples/enumerate + - name: Example "enumerate" (Kotlin) + run: ./gradlew -PjavaDoesUsbVersion=${{ steps.libver.outputs.version }} clean build + working-directory: ./examples/enumerate_kotlin + - name: Example "monitor" + run: ./mvnw $MVN_DEFAULT_ARGS -Djava-does-usb.version=${{ steps.libver.outputs.version }} clean compile + working-directory: ./examples/monitor + - name: Example "monitor" (Kotlin) + run: ./mvnw $MVN_DEFAULT_ARGS -Djava-does-usb.version=${{ steps.libver.outputs.version }} clean package + working-directory: ./examples/monitor_kotlin + - name: Example "stm_dfu" + run: ./mvnw $MVN_DEFAULT_ARGS -Djava-does-usb.version=${{ steps.libver.outputs.version }} clean compile + working-directory: ./examples/stm_dfu + - name: Example "epaper_display" + run: ./mvnw $MVN_DEFAULT_ARGS -Djava-does-usb.version=${{ steps.libver.outputs.version }} clean compile + working-directory: ./examples/epaper_display diff --git a/.github/workflows/test-devices.yaml b/.github/workflows/test-devices.yaml new file mode 100644 index 00000000..0ad9cc6e --- /dev/null +++ b/.github/workflows/test-devices.yaml @@ -0,0 +1,37 @@ +name: Test Devices CI + +on: + push: + paths: + - "test-devices/**" + - ".github/**" + pull_request: + paths: + - "test-devices/**" + - ".github/**" + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v7 + - uses: actions/cache@v5 + with: + path: | + ~/.cache/pip + ~/.platformio/.cache + key: ${{ runner.os }}-pio + - uses: actions/setup-python@v6 + with: + python-version: '3.9' + - name: Install PlatformIO Core + run: pip install --upgrade platformio + + - name: Build loopback firmware + run: pio run + working-directory: ./test-devices/loopback-stm32 + + - name: Build composite firmware + run: pio run + working-directory: ./test-devices/composite-stm32 diff --git a/.gitignore b/.gitignore index 27dc8f0a..b29b5c47 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +java-does-usb/sample.bin + # Maven target/ *.ser diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..9c988cc8 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,77 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What this is + +*Java Does USB* is a pure-Java USB library for communicating with USB devices using custom/vendor-specific protocols (not standard device classes like mass storage or HID). It accesses the OS's native USB APIs through the **Foreign Function and Memory API** — no JNI, no native third-party libraries. Requires **JDK 25** (the `pom.xml` targets release 25; older JDKs need older library versions, see README). + +The publishable library lives in `java-does-usb/`. The repo root also holds `examples/`, `test-devices/` (microcontroller firmware for the hardware test rig), `test-graalvm/` (GraalVM native-image compatibility check), and `reference/`. + +## Commands + +All library commands run from the `java-does-usb/` directory using the Maven wrapper (`./mvnw`). + +```bash +cd java-does-usb + +# Build & install to local Maven repo, skipping hardware tests +./mvnw clean install -DskipTests + +# Run all tests (REQUIRES a physical test device connected — see below) +./mvnw clean test + +# Run a single test class +./mvnw test -Dtest=BulkTransferTest + +# Run a single test method +./mvnw test -Dtest=BulkTransferTest#transferData + +# Build javadoc +./mvnw javadoc:javadoc +``` + +Examples are independent projects, each with its own `mvnw`/`gradlew`. Build one with `cd examples/ && ./mvnw clean compile` (or `./gradlew build` for Kotlin examples). + +### Native access flag + +Any code calling into the library needs native access enabled. Maven surefire passes `--enable-native-access=ALL-UNNAMED` automatically. When running from an IDE or standalone, add the VM option: +`--enable-native-access=net.codecrete.usb` (or `ALL-UNNAMED` if modules are ignored). + +## Testing requires hardware + +The unit tests are integration tests against a real USB device — they will fail with "No test device connected" without one. The device is built from an inexpensive STM32 board flashed with firmware from `test-devices/`: + +- **loopback-stm32** (VID `0xcafe`, PID `0xceaf`): supports all tests. +- **composite-stm32** (VID `0xcafe`, PID `0xcea0`): exercises composite-device handling; some tests are skipped. + +`TestDeviceConfig.java` hard-codes the VID/PID and endpoint numbers for both variants; `TestDeviceBase` auto-detects which one is connected. On Linux, a udev rule is needed for device access (see README "Linux" section). On Windows the test device auto-installs the WinUSB driver via WCID descriptors. + +The `continuous-integration.yaml` workflow only compiles the library and examples on all three OSes — it does **not** run the hardware tests. + +## Architecture + +### Cross-platform abstraction + +The public API is in package `net.codecrete.usb` (`Usb`, `UsbDevice`, `UsbInterface`, `UsbEndpoint`, exceptions, enums). It is the only exported package (`module-info.java`). + +`Usb.java` is the entry point. `Usb.instance()` lazily picks a platform implementation of `UsbDeviceRegistry` based on `os.name`/`os.arch` (Macos/Windows/Linux). Everything else flows through platform-specific subclasses of the abstractions in `net.codecrete.usb.common`: + +- `UsbDeviceRegistry` (common) — singleton that runs a background daemon thread enumerating devices and emitting connect/disconnect events. Each OS subclass implements `monitorDevices()` and calls `setInitialDeviceList()` when the first enumeration completes. +- `UsbDeviceImpl`, `UsbInterfaceImpl`, `UsbEndpointImpl`, `UsbAlternateInterfaceImpl` (common) — shared state and logic; each OS provides a `*UsbDevice` subclass that implements the actual native transfers. +- `EndpointInputStream`/`EndpointOutputStream` + platform `*EndpointInputStream`/`*EndpointOutputStream` — the high-throughput streaming layer. +- `Transfer`/`TransferCompletion` + platform `*Transfer` and `*AsyncTask` — asynchronous transfer plumbing (I/O completion ports on Windows, epoll on Linux, run loop on macOS). +- `ConfigurationParser` (common) — parses raw USB configuration descriptors (portable, byte-level; not OS-specific). + +When adding a feature, expect to touch the common abstraction plus **all three** platform implementations (`linux/`, `macos/`, `windows/`). + +### Native bindings (`gen` packages) + +Native API bindings live under `*/gen/` subpackages and are **generated code**, one package per shared library / macOS framework: + +- **Linux & macOS**: generated with [jextract](https://jdk.java.net/jextract/) via scripts in `java-does-usb/jextract/{linux,macos}/`. The generated code **is committed** to the repo (must be regenerated per-OS; it is portable across x64/ARM64 of the same OS). +- **Windows**: generated at build time by the `windowsapi-maven-plugin` (Windows API Generator) — the function/struct/constant list is configured in `pom.xml`, and the output is **not committed**. + +Some bindings are hand-written rather than generated, because jextract cannot capture thread-local error state (`errno` on Linux, `GetLastError()` on Windows) — those need an extra call-state parameter. See `java-does-usb/jextract/README.md` for the full generation process and jextract's quirks before regenerating. + +Standard USB descriptor structs (device/config/interface/endpoint/string descriptors, setup packet) are modeled in `net.codecrete.usb.usbstandard` as `MemorySegment` views — these are portable and shared across platforms. diff --git a/README.md b/README.md index e6a1f4f9..6ee637da 100644 --- a/README.md +++ b/README.md @@ -1,138 +1,172 @@ -# Java Does USB: USB library for Java +# Java Does USB: USB Library for Java [![javadoc](https://javadoc.io/badge2/net.codecrete.usb/java-does-usb/javadoc.svg)](https://javadoc.io/doc/net.codecrete.usb/java-does-usb) -*Java Does USB* is a library for working with USB devices from Java. It allows to query information about all conntected USB devices and to communicate with USB devices using custom / vendor specific protocols. (It is not intended for communication with standard types of USB devices such as mass storage devices, keyboards etc.) +*Java Does USB* is a Java library for working with USB devices. It allows you to query the connected USB devices and to communicate with them using custom / vendor-specific protocols. It is not intended for communication with standard types of USB devices such as mass storage devices, keyboards etc. -The library uses the [Foreign Function & Memory API](https://github.com/openjdk/panama-foreign) to access native APIs of the underlying operating system. It only uses Java code and does not need JNI or any native third-party library. +The library uses the [Foreign Function and Memory API](https://docs.oracle.com/en/java/javase/22/core/foreign-function-and-memory-api.html) to access native APIs of the underlying operating system. It is written entirely in Java and does not use JNI or any native third-party library. The *Foreign Function and Memory API* has been introduced with Java 22. -The Foreign Function & Memory API (aka as project Panama) is in preview and will be introduced in a future Java version. Currently, it can be tested with Java 19 Early Access (with preview features enabled). -## Prerequisite +## Features -- Java 19 Early Access, preview features enabled (available at https://www.azul.com/downloads/?package=jdk) -- Windows (x86 64-bit), macOS (x86 64-bit, ARM 64-bit) or Linux 64 bit (x86 64-bit, ARM 64-bit) +- Single API for all operating systems (similar to WebUSB API) +- Enumeration of USB devices +- Control, bulk and interrupt transfers (optionally with timeout) +- Notifications about connected/disconnected devices +- Descriptive information about interfaces, settings and endpoints +- High-throughput input/output streams +- Support for alternate interface settings, composite devices and interface association +- Published on Maven Central and licensed under the permissive MIT license -It has been tested with Azul Zulu 19.0.77 EA 34. +## Getting Started -## Features +The library is available at Maven Central. To use it, just add it to your Maven or Gradle project. -### Implemented +If you are using Maven, add the below dependency to your pom.xml: -- Single API for all operating systems (similar to WebUSB API) -- Enumeration of USB devices -- Control, bulk and interrupt transfer -- Notifications about connected/disconnected devices -- Descriptive information about interfaces, settings and endpoints -- Support fo composite devices -- Published on Maven Central +```xml + + net.codecrete.usb + java-does-usb + 1.3.0 + +``` -### To do +If you are using Gradle, add the below dependency to your build.gradle file: -- Transfers with time-out -- Device and USB protocol revision -- Alternate interface settings -- Support for associated interfaces -- Isochronous transfer +```groovy +compile group: 'net.codecrete.usb', name: 'java-does-usb', version: '1.3.0' +``` -### Not planned +```java +package net.codecrete.usb.sample; -- Changing configuration: The library selects the first configuration. Changing configurations is rarely used and not supported on Windows. -- USB 3.0 streams: Not supported on Windows. -- Providing information about USB buses, controllers and hubs +import net.codecrete.usb.Usb; +public class EnumerateDevices { -## Platform-specific considerations + public static void main(String[] args) { + for (var device : Usb.getDevices()) { + System.out.println(device); + } + } +} +``` -### macOS -No special considerations apply. Using this library, a Java application can connect to any USB device and claim any interfaces that aren't claimed by an operating system driver or another application. +## Documentation +- [Code Examples](https://github.com/manuelbl/JavaDoesUSB/wiki/Java-Does-USB-By-Examples) +- [Javadoc](https://javadoc.io/doc/net.codecrete.usb/java-does-usb) -### Linux -*libudev* is used to discover and monitor USB devices. It is closely tied to *systemd*. So the library only runs on Linux distributions with *systemd* and the related libraries. The majority of Linux distributions suitable for desktop computing (as opposed to distributions optimized for containers) fulfill this requirement. -Similar to macOS, a Java application can connect to any USB device and claim any interfaces that aren't claimed by an operating system driver or another application. +## Examples -Most Linux distributions by default set up user accounts without the permission to access USB devices directly. The *udev* system daemon is responsible for assigning permissions to USB devices. It can be configured to assign specific permissions or ownership. +- [Bulk Transfer](examples/bulk_transfer/) demonstrates how to find a USB device, open it and communicate using bulk transfer. +- Enumeration ([Java](examples/enumerate/) / [Kotlin](examples/enumerate_kotlin/)) lists all connected USB devices and displays information about interfaces and endpoints. +- Monitor ([Java](examples/monitor/) / [Kotlin](examples/monitor_kotlin/)) lists the connected USB devices and then monitors for devices being connected and disconnected. +- [Device Firmware Upload (DFU) for STM32](examples/stm_dfu) uploads firmware to STM32 microcontrollers supporting the built-in DFU mode. +- [ePaper Display](examples/epaper_display) communicates with an IT8951 controller for e-Paper displays and shows an image on the display. +- [Enumerate Native](examples/enumerate_native/) and [Monitor Native](examples/monitor_native/) demostrate how to build a native image with GraalVM. -Create a file called `/etc/udev/rules.d/80-javadoesusb-udev.rules` with the below content: -```text -SUBSYSTEM=="usb", ATTRS{idVendor}=="cafe", MODE="0666" -``` -This adds the rule to assign permission mode 0666 to all USB devices with vendor ID `0xCAFE`. This unregistered vendor ID is used by test device. +## Prerequisite + +- Java 25 or higher +- Windows (x86 64-bit, ARM 64-bit), macOS (x86 64-bit, ARM 64-bit) or Linux 64 bit (x86 64-bit, ARM 64-bit) -### Windows -The Windows driver model is more rigid than the ones of macOS or Linux. It's not possible to open any USB device by default. Instead, only devices using the *WinUSB* driver can be opened. This even applies to devices with no installed driver. +## Platform-specific Considerations -USB devices can implement certain control requests to instruct Windows to automatically install the WinUSB driver (search for WCID or Microsoft OS Compatibility Descriptors). The driver can also be manually installed or replaced using a software called [Zadig](https://zadig.akeo.ie/). -The test devices implement the required control requests. So the driver is installed automatically. +### macOS -This library does not yet run reliably on Windows as the Java VM sometimes overwrites the last error code, which is needed for proper function, not just in error cases. It works incorrectly when run in the debugger and sometimes even without the debugger. A future version of the Foreign Function & Memory API will hopefully provide a way to save the last error code. The developers are aware of the issue. +No special considerations apply. Using this library, a Java application can connect to any USB device and claim any interface that isn't claimed by an operating system driver or another application. Standard operating system drivers can be unloaded if the application is run with *root* privileges. It runs both on Macs with Apple Silicon and Intel processors. -The library has not been tested on Windows for ARM64. It might or might not work. +### Linux -### 32-bit versions +*libudev* is used to discover and monitor USB devices. It is closely tied to *systemd*. So the library runs on Linux distributions with *systemd* and the related libraries. The majority of Linux distributions suitable for desktop computing (as opposed to distributions optimized for containers) fulfill this requirement. It runs on both Intel/AMD and ARM processors. -The Foreign Function & Memory API has not been implemented for 32-bit operating systems / JDKs. So it does not support them (and likely never will). +Similar to macOS, a Java application can connect to any USB device and claim any interface that isn't claimed by an operating system driver or another application. Standard operating system drivers can be unloaded (without the need for root privileges). +Most Linux distributions set up user accounts without permissions to access USB devices. The *udev* system daemon is responsible for assigning permissions to USB devices. It can be configured to assign specific permissions or ownership: +Create a file called `/etc/udev/rules.d/80-javadoesusb-udev.rules` with the below content: -## Documentation +```text +SUBSYSTEM=="usb", ATTRS{idVendor}=="cafe", MODE="0666" +``` -- [Javadoc](https://javadoc.io/doc/net.codecrete.usb/java-does-usb) +This adds the rule to assign permission mode 0666 to all USB devices with vendor ID `0xCAFE`. This unregistered vendor ID is used by the test devices. +Without the *udev* rule, it is still possible to enumerate and query all USB devices. -## Examples +### Windows -- [Bulk Transfer](examples/bulk_transfer/) demonstrates how to find a USB device, open it and communicate using bulk transfer. -- [Enumeration](examples/enumerate/) lists all connected USB devices and displays information about interfaces and endpoints. -- [Monitor](examples/monitor/) lists the connected USB devices and then monitors for USB devices being connected and disconnnected. +The Windows driver model is rather rigid. It's not possible to open a USB device unless it uses the *WinUSB* driver. This even applies to devices with no installed driver. Enumerating and querying USB devices is possible independent of the driver. +USB devices can implement special control requests to instruct Windows to automatically install the WinUSB driver (search for *WCID* or *Microsoft OS Compatibility Descriptors*). The WinUSB driver can also be manually installed or replaced using a tool called [Zadig](https://zadig.akeo.ie/). +The test devices implement the required control requests. So the driver is installed automatically. + +The implementation runs on both Windows for Intel/AMD and ARM processors. -## Code generation -Many bindings for the native APIs have been generated with *jextract*. See the [jextract](java-does-usb/jextract) subdirectory for more information. + +## Building from source + +To build from source, run the following command: + +``` +cd java-does-usb +mvn clean install -DskipTests +``` + +The tests are skipped as they require that a special test device is connected to the computer. See the next section for more information. ## Testing -In order to run the unit tests, a special test device must be connected to the computer. See the [loopback-stm32](test-devices/loopback-stm32) directory. +In order to run the unit tests, a special test device must be connected to the computer, which can be easily created from very inexpensive microcontroller boards. Two variants exist: + +- [loopback-stm32](test-devices/loopback-stm32) +- [composite-stm32](test-devices/composite-stm32) + +The test device with the *loopback-stm32* code supports all tests. If the test device with the *composite-stm32* code is connected, some tests are skipped, but the correct handling of composite devices is verified instead. Tests can be run from the command line: ``` +cd java-does-usb mvn clean test ``` -If they are run from an IDE (such as IntelliJ IDEA), you must likely configure VM options to enable preview features and allow native access: +If they are run from an IDE (such as IntelliJ IDEA), you will likely need to configure VM options to allow native access: ``` ---enable-preview --enable-native-access=net.codecrete.usb +--enable-native-access=net.codecrete.usb ``` Or (if modules are ignored): ``` ---enable-preview --enable-native-access=ALL-UNNAMED +--enable-native-access=ALL-UNNAMED ``` -If you don't have the test device, you can get a glimpse at the library running the below command. It enumerates all connected USB devices. -``` -MAVEN_OPTS="--enable-preview --enable-native-access=ALL-UNNAMED" mvn install exec:java -Dexec.classpathScope="test" -DskipTests -Dexec.mainClass="net.codecrete.usb.sample.EnumerateDevices" -``` + +## Code generation + +Many bindings for the native APIs have been generated with *jextract*. See the [jextract](java-does-usb/jextract) subdirectory for more information. For functions that need to retain the error state (`errno` on Linux, `GetLastError()` on Windows), the bindings have been manually written as *jextract* does not support it. + +Since the code can only be generated for the current operating system, it must be generated on separate computers for Linux, Windows and macOS. Thus, the generated code is included in the repository. The generated code is compilable on all operating systems. diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 00000000..3e3511a0 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,100 @@ +# Releasing Java Does USB + +This document describes how a new version of the library is released to Maven Central, and how +version numbers flow through the repository afterwards. + +## Versioning scheme + +- The library follows [Semantic Versioning](https://semver.org/) (`MAJOR.MINOR.PATCH`). +- On the `main` branch, `java-does-usb/pom.xml` always carries the *next*, unreleased version + with a `-SNAPSHOT` suffix (e.g. `1.2.2-SNAPSHOT`). This makes it obvious that a build from `main` + is a development build, not something published to Central. +- `README.md` and the example projects under `examples/` intentionally do **not** track this + SNAPSHOT. They pin the latest *released* version, because they double as user-facing + documentation: a user reading the README or copying an example's `pom.xml`/`build.gradle.kts` + should see the version they can actually `mvn install` from Central today. +- Continuous integration overrides the pinned example version at build time so it still compiles + the examples against `main`'s HEAD (see "How CI stays honest" below). No file needs to be edited + for this to work. + +## Release checklist + +1. **Drop the `-SNAPSHOT` suffix.** + In `java-does-usb/pom.xml`, set `` to the release version, e.g.: + ```bash + cd java-does-usb + ./mvnw versions:set -DnewVersion=1.3.1 -DgenerateBackupPoms=false + ``` + +2. **Update the version everywhere it's pinned for users.** This means: + - `README.md` — the Maven (`...`) and Gradle (`version: '...'`) snippets + under "Getting Started", and the version history table if you're adding an entry. + - Every example's dependency on `java-does-usb`, pinned via a single property so it's one edit + per file (`java-does-usb.version` in the Maven examples, `javaDoesUsbVersion` in the Gradle + one): + - `examples/enumerate/pom.xml` + - `examples/bulk_transfer/pom.xml` + - `examples/monitor/pom.xml` + - `examples/monitor_kotlin/pom.xml` + - `examples/stm_dfu/pom.xml` + - `examples/epaper_display/pom.xml` + - `examples/enumerate_native/pom.xml` + - `examples/monitor_native/pom.xml` + - `examples/enumerate_kotlin/build.gradle.kts` + - The example's *own* project version (its `` / `version = "..."`, separate from the + `java-does-usb.version`/`javaDoesUsbVersion` property), which by convention tracks the library + release it was last verified against, for `enumerate`, `bulk_transfer`, `monitor`, + `monitor_kotlin`, `stm_dfu`, `epaper_display`, and `enumerate_kotlin`. (`enumerate_native` and + `monitor_native` version themselves independently as `1.0-SNAPSHOT` and don't need this.) + - The sample console output hardcoded in each example's own `README.md` (e.g. build log lines + like `[INFO] Building enumerate 1.2.1` or jar filenames like `stm_dfu-1.2.1.jar`), which + embeds the example's own project version from the point above. + + A simple search for the previous version number across `README.md` and `examples/` will locate + every occurrence listed above. + +3. **Commit, tag, and publish.** + ```bash + git add -A + git commit -m "Release 1.2.2" + git tag v1.2.2 + git push origin main v1.2.2 + cd java-does-usb + ./mvnw clean install # GPG passphrase from password store + ./mvnw clean deploy + ``` + +4. **Prepare `main` for the next development iteration.** + ```bash + cd java-does-usb + ./mvnw versions:set -DnewVersion=1.2.3-SNAPSHOT -DgenerateBackupPoms=false + git add pom.xml + git commit -m "Prepare for next development iteration" + git push origin main + ``` + Deliberately do **not** touch `README.md` or the examples in this step — they should keep + pointing at the release just made (1.2.2) until the *next* release checklist run. + +## How CI stays honest + +Because the examples pin the released version, a naive CI setup would silently compile them +against whatever is available on Maven Central instead of the code actually being tested — i.e. +after step 4 above, `main` might contain a breaking change that no CI run would ever catch until +the next release. + +To prevent that, `.github/workflows/continuous-integration.yaml`: + +1. Builds and `install`s the library from the checked-out `pom.xml` (whatever version — released + or `-SNAPSHOT` — is on HEAD) into the local Maven repository. +2. Reads that exact version back out with `mvn help:evaluate -Dexpression=project.version`. +3. Passes it to each example build as an override: + - Maven examples: `-Djava-does-usb.version=` + - The Gradle example (`enumerate_kotlin`): `-PjavaDoesUsbVersion=` + +This means CI always compiles every example against the exact commit under test, while the +committed example files keep showing users the last real release. + +If you add a new example, give its `java-does-usb` dependency the same treatment: introduce a +`java-does-usb.version` property (Maven) or an overridable `javaDoesUsbVersion` val (Gradle) +defaulting to the current released version, add a CI step following the existing pattern, and add +it to the list in step 2 of the release checklist above. diff --git a/examples/bulk_transfer/.mvn/wrapper/maven-wrapper.jar b/examples/bulk_transfer/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 00000000..cb28b0e3 Binary files /dev/null and b/examples/bulk_transfer/.mvn/wrapper/maven-wrapper.jar differ diff --git a/examples/bulk_transfer/.mvn/wrapper/maven-wrapper.properties b/examples/bulk_transfer/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..f3283b08 --- /dev/null +++ b/examples/bulk_transfer/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar diff --git a/examples/bulk_transfer/README.md b/examples/bulk_transfer/README.md index 8168582c..83b4d92e 100644 --- a/examples/bulk_transfer/README.md +++ b/examples/bulk_transfer/README.md @@ -4,16 +4,16 @@ This sample shows how to find a device, open it and transfer data from and to bu ## Prerequisites -- Java 19 +- Java 22 - Apache Maven - 64-bit operating system (Windows, macOS, Linux) - A USB device with bulk IN and OUT endpoints (e.g. the test device, see https://github.com/manuelbl/JavaDoesUSB/tree/main/test-devices/loopback-stm32) ## How to run -### Install Java 19 +### Install Java 22 or higher -Check that *Java 19* is installed: +Check that Java 22 or higher is installed: ```shell $ java -version @@ -31,17 +31,6 @@ $ mvn -version If it is not present, install it, typically using package manager like *Homebrew* on macOS, *Chocolately* on Windows and *apt* on Linux. -### Create the *java-does-usb* library - -Since the *java-does-usb* library is not yet available on Maven Central, it must be built locally: - -```shell -$ cd JavaDoesUSB/java-does-usb -$ mvn install -``` - -The result will be put in your local Maven repository. - ### Run the sample ```shell @@ -50,24 +39,22 @@ $ mvn compile exec:exec [INFO] Scanning for projects... [INFO] [INFO] --------------< net.codecrete.usb.examples:bulk-transfer >-------------- -[INFO] Building bulk-transfer 0.3-SNAPSHOT +[INFO] Building bulk-transfer 1.3.0 [INFO] --------------------------------[ jar ]--------------------------------- [INFO] -[INFO] --- maven-resources-plugin:3.0.2:resources (default-resources) @ bulk-transfer --- -[INFO] Using 'UTF-8' encoding to copy filtered resources. -[INFO] skip non existing resourceDirectory /Users/manuel/Documents/Lab/JavaDoesUSB/examples/bulk_transfer/src/main/resources +[INFO] --- maven-resources-plugin:3.3.1:resources (default-resources) @ bulk-transfer --- +[INFO] skip non existing resourceDirectory /home/user/Documents/JavaDoesUSB/examples/bulk_transfer/src/main/resources [INFO] -[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ bulk-transfer --- -[INFO] Changes detected - recompiling the module! -[INFO] Compiling 1 source file to /Users/manuel/Documents/Lab/JavaDoesUSB/examples/bulk_transfer/target/classes +[INFO] --- maven-compiler-plugin:3.12.1:compile (default-compile) @ bulk-transfer --- +[INFO] Nothing to compile - all classes are up to date. [INFO] -[INFO] --- exec-maven-plugin:3.1.0:exec (default-cli) @ bulk-transfer --- +[INFO] --- exec-maven-plugin:3.1.1:exec (default-cli) @ bulk-transfer --- 6 bytes sent. 6 bytes received. [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ -[INFO] Total time: 3.830 s -[INFO] Finished at: 2022-09-09T14:43:10+02:00 +[INFO] Total time: 1.228 s +[INFO] Finished at: 2024-10-13T16:23:29+01:00 [INFO] ------------------------------------------------------------------------ ``` diff --git a/examples/bulk_transfer/mvnw b/examples/bulk_transfer/mvnw new file mode 100755 index 00000000..8d937f4c --- /dev/null +++ b/examples/bulk_transfer/mvnw @@ -0,0 +1,308 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.2.0 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "$(uname)" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && + JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=$(which readlink) + if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then + if $darwin ; then + javaHome="$(dirname "\"$javaExecutable\"")" + javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" + else + javaExecutable="$(readlink -f "\"$javaExecutable\"")" + fi + javaHome="$(dirname "\"$javaExecutable\"")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=$(cd "$wdir/.." || exit 1; pwd) + fi + # end of workaround + done + printf '%s' "$(cd "$basedir" || exit 1; pwd)" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + # Remove \r in case we run on Windows within Git Bash + # and check out the repository with auto CRLF management + # enabled. Otherwise, we may read lines that are delimited with + # \r\n and produce $'-Xarg\r' rather than -Xarg due to word + # splitting rules. + tr -s '\r\n' ' ' < "$1" + fi +} + +log() { + if [ "$MVNW_VERBOSE" = true ]; then + printf '%s\n' "$1" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname "$0")") +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +log "$MAVEN_PROJECTBASEDIR" + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" +if [ -r "$wrapperJarPath" ]; then + log "Found $wrapperJarPath" +else + log "Couldn't find $wrapperJarPath, downloading it ..." + + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + fi + while IFS="=" read -r key value; do + # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) + safeValue=$(echo "$value" | tr -d '\r') + case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; + esac + done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget > /dev/null; then + log "Found wget ... using wget" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + log "Found curl ... using curl" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + fi + else + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +# If specified, validate the SHA-256 sum of the Maven wrapper jar file +wrapperSha256Sum="" +while IFS="=" read -r key value; do + case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; + esac +done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" +if [ -n "$wrapperSha256Sum" ]; then + wrapperSha256Result=false + if command -v sha256sum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + elif command -v shasum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." + echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." + exit 1 + fi + if [ $wrapperSha256Result = false ]; then + echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 + echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 + echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 + exit 1 + fi +fi + +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --windows "$CLASSPATH") + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +# shellcheck disable=SC2086 # safe args +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/examples/bulk_transfer/mvnw.cmd b/examples/bulk_transfer/mvnw.cmd new file mode 100644 index 00000000..f80fbad3 --- /dev/null +++ b/examples/bulk_transfer/mvnw.cmd @@ -0,0 +1,205 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.2.0 +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file +SET WRAPPER_SHA_256_SUM="" +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B +) +IF NOT %WRAPPER_SHA_256_SUM%=="" ( + powershell -Command "&{"^ + "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ + "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ + " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ + " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ + " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ + " exit 1;"^ + "}"^ + "}" + if ERRORLEVEL 1 goto error +) + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/examples/bulk_transfer/pom.xml b/examples/bulk_transfer/pom.xml index 30ae7f23..e6734349 100644 --- a/examples/bulk_transfer/pom.xml +++ b/examples/bulk_transfer/pom.xml @@ -6,81 +6,83 @@ net.codecrete.usb.examples bulk-transfer - 0.3.0 + 1.3.0 bulk-transfer https://github.com/manuelbl/JavaDoesUSB/examples/bulk_transfer UTF-8 - 19 - 19 + 22 + 22 + 1.3.0 net.codecrete.usb java-does-usb - 0.3.0 + ${java-does-usb.version} - + maven-clean-plugin - 3.1.0 + 3.3.2 maven-resources-plugin - 3.0.2 + 3.3.1 maven-compiler-plugin - 3.8.0 + 3.12.1 - 19 - --enable-preview - 19 - 19 + 22 + 22 + 22 maven-surefire-plugin - 2.22.1 + 3.2.5 + + --enable-native-access=ALL-UNNAMED + maven-jar-plugin - 3.0.2 + 3.3.0 maven-install-plugin - 2.5.2 + 3.1.1 maven-deploy-plugin - 2.8.2 + 3.1.1 maven-site-plugin - 3.7.1 + 3.12.1 maven-project-info-reports-plugin - 3.0.0 + 3.5.0 org.codehaus.mojo exec-maven-plugin - 3.1.0 + 3.1.1 java - --enable-preview --enable-native-access=ALL-UNNAMED -classpath diff --git a/examples/bulk_transfer/src/main/java/net/codecrete/usb/examples/BulkTransfer.java b/examples/bulk_transfer/src/main/java/net/codecrete/usb/examples/BulkTransfer.java index 34a27e95..4e738ef7 100644 --- a/examples/bulk_transfer/src/main/java/net/codecrete/usb/examples/BulkTransfer.java +++ b/examples/bulk_transfer/src/main/java/net/codecrete/usb/examples/BulkTransfer.java @@ -14,7 +14,8 @@ *

* This example assumes that one of the interfaces has two bulk endpoints, * one for sending and one for receiving data. The test device fulfils - * this requirement (see https://github.com/manuelbl/JavaDoesUSB/tree/main/test-devices/loopback-stm32) + * this requirement (see + * loopback-stm32) *

*/ public class BulkTransfer { @@ -26,7 +27,13 @@ public class BulkTransfer { private static final int ENDPOINT_IN = 2; public static void main(String[] args) { - var device = USB.getDevice(new USBDeviceFilter(VID, PID)); + var optionalDevice = Usb.findDevice(VID, PID); + if (optionalDevice.isEmpty()) { + System.out.printf("No USB device with VID=0x%04x and PID=0x%04x found.%n", VID, PID); + return; + } + + var device = optionalDevice.get(); device.open(); device.claimInterface(INTERFACE_NO); @@ -34,7 +41,7 @@ public static void main(String[] args) { device.transferOut(ENDPOINT_OUT, data); System.out.println(data.length + " bytes sent."); - data = device.transferIn(ENDPOINT_IN, 64); + data = device.transferIn(ENDPOINT_IN); System.out.println(data.length + " bytes received."); device.close(); diff --git a/examples/enumerate/.mvn/wrapper/maven-wrapper.jar b/examples/enumerate/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 00000000..cb28b0e3 Binary files /dev/null and b/examples/enumerate/.mvn/wrapper/maven-wrapper.jar differ diff --git a/examples/enumerate/.mvn/wrapper/maven-wrapper.properties b/examples/enumerate/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..f3283b08 --- /dev/null +++ b/examples/enumerate/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar diff --git a/examples/enumerate/README.md b/examples/enumerate/README.md index 0ad96ce1..42683a15 100644 --- a/examples/enumerate/README.md +++ b/examples/enumerate/README.md @@ -4,15 +4,15 @@ This sample enumerates the connected USB devices and provides information about ## Prerequisites -- Java 19 +- Java 22 - Apache Maven - 64-bit operating system (Windows, macOS, Linux) ## How to run -### Install Java 19 +### Install Java 22 or higher -Check that *Java 19* is installed: +Check that Java 22 or higher is installed: ```shell $ java -version @@ -30,17 +30,6 @@ $ mvn -version If it is not present, install it, typically using package manager like *Homebrew* on macOS, *Chocolately* on Windows and *apt* on Linux. -### Create the *java-does-usb* library - -Since the *java-does-usb* library is not yet available on Maven Central, it must be built locally: - -```shell -$ cd JavaDoesUSB/java-does-usb -$ mvn install -``` - -The result will be put in your local Maven repository. - ### Run the sample ```shell @@ -49,21 +38,21 @@ $ mvn compile exec:exec [INFO] Scanning for projects... [INFO] [INFO] ----------------< net.codecrete.usb.examples:enumerate >---------------- -[INFO] Building enumerate 0.3-SNAPSHOT +[INFO] Building enumerate 1.3.0 [INFO] --------------------------------[ jar ]--------------------------------- [INFO] -[INFO] --- maven-resources-plugin:3.0.2:resources (default-resources) @ enumerate --- -[INFO] Using 'UTF-8' encoding to copy filtered resources. -[INFO] skip non existing resourceDirectory /Users/me/Documents/JavaDoesUSB/examples/enumerate/src/main/resources +[INFO] --- maven-resources-plugin:3.3.1:resources (default-resources) @ enumerate --- +[INFO] Copying 1 resource from src/main/resources to target/classes [INFO] -[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ enumerate --- -[INFO] Nothing to compile - all classes are up to date +[INFO] --- maven-compiler-plugin:3.12.1:compile (default-compile) @ enumerate --- +[INFO] Nothing to compile - all classes are up to date. [INFO] -[INFO] --- exec-maven-plugin:3.1.0:exec (default-cli) @ enumerate --- +[INFO] --- exec-maven-plugin:3.1.1:exec (default-cli) @ enumerate --- Device: VID: 0xcafe PID: 0xceaf Manufacturer: JavaDoesUSB Product name: Loopback + Serial number: 35A737883336 ... ``` diff --git a/examples/enumerate/mvnw b/examples/enumerate/mvnw new file mode 100755 index 00000000..8d937f4c --- /dev/null +++ b/examples/enumerate/mvnw @@ -0,0 +1,308 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.2.0 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "$(uname)" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && + JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=$(which readlink) + if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then + if $darwin ; then + javaHome="$(dirname "\"$javaExecutable\"")" + javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" + else + javaExecutable="$(readlink -f "\"$javaExecutable\"")" + fi + javaHome="$(dirname "\"$javaExecutable\"")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=$(cd "$wdir/.." || exit 1; pwd) + fi + # end of workaround + done + printf '%s' "$(cd "$basedir" || exit 1; pwd)" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + # Remove \r in case we run on Windows within Git Bash + # and check out the repository with auto CRLF management + # enabled. Otherwise, we may read lines that are delimited with + # \r\n and produce $'-Xarg\r' rather than -Xarg due to word + # splitting rules. + tr -s '\r\n' ' ' < "$1" + fi +} + +log() { + if [ "$MVNW_VERBOSE" = true ]; then + printf '%s\n' "$1" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname "$0")") +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +log "$MAVEN_PROJECTBASEDIR" + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" +if [ -r "$wrapperJarPath" ]; then + log "Found $wrapperJarPath" +else + log "Couldn't find $wrapperJarPath, downloading it ..." + + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + fi + while IFS="=" read -r key value; do + # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) + safeValue=$(echo "$value" | tr -d '\r') + case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; + esac + done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget > /dev/null; then + log "Found wget ... using wget" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + log "Found curl ... using curl" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + fi + else + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +# If specified, validate the SHA-256 sum of the Maven wrapper jar file +wrapperSha256Sum="" +while IFS="=" read -r key value; do + case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; + esac +done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" +if [ -n "$wrapperSha256Sum" ]; then + wrapperSha256Result=false + if command -v sha256sum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + elif command -v shasum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." + echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." + exit 1 + fi + if [ $wrapperSha256Result = false ]; then + echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 + echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 + echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 + exit 1 + fi +fi + +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --windows "$CLASSPATH") + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +# shellcheck disable=SC2086 # safe args +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/examples/enumerate/mvnw.cmd b/examples/enumerate/mvnw.cmd new file mode 100644 index 00000000..f80fbad3 --- /dev/null +++ b/examples/enumerate/mvnw.cmd @@ -0,0 +1,205 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.2.0 +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file +SET WRAPPER_SHA_256_SUM="" +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B +) +IF NOT %WRAPPER_SHA_256_SUM%=="" ( + powershell -Command "&{"^ + "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ + "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ + " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ + " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ + " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ + " exit 1;"^ + "}"^ + "}" + if ERRORLEVEL 1 goto error +) + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/examples/enumerate/pom.xml b/examples/enumerate/pom.xml index 2e254974..07c06829 100644 --- a/examples/enumerate/pom.xml +++ b/examples/enumerate/pom.xml @@ -6,81 +6,98 @@ net.codecrete.usb.examples enumerate - 0.3.0 + 1.3.0 enumerate https://github.com/manuelbl/JavaDoesUSB/examples/enumerate UTF-8 - 19 - 19 + 22 + 22 + 1.3.0 net.codecrete.usb java-does-usb - 0.3.0 + ${java-does-usb.version} + + + org.tinylog + tinylog-api + 2.6.2 + + + org.tinylog + tinylog-impl + 2.6.2 + + + org.tinylog + jsl-tinylog + 2.6.2 - + maven-clean-plugin - 3.1.0 + 3.3.2 maven-resources-plugin - 3.0.2 + 3.3.1 maven-compiler-plugin - 3.8.0 + 3.12.1 - 19 - --enable-preview - 19 - 19 + 22 + 22 + 22 maven-surefire-plugin - 2.22.1 + 3.2.5 + + --enable-native-access=ALL-UNNAMED + maven-jar-plugin - 3.0.2 + 3.3.0 maven-install-plugin - 2.5.2 + 3.1.1 maven-deploy-plugin - 2.8.2 + 3.1.1 maven-site-plugin - 3.7.1 + 3.12.1 maven-project-info-reports-plugin - 3.0.0 + 3.5.0 org.codehaus.mojo exec-maven-plugin - 3.1.0 + 3.1.1 java - --enable-preview --enable-native-access=ALL-UNNAMED -classpath diff --git a/examples/enumerate/src/main/java/net/codecrete/usb/examples/Enumerate.java b/examples/enumerate/src/main/java/net/codecrete/usb/examples/Enumerate.java index ee89765a..4e387b8f 100644 --- a/examples/enumerate/src/main/java/net/codecrete/usb/examples/Enumerate.java +++ b/examples/enumerate/src/main/java/net/codecrete/usb/examples/Enumerate.java @@ -14,38 +14,43 @@ /** * Sample application enumerating connected USB devices. */ +@SuppressWarnings("java:S106") public class Enumerate { public static void main(String[] args) { // display the already present USB devices - for (var device : USB.getAllDevices()) + for (var device : Usb.getDevices()) printDevice(device); } - private static void printDevice(USBDevice device) { + private static void printDevice(UsbDevice device) { System.out.println("Device:"); - System.out.printf(" VID: 0x%04x%n", device.vendorId()); - System.out.printf(" PID: 0x%04x%n", device.productId()); - if (device.manufacturer() != null) - System.out.printf(" Manufacturer: %s%n", device.manufacturer()); - if (device.product() != null) - System.out.printf(" Product name: %s%n", device.product()); - if (device.serialNumber() != null) - System.out.printf(" Serial number: %s%n", device.serialNumber()); - System.out.printf(" Device class: 0x%02x", device.classCode()); - printInParens(USBClassInfo.lookupClass(device.classCode())); - System.out.printf(" Device subclass: 0x%02x", device.subclassCode()); - printInParens(USBClassInfo.lookupSubclass(device.classCode(), device.subclassCode())); - System.out.printf(" Device protocol: 0x%02x", device.protocolCode()); - printInParens(USBClassInfo.lookupProtocol(device.classCode(), device.subclassCode(), device.protocolCode())); - - for (var intf: device.interfaces()) + System.out.printf(" VID: 0x%04x%n", device.getVendorId()); + System.out.printf(" PID: 0x%04x%n", device.getProductId()); + if (device.getManufacturer() != null) + System.out.printf(" Manufacturer: %s%n", device.getManufacturer()); + if (device.getProduct() != null) + System.out.printf(" Product name: %s%n", device.getProduct()); + if (device.getSerialNumber() != null) + System.out.printf(" Serial number: %s%n", device.getSerialNumber()); + System.out.printf(" Device class: 0x%02x", device.getClassCode()); + printInParens(USBClassInfo.lookupClass(device.getClassCode())); + System.out.printf(" Device subclass: 0x%02x", device.getSubclassCode()); + printInParens(USBClassInfo.lookupSubclass(device.getClassCode(), device.getSubclassCode())); + System.out.printf(" Device protocol: 0x%02x", device.getProtocolCode()); + printInParens(USBClassInfo.lookupProtocol(device.getClassCode(), device.getSubclassCode(), device.getProtocolCode())); + + for (var intf: device.getInterfaces()) printInterface(intf); + printRawDescriptor("Device descriptor", device.getDeviceDescriptor()); + printRawDescriptor("Configuration descriptor", device.getConfigurationDescriptor()); + System.out.println(); System.out.println(); } + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") private static void printInParens(Optional text) { if (text.isPresent()) { System.out.printf(" (%s)%n", text.get()); @@ -54,35 +59,48 @@ private static void printInParens(Optional text) { } } - private static void printInterface(USBInterface intf) { - for (var alt : intf.alternates()) - printAlternate(alt, intf.number(), alt == intf.alternate()); + private static void printInterface(UsbInterface intf) { + for (var alt : intf.getAlternates()) + printAlternate(alt, intf.getNumber(), alt == intf.getCurrentAlternate()); } - private static void printAlternate(USBAlternateInterface alt, int intferaceNumber, boolean isDefault) { + private static void printAlternate(UsbAlternateInterface alt, int intferaceNumber, boolean isDefault) { System.out.println(); if (isDefault) { System.out.printf(" Interface %d%n", intferaceNumber); } else { - System.out.printf(" Interface %d (alternate %d)%n", intferaceNumber, alt.number()); + System.out.printf(" Interface %d (alternate %d)%n", intferaceNumber, alt.getNumber()); } - System.out.printf(" Interface class: 0x%02x", alt.classCode()); - printInParens(USBClassInfo.lookupClass(alt.classCode())); - System.out.printf(" Interface subclass: 0x%02x", alt.subclassCode()); - printInParens(USBClassInfo.lookupProtocol(alt.classCode(), alt.subclassCode(), alt.protocolCode())); - System.out.printf(" Interface protocol: 0x%02x", alt.protocolCode()); - printInParens(USBClassInfo.lookupProtocol(alt.classCode(), alt.subclassCode(), alt.protocolCode())); + System.out.printf(" Interface class: 0x%02x", alt.getClassCode()); + printInParens(USBClassInfo.lookupClass(alt.getClassCode())); + System.out.printf(" Interface subclass: 0x%02x", alt.getSubclassCode()); + printInParens(USBClassInfo.lookupProtocol(alt.getClassCode(), alt.getSubclassCode(), alt.getProtocolCode())); + System.out.printf(" Interface protocol: 0x%02x", alt.getProtocolCode()); + printInParens(USBClassInfo.lookupProtocol(alt.getClassCode(), alt.getSubclassCode(), alt.getProtocolCode())); - for (var endpoint : alt.endpoints()) + for (var endpoint : alt.getEndpoints()) printEndpoint(endpoint); } - private static void printEndpoint(USBEndpoint endpoint) { + private static void printEndpoint(UsbEndpoint endpoint) { + System.out.println(); + System.out.printf(" Endpoint %d%n", endpoint.getNumber()); + System.out.printf(" Direction: %s%n", endpoint.getDirection().name()); + System.out.printf(" Transfer type: %s%n", endpoint.getTransferType().name()); + System.out.printf(" Packet size: %d bytes%n", endpoint.getPacketSize()); + } + + private static void printRawDescriptor(String title, byte[] descriptor) { System.out.println(); - System.out.printf(" Endpoint %d%n", endpoint.number()); - System.out.printf(" Direction: %s%n", endpoint.direction().name()); - System.out.printf(" Transfer type: %s%n", endpoint.transferType().name()); - System.out.printf(" Packet size: %d bytes%n", endpoint.packetSize()); + System.out.println(title); + + int len = descriptor.length; + for (int i = 0; i < len; i += 16) { + System.out.printf("%04x ", i); + for (int j = i; j < Math.min(i + 16, len); j += 1) + System.out.printf(" %02x", descriptor[j] & 255); + System.out.println(); + } } } diff --git a/examples/enumerate/src/main/resources/tinylog.properties b/examples/enumerate/src/main/resources/tinylog.properties new file mode 100644 index 00000000..9cf0c142 --- /dev/null +++ b/examples/enumerate/src/main/resources/tinylog.properties @@ -0,0 +1,3 @@ +writer = console +writer.format = {date: HH:mm:ss.SSS} {level|size=5} [{class|size=40}] {message} +writer.level = info diff --git a/examples/enumerate_kotlin/.gitignore b/examples/enumerate_kotlin/.gitignore new file mode 100644 index 00000000..b1dff0dd --- /dev/null +++ b/examples/enumerate_kotlin/.gitignore @@ -0,0 +1,45 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Kotlin ### +.kotlin + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/examples/enumerate_kotlin/README.md b/examples/enumerate_kotlin/README.md new file mode 100644 index 00000000..63418d03 --- /dev/null +++ b/examples/enumerate_kotlin/README.md @@ -0,0 +1,44 @@ +# USB Device Enumeration (Kotlin) + +This sample enumerates the connected USB devices and provides information about the interfaces and endpoints. + +## Prerequisites + +- Java 25 +- Gradle +- 64-bit operating system (Windows, macOS, Linux) + +## How to run + +### Install Java 25 or higher + +Check that Java 25 or higher is installed: + +```shell +$ java -version +``` + +If not, download and install it, e.g. from [Azul](https://www.azul.com/downloads/?package=jdk). + +### Install Maven + +Check that *Maven* is installed: + +```shell +$ gradle -version +``` + +If it is not present, install it, typically using package manager like *Homebrew* on macOS, *Chocolately* on Windows and *apt* on Linux. + +### Build and run the program + +```shell +$ cd JavaDoesUSB/examples/enumerate_kotlin +$ gradle run +Device: + VID: 0xcafe + PID: 0xceaf + Manufacturer: JavaDoesUSB + Product name: Loopback +... +``` diff --git a/examples/enumerate_kotlin/build.gradle.kts b/examples/enumerate_kotlin/build.gradle.kts new file mode 100644 index 00000000..6d836d13 --- /dev/null +++ b/examples/enumerate_kotlin/build.gradle.kts @@ -0,0 +1,37 @@ +plugins { + kotlin("jvm") version "2.3.21" + application +} + +group = "net.codecrete.usb.examples" +version = "1.3.0" + +val javaDoesUsbVersion = (findProperty("javaDoesUsbVersion") as String?) ?: "1.3.0" +val tinyLogVersion = "2.7.0" + +repositories { + mavenLocal() + mavenCentral() +} + +dependencies { + implementation("net.codecrete.usb:java-does-usb:$javaDoesUsbVersion") + implementation("org.tinylog:tinylog-api:$tinyLogVersion") + implementation("org.tinylog:tinylog-impl:$tinyLogVersion") + implementation("org.tinylog:jsl-tinylog:$tinyLogVersion") + + testImplementation(kotlin("test")) +} + +kotlin { + jvmToolchain(25) +} + +application { + mainClass = "net.codecrete.usb.examples.EnumerateKt" + applicationDefaultJvmArgs = listOf("--enable-native-access=ALL-UNNAMED") +} + +tasks.test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/examples/enumerate_kotlin/gradle.properties b/examples/enumerate_kotlin/gradle.properties new file mode 100644 index 00000000..2610d58f --- /dev/null +++ b/examples/enumerate_kotlin/gradle.properties @@ -0,0 +1,2 @@ +kotlin.code.style=official +org.gradle.configuration-cache=true diff --git a/examples/enumerate_kotlin/gradle/wrapper/gradle-wrapper.jar b/examples/enumerate_kotlin/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..b1b8ef56 Binary files /dev/null and b/examples/enumerate_kotlin/gradle/wrapper/gradle-wrapper.jar differ diff --git a/examples/enumerate_kotlin/gradle/wrapper/gradle-wrapper.properties b/examples/enumerate_kotlin/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..df6a6ad7 --- /dev/null +++ b/examples/enumerate_kotlin/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,9 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip +networkTimeout=10000 +retries=0 +retryBackOffMs=500 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/examples/enumerate_kotlin/gradlew b/examples/enumerate_kotlin/gradlew new file mode 100755 index 00000000..b9bb139f --- /dev/null +++ b/examples/enumerate_kotlin/gradlew @@ -0,0 +1,248 @@ +#!/bin/sh + +# +# Copyright © 2015 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/examples/enumerate_kotlin/gradlew.bat b/examples/enumerate_kotlin/gradlew.bat new file mode 100644 index 00000000..24c62d56 --- /dev/null +++ b/examples/enumerate_kotlin/gradlew.bat @@ -0,0 +1,82 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables, and ensure extensions are enabled +setlocal EnableExtensions + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +"%COMSPEC%" /c exit 1 + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +"%COMSPEC%" /c exit 1 + +:execute +@rem Setup the command line + + + +@rem Execute Gradle +@rem endlocal doesn't take effect until after the line is parsed and variables are expanded +@rem which allows us to clear the local environment before executing the java command +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel + +:exitWithErrorLevel +@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts +"%COMSPEC%" /c exit %ERRORLEVEL% diff --git a/examples/enumerate_kotlin/settings.gradle.kts b/examples/enumerate_kotlin/settings.gradle.kts new file mode 100644 index 00000000..d48b99a0 --- /dev/null +++ b/examples/enumerate_kotlin/settings.gradle.kts @@ -0,0 +1,4 @@ +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" +} +rootProject.name = "enumerate" \ No newline at end of file diff --git a/examples/enumerate_kotlin/src/main/kotlin/net/codecrete/usb/examples/Enumerate.kt b/examples/enumerate_kotlin/src/main/kotlin/net/codecrete/usb/examples/Enumerate.kt new file mode 100644 index 00000000..1d15c964 --- /dev/null +++ b/examples/enumerate_kotlin/src/main/kotlin/net/codecrete/usb/examples/Enumerate.kt @@ -0,0 +1,95 @@ +package net.codecrete.usb.examples + +import net.codecrete.usb.* +import kotlin.math.min + + +fun main() { + Enumerate().enumerate() +} + +class Enumerate { + + private var classInfo = UsbClassInfo() + + fun enumerate() { + for (device in Usb.getDevices()) { + printDevice(device) + } + } + + private fun printDevice(device: UsbDevice) { + println("Device:") + System.out.printf(" VID: 0x%04x%n", device.vendorId) + System.out.printf(" PID: 0x%04x%n", device.productId) + if (device.manufacturer != null) System.out.printf(" Manufacturer: %s%n", device.manufacturer) + if (device.product != null) System.out.printf(" Product name: %s%n", device.product) + if (device.serialNumber != null) System.out.printf(" Serial number: %s%n", device.serialNumber) + System.out.printf(" Device class: 0x%02x", device.classCode) + printInParens(classInfo.lookupClass(device.classCode)) + System.out.printf(" Device subclass: 0x%02x", device.subclassCode) + printInParens(classInfo.lookupSubclass(device.classCode, device.subclassCode)) + System.out.printf(" Device protocol: 0x%02x", device.protocolCode) + printInParens(classInfo.lookupProtocol(device.classCode, device.subclassCode, device.protocolCode)) + for (intf in device.interfaces) printInterface(intf) + printRawDescriptor("Device descriptor", device.deviceDescriptor) + printRawDescriptor("Configuration descriptor", device.configurationDescriptor) + println() + println() + } + + private fun printInParens(text: String?) { + if (text != null) { + System.out.printf(" (%s)%n", text) + } else { + println() + } + } + + private fun printInterface(intf: UsbInterface) { + for (alt in intf.alternates) printAlternate(alt, intf.number, alt === intf.currentAlternate) + } + + private fun printAlternate(alt: UsbAlternateInterface, intferfaceNumber: Int, isDefault: Boolean) { + println() + if (isDefault) { + System.out.printf(" Interface %d%n", intferfaceNumber) + } else { + System.out.printf(" Interface %d (alternate %d)%n", intferfaceNumber, alt.number) + } + System.out.printf(" Interface class: 0x%02x", alt.classCode) + printInParens(classInfo.lookupClass(alt.classCode)) + System.out.printf(" Interface subclass: 0x%02x", alt.subclassCode) + printInParens(classInfo.lookupProtocol(alt.classCode, alt.subclassCode, alt.protocolCode)) + System.out.printf(" Interface protocol: 0x%02x", alt.protocolCode) + printInParens(classInfo.lookupProtocol(alt.classCode, alt.subclassCode, alt.protocolCode)) + for (endpoint in alt.endpoints) + printEndpoint(endpoint) + } + + private fun printEndpoint(endpoint: UsbEndpoint) { + println() + System.out.printf(" Endpoint %d%n", endpoint.number) + System.out.printf(" Direction: %s%n", endpoint.direction.name) + System.out.printf(" Transfer type: %s%n", endpoint.transferType.name) + System.out.printf(" Packet size: %d bytes%n", endpoint.packetSize) + } + + private fun printRawDescriptor(title: String, descriptor: ByteArray) { + println() + println(title) + val len = descriptor.size + var i = 0 + while (i < len) { + System.out.printf("%04x ", i) + var j = i + while (j < min(i + 16, len)) { + System.out.printf(" %02x", descriptor[j].toInt() and 255) + j += 1 + } + println() + i += 16 + } + } + +} diff --git a/examples/enumerate_kotlin/src/main/kotlin/net/codecrete/usb/examples/UsbClassInfo.kt b/examples/enumerate_kotlin/src/main/kotlin/net/codecrete/usb/examples/UsbClassInfo.kt new file mode 100644 index 00000000..56ee48dc --- /dev/null +++ b/examples/enumerate_kotlin/src/main/kotlin/net/codecrete/usb/examples/UsbClassInfo.kt @@ -0,0 +1,234 @@ +package net.codecrete.usb.examples + +import java.io.BufferedReader +import java.io.IOException +import java.io.StringReader +import java.util.* + + +class UsbClassInfo { + private val classCodes: MutableList = ArrayList() + private val subclassCodes: MutableList = ArrayList() + private val protocolCodes: MutableList = ArrayList() + + // List of known device classes, subclasses and + // from http://www.linux-usb.org/usb.ids + private val rawClassData = """ +C 00 (Defined at Interface level) +C 01 Audio + 01 Control Device + 02 Streaming + 03 MIDI Streaming +C 02 Communications + 01 Direct Line + 02 Abstract (modem) + 00 None + 01 AT-commands (v.25ter) + 02 AT-commands (PCCA101) + 03 AT-commands (PCCA101 + wakeup) + 04 AT-commands (GSM) + 05 AT-commands (3G) + 06 AT-commands (CDMA) + fe Defined by command set descriptor + ff Vendor Specific (MSFT RNDIS?) + 03 Telephone + 04 Multi-Channel + 05 CAPI Control + 06 Ethernet Networking + 07 ATM Networking + 08 Wireless Handset Control + 09 Device Management + 0a Mobile Direct Line + 0b OBEX + 0c Ethernet Emulation + 07 Ethernet Emulation (EEM) +C 03 Human Interface Device + 00 No Subclass + 00 None + 01 Keyboard + 02 Mouse + 01 Boot Interface Subclass + 00 None + 01 Keyboard + 02 Mouse +C 05 Physical Interface Device +C 06 Imaging + 01 Still Image Capture + 01 Picture Transfer Protocol (PIMA 15470) +C 07 Printer + 01 Printer + 00 Reserved/Undefined + 01 Unidirectional + 02 Bidirectional + 03 IEEE 1284.4 compatible bidirectional + ff Vendor Specific +C 08 Mass Storage + 01 RBC (typically Flash) + 00 Control/Bulk/Interrupt + 01 Control/Bulk + 50 Bulk-Only + 02 SFF-8020i, MMC-2 (ATAPI) + 03 QIC-157 + 04 Floppy (UFI) + 00 Control/Bulk/Interrupt + 01 Control/Bulk + 50 Bulk-Only + 05 SFF-8070i + 06 SCSI + 00 Control/Bulk/Interrupt + 01 Control/Bulk + 50 Bulk-Only +C 09 Hub + 00 Unused + 00 Full speed (or root) hub + 01 Single TT + 02 TT per port +C 0a CDC Data + 00 Unused + 30 I.430 ISDN BRI + 31 HDLC + 32 Transparent + 50 Q.921M + 51 Q.921 + 52 Q.921TM + 90 V.42bis + 91 Q.932 EuroISDN + 92 V.120 V.24 rate ISDN + 93 CAPI 2.0 + fd Host Based Driver + fe CDC PUF + ff Vendor specific +C 0b Chip/SmartCard +C 0d Content Security +C 0e Video + 00 Undefined + 01 Video Control + 02 Video Streaming + 03 Video Interface Collection +C 58 Xbox + 42 Controller +C dc Diagnostic + 01 Reprogrammable Diagnostics + 01 USB2 Compliance +C e0 Wireless + 01 Radio Frequency + 01 Bluetooth + 02 Ultra WideBand Radio Control + 03 RNDIS + 02 Wireless USB Wire Adapter + 01 Host Wire Adapter Control/Data Streaming + 02 Device Wire Adapter Control/Data Streaming + 03 Device Wire Adapter Isochronous Streaming +C ef Miscellaneous Device + 01 ? + 01 Microsoft ActiveSync + 02 Palm Sync + 02 ? + 01 Interface Association + 02 Wire Adapter Multifunction Peripheral + 03 ? + 01 Cable Based Association + 05 USB3 Vision +C fe Application Specific Interface + 01 Device Firmware Update + 02 IRDA Bridge + 03 Test and Measurement + 01 TMC + 02 USB488 +C ff Vendor Specific Class + ff Vendor Specific Subclass + ff Vendor Specific Protocol + """.trimIndent() + + + /** + * Provides the name of the specified USB class. + * + * @param classCode the USB class code + * @return an optional name + */ + fun lookupClass(classCode: Int): String? { + loadData() + return classCodes + .filter { cc -> cc.classCode == classCode } + .map { cc -> cc.name } + .firstOrNull() + } + + /** + * Provides the name of the specified USB subclass. + * + * @param classCode the USB class code + * @param subclassCode the USB subclass code + * @return an optional name + */ + fun lookupSubclass(classCode: Int, subclassCode: Int): String? { + loadData() + return subclassCodes + .filter { scc -> scc.classCode == classCode && scc.subclassCode == subclassCode } + .map { scc -> scc.name } + .firstOrNull() + } + + /** + * Provides the name of the specified USB protocol. + * + * @param classCode the USB class code + * @param subclassCode the USB subclass code + * @param protocolCode the USB protocol code + * @return an optional name + */ + fun lookupProtocol(classCode: Int, subclassCode: Int, protocolCode: Int): String? { + loadData() + return protocolCodes + .filter { prot -> prot.classCode == classCode && prot.subclassCode == subclassCode && prot.protocolCode == protocolCode } + .map { prot -> prot.name } + .firstOrNull() + } + + private fun loadData() { + if (classCodes.isNotEmpty()) + return + + try { + StringReader(rawClassData).use { stringReader -> + BufferedReader(stringReader).use { reader -> + var classCode = 0 + var subclassCode = 0 + var line = reader.readLine() + while (line != null) { + // protocol line + when { + line.startsWith("\t\t") -> { + val protocol = line.substring(2, 4).toInt(16) + protocolCodes.add(ProtocolCode(classCode, subclassCode, protocol, line.substring(6))) + // subclass line + } + line.startsWith("\t") -> { + subclassCode = line.substring(1, 3).toInt(16) + subclassCodes.add(SubclassCode(classCode, subclassCode, line.substring(5))) + // class line + } + line.startsWith("C ") -> { + classCode = line.substring(2, 4).toInt(16) + classCodes.add(ClassCode(classCode, line.substring(6))) + } + else -> { + error("Invalid raw data") + } + } + line = reader.readLine() + } + } + } + } catch (e: IOException) { + throw RuntimeException(e) + } + } + + internal data class ClassCode(val classCode: Int, val name: String) + + internal data class SubclassCode(val classCode: Int, val subclassCode: Int, val name: String) + + internal data class ProtocolCode(val classCode: Int, val subclassCode: Int, val protocolCode: Int, val name: String) +} \ No newline at end of file diff --git a/examples/enumerate_native/README.md b/examples/enumerate_native/README.md new file mode 100644 index 00000000..f35b1095 --- /dev/null +++ b/examples/enumerate_native/README.md @@ -0,0 +1,47 @@ +# Device Enumeration (Native) + +This example project demonstrates how th build a native application that +enumerates connected devices using [GraalVM](https://www.graalvm.org/) and +this _Java Does USB_ library. + +## Build and Run + +### Prerequisites + +- [GraalVM](https://www.graalvm.org/) 25 or higher +- [Maven](https://maven.apache.org/) 3.9 or higher + + +### Preparation + +GraalVM needs help to learn about the Java FFM downcall and upcall descriptors, +and it needs some help to include all required methods. The relevant items +differ from operating system to operating system. When building the native image, +the operating system must be selected by changing the path in the file +`native-image.properties` in the directory +`src/main/resources/META-INF/native-image/net.codecrete.usb.examples/enumerate_native`. + +```properties +Args = --enable-native-access=ALL-UNNAMED -H:ConfigurationFileDirectories=config/macos +``` + +Note the last word of the line. In this case, it is `macos`. Change this to +`linux` or `windows` if needed. + +In your own Maven project, you might also need to move the file or rather rename +directory. It must be named according to the pattern +`src/main/resources/META-INF/native-image//`. + + +### Building + +```shell +mvn -Pnative package +``` + + +### Running + +```shell +./target/enumerate-native +``` diff --git a/examples/enumerate_native/config/linux/reachability-metadata.json b/examples/enumerate_native/config/linux/reachability-metadata.json new file mode 100644 index 00000000..e1746c04 --- /dev/null +++ b/examples/enumerate_native/config/linux/reachability-metadata.json @@ -0,0 +1,111 @@ +{ + "foreign": { + "downcalls": [ + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint", + "jlong", + "void*" + ], + "options": { + "captureCallState": true, + "firstVariadicArg": 2 + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint", + "void*", + "jint", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "void*", + "parameterTypes": [] + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "jint" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint", + "jint", + "jint", + "void*" + ], + "options": { + "captureCallState": true + } + } + ] + } +} \ No newline at end of file diff --git a/examples/enumerate_native/config/macos/reachability-metadata.json b/examples/enumerate_native/config/macos/reachability-metadata.json new file mode 100644 index 00000000..113fb587 --- /dev/null +++ b/examples/enumerate_native/config/macos/reachability-metadata.json @@ -0,0 +1,318 @@ +{ + "foreign": { + "upcalls": [ + { + "returnType": "void", + "parameterTypes": [ + "void*", + "jint", + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "jint", + "void*", + "void*" + ] + }, + { + "returnType": "void", + "parameterTypes": [ + "void*", + "jint" + ] + } + ], + "downcalls": [ + { + "returnType": "void", + "parameterTypes": [ + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "void", + "parameterTypes": [ + "void*", + "struct(jlong,jlong)", + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "void*", + "jlong" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jint", + "void*", + "jdouble", + "jdouble", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*", + "void*", + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint" + ] + }, + { + "returnType": "jlong", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint", + "void*", + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "jint", + "void*", + "void*", + "jint" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "jint", + "void*", + "void*" + ] + }, + { + "returnType": "jbyte", + "parameterTypes": [ + "void*", + "jlong", + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "struct(jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte)" + ] + }, + { + "returnType": "void", + "parameterTypes": [] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte", + "void*", + "jint", + "jint", + "jint", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte", + "void*", + "void*", + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "jlong", + "parameterTypes": [] + }, + { + "returnType": "void", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint", + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "jint" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "void*", + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte", + "void*", + "jint", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "struct(jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte)", + "void*" + ] + }, + { + "returnType": "struct(jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte)", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "void", + "parameterTypes": [ + "void*", + "jint" + ] + } + ] + }, + "reflection": [ + { + "type": "net.codecrete.usb.macos.gen.corefoundation.CFMessagePortCreateLocal$callout$Function", + "methods": [ + { + "name": "apply", + "parameterTypes": [ + "java.lang.foreign.MemorySegment", + "int", + "java.lang.foreign.MemorySegment", + "java.lang.foreign.MemorySegment" + ] + } + ] + }, + { + "type": "net.codecrete.usb.macos.gen.iokit.IOServiceAddMatchingNotification$callback$Function", + "methods": [ + { + "name": "apply", + "parameterTypes": [ + "java.lang.foreign.MemorySegment", + "int" + ] + } + ] + } + ] +} diff --git a/examples/enumerate_native/config/windows/reachability-metadata.json b/examples/enumerate_native/config/windows/reachability-metadata.json new file mode 100644 index 00000000..f3abe566 --- /dev/null +++ b/examples/enumerate_native/config/windows/reachability-metadata.json @@ -0,0 +1,377 @@ +{ + "foreign": { + "downcalls": [ + { + "returnType": "jint", + "parameterTypes": [ + "jint", + "void*", + "jint", + "jint", + "void*", + "jint", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jlong" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jlong", + "jint", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jlong", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jlong", + "void*", + "jint", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jlong", + "void*", + "void*", + "jint", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jlong", + "void*", + "void*", + "jint", + "void*", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jlong", + "void*", + "void*", + "void*", + "void*", + "jint", + "void*", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte", + "jint", + "jint", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte", + "void*", + "jint", + "void*", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jint", + "void*", + "jint", + "void*", + "jint", + "void*", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*", + "jint", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*", + "void*", + "void*", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*", + "void*", + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "jlong", + "parameterTypes": [ + "void*", + "jint", + "jlong", + "jlong" + ] + }, + { + "returnType": "jlong", + "parameterTypes": [ + "void*", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jlong", + "parameterTypes": [ + "void*", + "void*", + "void*", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jshort", + "parameterTypes": [ + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "void*", + "parameterTypes": [ + "jint", + "void*", + "void*", + "jint", + "jint", + "jint", + "jint", + "jint", + "void*", + "void*", + "void*", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "void*", + "parameterTypes": [ + "jlong", + "void*", + "jint", + "jint", + "jint", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "jint", + "jint", + "void*", + "jint", + "jint", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "void*", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "void*", + "jlong", + "jint" + ], + "options": { + "captureCallState": true + } + } + ], + "upcalls": [ + { + "returnType": "jlong", + "parameterTypes": [ + "void*", + "jint", + "jlong", + "jlong" + ] + } + ] + }, + "reflection": [ + { + "type": "windows.win32.ui.windowsandmessaging.WNDPROC$Function", + "methods": [ + { + "name": "invoke", + "parameterTypes": [ + "java.lang.foreign.MemorySegment", + "int", + "long", + "long" + ] + } + ] + } + ] +} diff --git a/examples/enumerate_native/pom.xml b/examples/enumerate_native/pom.xml new file mode 100644 index 00000000..c6a9c555 --- /dev/null +++ b/examples/enumerate_native/pom.xml @@ -0,0 +1,85 @@ + + 4.0.0 + + net.codecrete.usb.examples + enumerate_native + jar + 1.0-SNAPSHOT + enumerate_native + https://www.github.com/manuelbl/java-does-usb + + + 25 + 25 + UTF-8 + 0.11.0 + 1.3.0 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.14.0 + + true + + + + org.apache.maven.plugins + maven-jar-plugin + 3.4.2 + + + + net.codecrete.usb.examples.App + true + + + + + + + + + + net.codecrete.usb + java-does-usb + ${java-does-usb.version} + + + junit + junit + 3.8.1 + test + + + + + + native + + + + org.graalvm.buildtools + native-maven-plugin + ${native.maven.plugin.version} + true + + + build-native + + compile-no-fork + + package + + + + + + + + + diff --git a/examples/enumerate_native/src/main/java/net/codecrete/usb/examples/App.java b/examples/enumerate_native/src/main/java/net/codecrete/usb/examples/App.java new file mode 100644 index 00000000..943f20e3 --- /dev/null +++ b/examples/enumerate_native/src/main/java/net/codecrete/usb/examples/App.java @@ -0,0 +1,19 @@ +// +// Java Does USB +// Copyright (c) 2025 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.examples; + +import net.codecrete.usb.Usb; + +public class App +{ + public static void main(String[] args) { + for (var device : Usb.getDevices()) { + System.out.println(device); + } + } +} diff --git a/examples/enumerate_native/src/main/resources/META-INF/native-image/net.codecrete.usb.examples/enumerate_native/native-image.properties b/examples/enumerate_native/src/main/resources/META-INF/native-image/net.codecrete.usb.examples/enumerate_native/native-image.properties new file mode 100644 index 00000000..4e4f3471 --- /dev/null +++ b/examples/enumerate_native/src/main/resources/META-INF/native-image/net.codecrete.usb.examples/enumerate_native/native-image.properties @@ -0,0 +1 @@ +Args = --enable-native-access=ALL-UNNAMED -H:ConfigurationFileDirectories=config/macos diff --git a/examples/epaper_display/.mvn/wrapper/maven-wrapper.jar b/examples/epaper_display/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 00000000..cb28b0e3 Binary files /dev/null and b/examples/epaper_display/.mvn/wrapper/maven-wrapper.jar differ diff --git a/examples/epaper_display/.mvn/wrapper/maven-wrapper.properties b/examples/epaper_display/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..f3283b08 --- /dev/null +++ b/examples/epaper_display/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar diff --git a/examples/epaper_display/README.md b/examples/epaper_display/README.md new file mode 100644 index 00000000..c45f7e08 --- /dev/null +++ b/examples/epaper_display/README.md @@ -0,0 +1,84 @@ +# E-Paper Display + +This sample shows how to communicate with an IT8951 controller for e-paper displays, e.g. [Waveshare E-Ink display HAT for Raspberry Pi](https://www.waveshare.com/9.7inch-e-paper-hat.htm) + +## Prerequisites + +- Java 22 +- Apache Maven +- 64-bit operating system (macOS, Linux, Windows) +- IT8951 controller + +On Windows, the display controller's driver must be replaced with the *WinUSB* driver +using [Zadig](https://zadig.akeo.ie/). + +On macOS, root privileges are required to successfully run the sample. (The standard driver will +be temporarily detached.) + +## How to run + +### Install Java 22 or higher + +Check that Java 22 or higher is installed: + +```shell +$ java -version +``` + +If not, download and install it, e.g. from [Azul](https://www.azul.com/downloads/?package=jdk). + +### Install Maven + +Check that *Maven* is installed: + +```shell +$ mvn -version +``` + +If it is not present, install it, typically using package manager like *Homebrew* on macOS, *Chocolately* on Windows and *apt* on Linux. + +### Run the sample + +```shell +$ cd JavaDoesUSB/examples/epaper_display +$ mvn compile exec:exec +[INFO] Scanning for projects... +[INFO] +[INFO] -------------< net.codecrete.usb.examples:epaper-display >-------------- +[INFO] Building epaper-display 1.3.0 +[INFO] --------------------------------[ jar ]--------------------------------- +[INFO] +[INFO] --- maven-resources-plugin:3.3.1:resources (default-resources) @ epaper-display --- +[INFO] skip non existing resourceDirectory /home/user/Documents/JavaDoesUSB/examples/epaper_display/src/main/resources +[INFO] +[INFO] --- maven-compiler-plugin:3.12.1:compile (default-compile) @ epaper-display --- +[INFO] Nothing to compile - all classes are up to date. +[INFO] +[INFO] --- exec-maven-plugin:3.1.1:exec (default-cli) @ epaper-display --- +Display size: 1200 x 825 +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD SUCCESS +[INFO] ------------------------------------------------------------------------ +[INFO] Total time: 2.247 s +[INFO] Finished at: 2024-10-13T16:48:43+01:00 +[INFO] ------------------------------------------------------------------------ +``` + +### Run on macOS + +In order to run the sample with root privileges, the best approach is to build it first without +root privileges and then run it as root without Maven: + +```shell +$ cd JavaDoesUSB/examples/epaper_display +$ mvn compile +[INFO] Scanning for projects... +... +[INFO] ------------------------------------------------------------------------ +$ sudo -i +Password: +$ cd /Users/me/Documents/JavaDoesUSB/examples/epaper_display +$ export JAVA_HOME=/Library/Java/JavaVirtualMachines/zulu-20.jdk/Contents/Home +$ $JAVA_HOME/bin/java --enable-native-access=ALL-UNNAMED -cp target/classes:/Users/me/.m2/repository/net/codecrete/usb/java-does-usb/1.3.0/java-does-usb-1.3.0.jar net.codecrete.usb.examples.EPaperDisplay +Display size: 1200 x 825 +``` diff --git a/examples/epaper_display/mvnw b/examples/epaper_display/mvnw new file mode 100755 index 00000000..8d937f4c --- /dev/null +++ b/examples/epaper_display/mvnw @@ -0,0 +1,308 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.2.0 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "$(uname)" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && + JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=$(which readlink) + if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then + if $darwin ; then + javaHome="$(dirname "\"$javaExecutable\"")" + javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" + else + javaExecutable="$(readlink -f "\"$javaExecutable\"")" + fi + javaHome="$(dirname "\"$javaExecutable\"")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=$(cd "$wdir/.." || exit 1; pwd) + fi + # end of workaround + done + printf '%s' "$(cd "$basedir" || exit 1; pwd)" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + # Remove \r in case we run on Windows within Git Bash + # and check out the repository with auto CRLF management + # enabled. Otherwise, we may read lines that are delimited with + # \r\n and produce $'-Xarg\r' rather than -Xarg due to word + # splitting rules. + tr -s '\r\n' ' ' < "$1" + fi +} + +log() { + if [ "$MVNW_VERBOSE" = true ]; then + printf '%s\n' "$1" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname "$0")") +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +log "$MAVEN_PROJECTBASEDIR" + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" +if [ -r "$wrapperJarPath" ]; then + log "Found $wrapperJarPath" +else + log "Couldn't find $wrapperJarPath, downloading it ..." + + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + fi + while IFS="=" read -r key value; do + # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) + safeValue=$(echo "$value" | tr -d '\r') + case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; + esac + done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget > /dev/null; then + log "Found wget ... using wget" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + log "Found curl ... using curl" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + fi + else + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +# If specified, validate the SHA-256 sum of the Maven wrapper jar file +wrapperSha256Sum="" +while IFS="=" read -r key value; do + case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; + esac +done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" +if [ -n "$wrapperSha256Sum" ]; then + wrapperSha256Result=false + if command -v sha256sum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + elif command -v shasum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." + echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." + exit 1 + fi + if [ $wrapperSha256Result = false ]; then + echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 + echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 + echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 + exit 1 + fi +fi + +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --windows "$CLASSPATH") + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +# shellcheck disable=SC2086 # safe args +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/examples/epaper_display/mvnw.cmd b/examples/epaper_display/mvnw.cmd new file mode 100644 index 00000000..f80fbad3 --- /dev/null +++ b/examples/epaper_display/mvnw.cmd @@ -0,0 +1,205 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.2.0 +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file +SET WRAPPER_SHA_256_SUM="" +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B +) +IF NOT %WRAPPER_SHA_256_SUM%=="" ( + powershell -Command "&{"^ + "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ + "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ + " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ + " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ + " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ + " exit 1;"^ + "}"^ + "}" + if ERRORLEVEL 1 goto error +) + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/examples/epaper_display/pom.xml b/examples/epaper_display/pom.xml new file mode 100644 index 00000000..b69b1432 --- /dev/null +++ b/examples/epaper_display/pom.xml @@ -0,0 +1,96 @@ + + + + 4.0.0 + + net.codecrete.usb.examples + epaper-display + 1.3.0 + + epaper-display + https://github.com/manuelbl/JavaDoesUSB/examples/epaper_display + + + UTF-8 + 22 + 22 + 1.3.0 + + + + + net.codecrete.usb + java-does-usb + ${java-does-usb.version} + + + + + + + + + maven-clean-plugin + 3.3.2 + + + + maven-resources-plugin + 3.3.1 + + + maven-compiler-plugin + 3.12.1 + + 22 + 22 + 22 + + + + maven-surefire-plugin + 3.2.5 + + --enable-native-access=ALL-UNNAMED + + + + maven-jar-plugin + 3.3.0 + + + maven-install-plugin + 3.1.1 + + + maven-deploy-plugin + 3.1.1 + + + + maven-site-plugin + 3.12.1 + + + maven-project-info-reports-plugin + 3.5.0 + + + org.codehaus.mojo + exec-maven-plugin + 3.1.1 + + java + + --enable-native-access=ALL-UNNAMED + -classpath + + net.codecrete.usb.examples.EPaperDisplay + + + + + + + diff --git a/examples/epaper_display/src/main/java/net/codecrete/usb/examples/EPaperDisplay.java b/examples/epaper_display/src/main/java/net/codecrete/usb/examples/EPaperDisplay.java new file mode 100644 index 00000000..75dbd0f2 --- /dev/null +++ b/examples/epaper_display/src/main/java/net/codecrete/usb/examples/EPaperDisplay.java @@ -0,0 +1,91 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.examples; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +/** + * Example program for displaying an image and text on an e-paper display + * controlled by an IT8951 controller. + */ +public class EPaperDisplay { + + public static void main(String[] args) throws IOException { + // load image of tiger + var tigerImage = ImageIO.read(new File("tiger.jpg")); + + // connect to display controller + var display = new IT8951Driver(); + display.open(); + + var width = display.info().width(); + var height = display.info().height(); + System.out.printf("Display size: %d x %d%n", width, height); + + // resize image to fit display (converting it to grayscale) + var image = resizedImage(tigerImage, width, height); + + // add text to image + Graphics2D g = (Graphics2D) image.getGraphics(); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g.setFont(new Font("Arial", Font.BOLD, 40)); + g.drawString("Java Does USB", 30, height - 40); + g.dispose(); + + // display image + display.displayImage(image, 0, 0); + + display.close(); + } + + /** + * Returns a copy of the image, resized to the specified width and height. + *

+ * To prevent distortion, the image is cropped to fit the target width and height. + *

+ *

+ * The new image will be in 8-bit grayscale. + *

+ * @param image image to resize + * @param width width, in pixels + * @param height height, in pixels + * @return resized image + */ + static BufferedImage resizedImage(BufferedImage image, int width, int height) { + + var resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); + var g = (Graphics2D) resizedImage.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + int srcWidth = image.getWidth(); + int srcHeight = image.getHeight(); + var targetAspectRatio = (double) width / height; + var srcAspectRatio = (double) srcWidth / srcHeight; + + if (targetAspectRatio > srcAspectRatio) { + // target image is wider than source image - fit width and cut top and bottom + var modifiedHeight = width / srcAspectRatio; + var cutY = (int) Math.round((modifiedHeight - height) / 2); + g.drawImage(image, 0, -cutY, width, height + cutY, 0, 0, srcWidth, srcHeight, null); + } else { + // target image is narrower than source image - fit height and cut left and right + var modifiedWidth = height * srcAspectRatio; + var cutX = (int) Math.round((modifiedWidth - width) / 2); + g.drawImage(image, -cutX, 0, width + cutX, height, 0, 0, srcWidth, srcHeight, null); + } + + g.dispose(); + return resizedImage; + } +} diff --git a/examples/epaper_display/src/main/java/net/codecrete/usb/examples/IT8951Driver.java b/examples/epaper_display/src/main/java/net/codecrete/usb/examples/IT8951Driver.java new file mode 100644 index 00000000..47ae2b29 --- /dev/null +++ b/examples/epaper_display/src/main/java/net/codecrete/usb/examples/IT8951Driver.java @@ -0,0 +1,371 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.examples; + +import net.codecrete.usb.Usb; +import net.codecrete.usb.UsbDevice; + +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferByte; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Arrays; +import java.util.Random; + +/** + * Driver for the IT8951 e-paper display controller. + */ +public class IT8951Driver { + + private static final int ENDPOINT_IN = 1; + private static final int ENDPOINT_OUT = 2; + + private static final byte[] GET_SYS_CMD = { + (byte)0xfe, 0, 0x38, 0x39, 0x35, 0x31, (byte)0x80, 0, 0x01, 0, 0x02, 0, 0, 0, 0, 0 + }; + private static final byte[] LD_IMG_AREA_CMD = { + (byte)0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, (byte)0xa2, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + private static final byte[] DPY_AREA_CMD = { + (byte)0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, (byte)0x94, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + + private UsbDevice device; + private int sequenceNo = 1; + + private DisplayInfo displayInfo; + + /** + * Opens the controller for communication. + * + * @throws IllegalStateException if no IT8951 device is found + */ + public void open() { + var optionalDevice = Usb.findDevice(0x048d, 0x8951); + if (optionalDevice.isEmpty()) + throw new IllegalStateException("No IT8951 device found"); + + device = optionalDevice.get(); + device.detachStandardDrivers(); + device.open(); + try { + device.claimInterface(0); + var sysInfoBytes = readCommand(GET_SYS_CMD, DisplayInfo.LENGTH); + displayInfo = DisplayInfo.from(sysInfoBytes); + + } catch (Exception t) { + device.close(); + throw t; + } + } + + /** + * Displays the provided image at the specified position. + *

+ * The provided image must be an 8-bit grayscale image, + * and it must fully fit within the bounds of the display. + *

+ *

+ * The image is rendered in 4-bit grayscale mode. For the + * reduction to 4-bit, Floyd-Steinberg dithering is used. + *

+ * + * @param image image + * @param x x-position + * @param y y-position + */ + public void displayImage(BufferedImage image, int x, int y) { + int width = image.getWidth(); + int height = image.getHeight(); + var stride = image.getRaster().getDataBuffer().getSize() / image.getHeight(); + int address = info().imageBufBase; + + // split into bands to no exceed 60KB transfer size + int bandHeight = (60000 - 20) / width; + + var errors = createInitialDitheringErrors(width); + + for (int yOffset = 0; yOffset < height; yOffset += bandHeight) { + bandHeight = Math.min(bandHeight, height - yOffset); + var pixelData = pixelsFromImage(image, 0, yOffset, width, bandHeight); + errors = dither(pixelData, stride, errors); + loadImageArea(new Area(address, x, y + yOffset, width, bandHeight), pixelData); + } + + displayArea(new DisplayArea(address, 2, x, y, width, height, 1)); + } + + /** + * Close the communication with the controller. + */ + public void close() { + device.close(); + device.attachStandardDrivers(); + } + + /** + * Gets information about the display and controller. + * + * @return information + */ + public DisplayInfo info() { + return displayInfo; + } + + /** + * Execute a read command. + * @param command command + * @param expectedLength expected length of received data (in bytes) + * @return received data + */ + private byte[] readCommand(byte[] command, int expectedLength) { + var cmd = createCommandBlock(command, expectedLength, true); + device.transferOut(ENDPOINT_OUT, cmd); + byte[] result = device.transferIn(ENDPOINT_IN, 1000); + readStatus(); + return result; + } + + /** + * Execute a write command. + * @param command command + * @param data1 part 1 of data to be written + * @param data2 part 2 of data to be written (or {@code null} if there is no second part) + */ + private void writeCommand(byte[] command, byte[] data1, byte[] data2) { + var cmd = createCommandBlock(command, data1.length + ((data2 != null) ? data2.length : 0), false); + device.transferOut(ENDPOINT_OUT, cmd); + device.transferOut(ENDPOINT_OUT, data1); + if (data2 != null) + device.transferOut(ENDPOINT_OUT, data2); + readStatus(); + } + + /** + * Execute a write command. + * @param command command + * @param data data to be written + */ + private void writeCommand(byte[] command, byte[] data) { + writeCommand(command, data, null); + } + + /** + * Wraps the given command in a command block. + * @param command command + * @param dataLength length of data to be read or written (in bytes) + * @param isDirectionIn indicates if data direction is in (from device to host) + * @return the command block + */ + private byte[] createCommandBlock(byte[] command, int dataLength, boolean isDirectionIn) { + var cmd = ByteBuffer.allocate(15 + command.length).order(ByteOrder.LITTLE_ENDIAN); + cmd.putInt(0x43425355); // signature + cmd.putInt(sequenceNo); + sequenceNo += 1; + cmd.putInt(dataLength); // data transfer length + cmd.put((byte)(isDirectionIn ? 0x80 : 0x00)); // flags + cmd.put((byte)0); // logical unit number + cmd.put((byte)command.length); + cmd.put(command); + return cmd.array(); + } + + private void loadImageArea(Area area, byte[] pixelData) { + writeCommand(LD_IMG_AREA_CMD, area.toByteArray(), pixelData); + } + + private void displayArea(DisplayArea area) { + writeCommand(DPY_AREA_CMD, area.toByteArray()); + } + + private static byte[] pixelsFromImage(BufferedImage image, int x, int y, int w, int h) { + var buffer = (DataBufferByte) image.getRaster().getDataBuffer(); + var data = buffer.getData(); + var stride = buffer.getSize() / image.getHeight(); + + byte[] pixels = new byte[w * h]; + for (int iy = 0; iy < h; iy += 1) + System.arraycopy(data, (iy + y) * stride + x, pixels, iy * w, w); + + return pixels; + } + + /** + * Initializes the errors for dithering. + * @param width image width (in pixels) + * @return initial errors + */ + private static int[] createInitialDitheringErrors(int width) { + // initialize errors with random values in the range [-127, 128]. + int[] errors = new int[width + 2]; + var random = new Random(); + + for (int i = 0; i < errors.length; i++) + errors[i] = random.nextInt(256) - 127; + + return errors; + } + + /** + * Quantizes the provided grayscale pixel data to 16 levels of gray + * using Floyd-Steinberg dithering. + *

+ * The pixel data is quantized in place. The resulting grayscale values + * are 0, 16, 32, ... 240. + *

+ *

+ * The pixel data consists of a single byte per pixel. A new pixel line + * starts every stride bytes. The actual image width can be + * slightly shorter. + *

+ * @param pixels array with pixel data to be modified + * @param stride the + * @param errors errors carried forward from the previous band + * @return errors to be carried forward to the next band + */ + private static int[] dither(byte[] pixels, int stride, int[] errors) { + int[] currentErrors = errors; + int[] nextErrors = new int[errors.length]; + + for (int offset = 0; offset < pixels.length; offset += stride) { + Arrays.fill(nextErrors, 0); + ditherRow(pixels, offset, currentErrors, nextErrors); + + // swap error arrays + int[] errs = currentErrors; + currentErrors = nextErrors; + nextErrors = errs; + } + + return currentErrors; + } + + private static void ditherRow(byte[] pixels, int offset, int[] currentErrors, int[] nextErrors) { + int w = currentErrors.length - 2; + for (int i = 0; i < w; i += 1) { + // scale range from [0, 255] to [0, 240] + int targetValue = (((pixels[offset + i] & 0xff) * 16 + currentErrors[i + 1]) * 15 + 127) / 255; + int quantizedValue; + if (targetValue <= 0) + quantizedValue = 0; + else if (targetValue >= 240) + quantizedValue = 240; + else + quantizedValue = (targetValue + 8) & 0xf0; + pixels[offset + i] = (byte) quantizedValue; + + int quantizationError = targetValue - quantizedValue; + currentErrors[i + 2] += quantizationError * 7; + nextErrors[i] += quantizationError * 3; + nextErrors[i + 1] += quantizationError * 5 ; + nextErrors[i + 2] += quantizationError; + } + } + + /** + * Read the status block. + * @return status block + */ + private Status readStatus() { + var result = device.transferIn(ENDPOINT_IN, 1000); + if (result.length == 13) + return Status.from(result); + + throw new IllegalStateException(String.format("Unexpected length of status block (%d)", result.length)); + } + + /** + * Display and controller information + * + * @param standardCmdNo standard command number 2T-con Communication Protocol + * @param extendedCmdNo extended command number + * @param signature signature (0x31 0x35 0x39 0x38 (8951)) + * @param version command table version + * @param width display width (in pixels) + * @param height display height (in pixels) + * @param updateBufBase update buffer base address + * @param imageBufBase image buffer base address (index 0) + * @param temperatureNo temperature segment number + * @param modeNo display mode number + * @param frameCount frame count for each mode (8 elements) + * @param numImgBuf number of image buffers + */ + public record DisplayInfo( + int standardCmdNo, + int extendedCmdNo, + int signature, + int version, + int width, + int height, + int updateBufBase, + int imageBufBase, + int temperatureNo, + /// Display mode + int modeNo, + int[] frameCount, // 8 elements + int numImgBuf + ) { + static DisplayInfo from(byte[] bytes) { + var buf = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN); + int standardCmdNo = buf.getInt(); + int extendedCmdNo = buf.getInt(); + int signature = buf.getInt(); + int version = buf.getInt(); + int width = buf.getInt(); + int height = buf.getInt(); + int updateBufBase = buf.getInt(); + int imageBufBase = buf.getInt(); + int temperatureNo = buf.getInt(); + int modeNo = buf.getInt(); + int[] frameCount = new int[8]; + for (int i = 0; i < 8; i += 1) + frameCount[i] = buf.getInt(); + int numImgBuf = buf.getInt(); + return new DisplayInfo(standardCmdNo, extendedCmdNo, signature, version, width, height, + updateBufBase, imageBufBase, temperatureNo, modeNo, frameCount, numImgBuf); + } + + static final int LENGTH = 112; + } + + record Status(int sequence, int dataRemaining, byte status) { + static Status from(byte[] bytes) { + var buf = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); + int sequence = buf.getInt(); + int dataLeft = buf.getInt(); + byte status = buf.get(); + return new Status(sequence, dataLeft, status); + } + } + + record Area(int address, int x, int y, int w, int h) { + byte[] toByteArray() { + var buf = ByteBuffer.allocate(20).order(ByteOrder.BIG_ENDIAN); + buf.putInt(address); + buf.putInt(x); + buf.putInt(y); + buf.putInt(w); + buf.putInt(h); + return buf.array(); + } + } + + record DisplayArea(int address, int mode, int x, int y, int w, int h, int wait_ready) { + byte[] toByteArray() { + var buf = ByteBuffer.allocate(28).order(ByteOrder.BIG_ENDIAN); + buf.putInt(address); + buf.putInt(mode); + buf.putInt(x); + buf.putInt(y); + buf.putInt(w); + buf.putInt(h); + buf.putInt(wait_ready); + return buf.array(); + } + } +} diff --git a/examples/epaper_display/tiger.jpg b/examples/epaper_display/tiger.jpg new file mode 100644 index 00000000..82060f99 Binary files /dev/null and b/examples/epaper_display/tiger.jpg differ diff --git a/examples/monitor/.mvn/wrapper/maven-wrapper.jar b/examples/monitor/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 00000000..cb28b0e3 Binary files /dev/null and b/examples/monitor/.mvn/wrapper/maven-wrapper.jar differ diff --git a/examples/monitor/.mvn/wrapper/maven-wrapper.properties b/examples/monitor/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..f3283b08 --- /dev/null +++ b/examples/monitor/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar diff --git a/examples/monitor/README.md b/examples/monitor/README.md index 33acf3ba..f32c60c9 100644 --- a/examples/monitor/README.md +++ b/examples/monitor/README.md @@ -4,15 +4,15 @@ This sample program monitors USB devices as they are connected and disconnected. ## Prerequisites -- Java 19 +- Java 22 - Apache Maven - 64-bit operating system (Windows, macOS, Linux) ## How to run -### Install Java 19 +### Install Java 22 or higher -Check that *Java 19* is installed: +Check that Java 22 or higher is installed: ```shell $ java -version @@ -30,37 +30,35 @@ $ mvn -version If it is not present, install it, typically using package manager like *Homebrew* on macOS, *Chocolately* on Windows and *apt* on Linux. -### Create the *java-does-usb* library - -Since the *java-does-usb* library is not yet available on Maven Central, it must be built locally: - -```shell -$ cd JavaDoesUSB/java-does-usb -$ mvn install -``` - -The result will be put in your local Maven repository. - ### Run the sample ```shell $ cd JavaDoesUSB/examples/monitor $ mvn compile exec:exec + [INFO] Scanning for projects... [INFO] [INFO] -----------------< net.codecrete.usb.examples:monitor >----------------- -[INFO] Building monitor 0.3-SNAPSHOT +[INFO] Building monitor 1.3.0 [INFO] --------------------------------[ jar ]--------------------------------- [INFO] -[INFO] --- maven-resources-plugin:3.0.2:resources (default-resources) @ monitor --- -[INFO] Using 'UTF-8' encoding to copy filtered resources. -[INFO] skip non existing resourceDirectory /Users/me/Documents/JavaDoesUSB/examples/monitor/src/main/resources +[INFO] --- maven-resources-plugin:3.3.1:resources (default-resources) @ monitor --- +[INFO] Copying 1 resource from src/main/resources to target/classes [INFO] -[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ monitor --- -[INFO] Nothing to compile - all classes are up to date +[INFO] --- maven-compiler-plugin:3.12.1:compile (default-compile) @ monitor --- +[INFO] Nothing to compile - all classes are up to date. [INFO] -[INFO] --- exec-maven-plugin:3.1.0:exec (default-cli) @ monitor --- -Present: VID: 0xcafe, PID: 0xceaf, manufacturer: JavaDoesUSB, product: Loopback, serial: 8D8F515C5456, ID: 4295291950 -Present: VID: 0x1a40, PID: 0x0801, manufacturer: null, product: USB 2.0 Hub, serial: null, ID: 4295291734 +[INFO] --- exec-maven-plugin:3.1.1:exec (default-cli) @ monitor --- +Present: VID: 0x1d6b, PID: 0x0002, manufacturer: Linux 6.5.0-18-generic xhci-hcd, product: xHCI Host Controller, serial: 0000:00:14.0, ID: /dev/bus/usb/001/001 +Present: VID: 0xcafe, PID: 0xceaf, manufacturer: JavaDoesUSB, product: Loopback, serial: 35A737883336, ID: /dev/bus/usb/001/005 Monitoring... Press ENTER to quit. +Disconnected: VID: 0xcafe, PID: 0xceaf, manufacturer: JavaDoesUSB, product: Loopback, serial: 35A737883336, ID: /dev/bus/usb/001/005 +Connected: VID: 0xcafe, PID: 0xceaf, manufacturer: JavaDoesUSB, product: Loopback, serial: 35A737883336, ID: /dev/bus/usb/001/009 + +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD SUCCESS +[INFO] ------------------------------------------------------------------------ +[INFO] Total time: 17.647 s +[INFO] Finished at: 2024-10-13T16:50:59+01:00 +[INFO] ----------------------------------------------------------------------- ``` diff --git a/examples/monitor/mvnw b/examples/monitor/mvnw new file mode 100755 index 00000000..8d937f4c --- /dev/null +++ b/examples/monitor/mvnw @@ -0,0 +1,308 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.2.0 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "$(uname)" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && + JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=$(which readlink) + if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then + if $darwin ; then + javaHome="$(dirname "\"$javaExecutable\"")" + javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" + else + javaExecutable="$(readlink -f "\"$javaExecutable\"")" + fi + javaHome="$(dirname "\"$javaExecutable\"")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=$(cd "$wdir/.." || exit 1; pwd) + fi + # end of workaround + done + printf '%s' "$(cd "$basedir" || exit 1; pwd)" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + # Remove \r in case we run on Windows within Git Bash + # and check out the repository with auto CRLF management + # enabled. Otherwise, we may read lines that are delimited with + # \r\n and produce $'-Xarg\r' rather than -Xarg due to word + # splitting rules. + tr -s '\r\n' ' ' < "$1" + fi +} + +log() { + if [ "$MVNW_VERBOSE" = true ]; then + printf '%s\n' "$1" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname "$0")") +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +log "$MAVEN_PROJECTBASEDIR" + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" +if [ -r "$wrapperJarPath" ]; then + log "Found $wrapperJarPath" +else + log "Couldn't find $wrapperJarPath, downloading it ..." + + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + fi + while IFS="=" read -r key value; do + # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) + safeValue=$(echo "$value" | tr -d '\r') + case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; + esac + done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget > /dev/null; then + log "Found wget ... using wget" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + log "Found curl ... using curl" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + fi + else + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +# If specified, validate the SHA-256 sum of the Maven wrapper jar file +wrapperSha256Sum="" +while IFS="=" read -r key value; do + case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; + esac +done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" +if [ -n "$wrapperSha256Sum" ]; then + wrapperSha256Result=false + if command -v sha256sum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + elif command -v shasum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." + echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." + exit 1 + fi + if [ $wrapperSha256Result = false ]; then + echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 + echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 + echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 + exit 1 + fi +fi + +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --windows "$CLASSPATH") + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +# shellcheck disable=SC2086 # safe args +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/examples/monitor/mvnw.cmd b/examples/monitor/mvnw.cmd new file mode 100644 index 00000000..f80fbad3 --- /dev/null +++ b/examples/monitor/mvnw.cmd @@ -0,0 +1,205 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.2.0 +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file +SET WRAPPER_SHA_256_SUM="" +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B +) +IF NOT %WRAPPER_SHA_256_SUM%=="" ( + powershell -Command "&{"^ + "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ + "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ + " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ + " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ + " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ + " exit 1;"^ + "}"^ + "}" + if ERRORLEVEL 1 goto error +) + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/examples/monitor/pom.xml b/examples/monitor/pom.xml index 6f0f8e1a..52206a11 100644 --- a/examples/monitor/pom.xml +++ b/examples/monitor/pom.xml @@ -6,81 +6,98 @@ net.codecrete.usb.examples monitor - 0.3.0 + 1.3.0 monitor https://github.com/manuelbl/JavaDoesUSB/examples/monitor UTF-8 - 19 - 19 + 22 + 22 + 1.3.0 net.codecrete.usb java-does-usb - 0.3.0 + ${java-does-usb.version} + + + org.tinylog + tinylog-api + 2.6.2 + + + org.tinylog + tinylog-impl + 2.6.2 + + + org.tinylog + jsl-tinylog + 2.6.2 - + maven-clean-plugin - 3.1.0 + 3.3.2 maven-resources-plugin - 3.0.2 + 3.3.1 maven-compiler-plugin - 3.8.0 + 3.12.1 - 19 - --enable-preview - 19 - 19 + 22 + 22 + 22 maven-surefire-plugin - 2.22.1 + 3.2.5 + + --enable-native-access=ALL-UNNAMED + maven-jar-plugin - 3.0.2 + 3.3.0 maven-install-plugin - 2.5.2 + 3.1.1 maven-deploy-plugin - 2.8.2 + 3.1.1 maven-site-plugin - 3.7.1 + 3.12.1 maven-project-info-reports-plugin - 3.0.0 + 3.5.0 org.codehaus.mojo exec-maven-plugin - 3.1.0 + 3.1.1 java - --enable-preview --enable-native-access=ALL-UNNAMED -classpath diff --git a/examples/monitor/src/main/java/net/codecrete/usb/examples/Monitor.java b/examples/monitor/src/main/java/net/codecrete/usb/examples/Monitor.java index a897ba94..482d1b9f 100644 --- a/examples/monitor/src/main/java/net/codecrete/usb/examples/Monitor.java +++ b/examples/monitor/src/main/java/net/codecrete/usb/examples/Monitor.java @@ -7,8 +7,8 @@ package net.codecrete.usb.examples; -import net.codecrete.usb.USB; -import net.codecrete.usb.USBDevice; +import net.codecrete.usb.Usb; +import net.codecrete.usb.UsbDevice; import java.io.IOException; @@ -19,11 +19,11 @@ public class Monitor { public static void main(String[] args) throws IOException { // register callbacks for events - USB.setOnDeviceConnected((device) -> printDetails(device, "Connected")); - USB.setOnDeviceDisconnected((device) -> printDetails(device, "Disconnected")); + Usb.setOnDeviceConnected(device -> printDetails(device, "Connected")); + Usb.setOnDeviceDisconnected(device -> printDetails(device, "Disconnected")); // display the already present USB devices - for (var device : USB.getAllDevices()) + for (var device : Usb.getDevices()) printDetails(device, "Present"); // wait for ENTER to quit program @@ -31,7 +31,7 @@ public static void main(String[] args) throws IOException { System.in.read(); } - private static void printDetails(USBDevice device, String event) { + private static void printDetails(UsbDevice device, String event) { System.out.printf("%-14s", event + ":"); System.out.println(device.toString()); } diff --git a/examples/monitor/src/main/resources/tinylog.properties b/examples/monitor/src/main/resources/tinylog.properties new file mode 100644 index 00000000..08b5f1d0 --- /dev/null +++ b/examples/monitor/src/main/resources/tinylog.properties @@ -0,0 +1,3 @@ +writer = console +writer.format = {date: HH:mm:ss.SSS} {level|size=5} [{class|size=40}] {message} +writer.level = debug diff --git a/examples/monitor_kotlin/.gitignore b/examples/monitor_kotlin/.gitignore new file mode 100644 index 00000000..5ff6309b --- /dev/null +++ b/examples/monitor_kotlin/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/examples/monitor_kotlin/.mvn/wrapper/maven-wrapper.properties b/examples/monitor_kotlin/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..c595b009 --- /dev/null +++ b/examples/monitor_kotlin/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,3 @@ +wrapperVersion=3.3.4 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.14/apache-maven-3.9.14-bin.zip diff --git a/examples/monitor_kotlin/README.md b/examples/monitor_kotlin/README.md new file mode 100644 index 00000000..f98849da --- /dev/null +++ b/examples/monitor_kotlin/README.md @@ -0,0 +1,49 @@ +# USB Device Monitoring (Kotlin) + +This sample enumerates the connected USB devices and provides information about the interfaces and endpoints. + +## Prerequisites + +- Java 25 +- Apache Maven +- 64-bit operating system (Windows, macOS, Linux) + +## How to run + +### Install Java 25 or higher + +Check that Java 25 or higher is installed: + +```shell +$ java -version +``` + +If not, download and install it, e.g. from [Azul](https://www.azul.com/downloads/?package=jdk). + +### Install Maven + +Check that *Maven* is installed: + +```shell +$ mvn -version +``` + +If it is not present, install it, typically using package manager like *Homebrew* on macOS, *Chocolately* on Windows and *apt* on Linux. + +### Build a self-contained jar file + +```shell +$ cd JavaDoesUSB/examples/monitor_kotlin +$ mvn clean package +``` + +### Run the jar + +```shell +$ java --enable-native-access=ALL-UNNAMED -jar target/monitor-1.3.0-jar-with-dependencies.jar +Present: VID: 0x1d6b, PID: 0x0002, manufacturer: Linux 6.5.0-18-generic xhci-hcd, product: xHCI Host Controller, serial: 0000:00:14.0, ID: /dev/bus/usb/001/001 +Present: VID: 0xcafe, PID: 0xceaf, manufacturer: JavaDoesUSB, product: Loopback, serial: 35A737883336, ID: /dev/bus/usb/001/009 +Monitoring... Press ENTER to quit. +Disconnected: VID: 0xcafe, PID: 0xceaf, manufacturer: JavaDoesUSB, product: Loopback, serial: 35A737883336, ID: /dev/bus/usb/001/009 +Connected: VID: 0xcafe, PID: 0xceaf, manufacturer: JavaDoesUSB, product: Loopback, serial: 35A737883336, ID: /dev/bus/usb/001/010 +``` diff --git a/examples/monitor_kotlin/mvnw b/examples/monitor_kotlin/mvnw new file mode 100755 index 00000000..bd8896bf --- /dev/null +++ b/examples/monitor_kotlin/mvnw @@ -0,0 +1,295 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.4 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +scriptDir="$(dirname "$0")" +scriptName="$(basename "$0")" + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +actualDistributionDir="" + +# First try the expected directory name (for regular distributions) +if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then + if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then + actualDistributionDir="$distributionUrlNameMain" + fi +fi + +# If not found, search for any directory with the Maven executable (for snapshots) +if [ -z "$actualDistributionDir" ]; then + # enable globbing to iterate over items + set +f + for dir in "$TMP_DOWNLOAD_DIR"/*; do + if [ -d "$dir" ]; then + if [ -f "$dir/bin/$MVN_CMD" ]; then + actualDistributionDir="$(basename "$dir")" + break + fi + fi + done + set -f +fi + +if [ -z "$actualDistributionDir" ]; then + verbose "Contents of $TMP_DOWNLOAD_DIR:" + verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" + die "Could not find Maven distribution directory in extracted archive" +fi + +verbose "Found extracted Maven distribution directory: $actualDistributionDir" +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/examples/monitor_kotlin/mvnw.cmd b/examples/monitor_kotlin/mvnw.cmd new file mode 100644 index 00000000..5761d948 --- /dev/null +++ b/examples/monitor_kotlin/mvnw.cmd @@ -0,0 +1,189 @@ +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.4 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' + +$MAVEN_M2_PATH = "$HOME/.m2" +if ($env:MAVEN_USER_HOME) { + $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" +} + +if (-not (Test-Path -Path $MAVEN_M2_PATH)) { + New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null +} + +$MAVEN_WRAPPER_DISTS = $null +if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { + $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" +} else { + $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" +} + +$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +$actualDistributionDir = "" + +# First try the expected directory name (for regular distributions) +$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" +$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" +if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { + $actualDistributionDir = $distributionUrlNameMain +} + +# If not found, search for any directory with the Maven executable (for snapshots) +if (!$actualDistributionDir) { + Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { + $testPath = Join-Path $_.FullName "bin/$MVN_CMD" + if (Test-Path -Path $testPath -PathType Leaf) { + $actualDistributionDir = $_.Name + } + } +} + +if (!$actualDistributionDir) { + Write-Error "Could not find Maven distribution directory in extracted archive" +} + +Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/examples/monitor_kotlin/pom.xml b/examples/monitor_kotlin/pom.xml new file mode 100644 index 00000000..2612f221 --- /dev/null +++ b/examples/monitor_kotlin/pom.xml @@ -0,0 +1,92 @@ + + 4.0.0 + + net.codecrete.usb.examples + monitor + 1.3.0 + jar + + monitor + https://github.com/manuelbl/JavaDoesUSB/examples/monitor_kotlin + + + 2.3.21 + true + UTF-8 + net.codecrete.usb.examples.MonitorKt + 1.3.0 + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + true + + + org.apache.maven.plugins + maven-assembly-plugin + 3.6.0 + + + make-assembly + package + + single + + + + + ${main.class} + + + + jar-with-dependencies + + + + + + + + + + + org.jetbrains.kotlin + kotlin-stdlib + ${kotlin.version} + + + net.codecrete.usb + java-does-usb + ${java-does-usb.version} + + + org.tinylog + tinylog-api + 2.7.0 + + + org.tinylog + tinylog-impl + 2.7.0 + + + org.tinylog + jsl-tinylog + 2.7.0 + + + junit + junit + 4.13.2 + test + + + diff --git a/examples/monitor_kotlin/src/main/kotlin/net/codecrete/usb/examples/Monitor.kt b/examples/monitor_kotlin/src/main/kotlin/net/codecrete/usb/examples/Monitor.kt new file mode 100644 index 00000000..b16d568a --- /dev/null +++ b/examples/monitor_kotlin/src/main/kotlin/net/codecrete/usb/examples/Monitor.kt @@ -0,0 +1,41 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +package net.codecrete.usb.examples + +import net.codecrete.usb.Usb +import net.codecrete.usb.UsbDevice + +/** + * Sample application monitoring USB devices as they are connected and disconnected. + */ + +fun main() { + Monitor().monitor() +} + +class Monitor { + fun monitor() { + // register callbacks for events + Usb.setOnDeviceConnected { device -> printDetails(device, "Connected") } + Usb.setOnDeviceDisconnected { device -> + printDetails( device, "Disconnected") + } + + // display the already present USB devices + for (device in Usb.getDevices()) + printDetails(device, "Present") + + // wait for ENTER to quit program + println("Monitoring... Press ENTER to quit.") + System.`in`.read() + } + + private fun printDetails(device: UsbDevice, event: String) { + print(String.format("%-14s", "$event:")) + println(device.toString()) + } +} \ No newline at end of file diff --git a/examples/monitor_native/README.md b/examples/monitor_native/README.md new file mode 100644 index 00000000..3018321d --- /dev/null +++ b/examples/monitor_native/README.md @@ -0,0 +1,49 @@ +# Device Monitoring (Native) + +This example project demonstrates how to build a native application that +monitors USB devices using [GraalVM](https://www.graalvm.org/) and +this _Java Does USB_ library. + +## Build and Run + +### Prerequisites + +- [GraalVM](https://www.graalvm.org/) 25 or higher +- [Maven](https://maven.apache.org/) 3.9 or higher + + +### Preparation + +GraalVM needs help to learn about the Java FFM downcall and upcall descriptors, +and it needs some help to include all required methods. The relevant items +differ from operating system to operating system. When building the native image, +the operating system must be selected by changing the path in the file +`native-image.properties` in the directory +`src/main/resources/META-INF/native-image/net.codecrete.usb.examples/monitor_native`. + +```properties +Args = --enable-native-access=ALL-UNNAMED -H:ConfigurationFileDirectories=config/macos +``` + +Note the last word of the line. In this case, it is `macos`. Change this to +`linux` or `windows` if needed. + +In your own Maven project, you might also need to move the file or rather rename +directory. It must be named according to the pattern +`src/main/resources/META-INF/native-image//`. + +Also check that the environment variable `JAVA_HOME` points to the GraalVM directory. + + +### Building + +```shell +mvn -Pnative package +``` + + +### Running + +```shell +./target/monitor-native +``` diff --git a/examples/monitor_native/config/linux/reachability-metadata.json b/examples/monitor_native/config/linux/reachability-metadata.json new file mode 100644 index 00000000..e1746c04 --- /dev/null +++ b/examples/monitor_native/config/linux/reachability-metadata.json @@ -0,0 +1,111 @@ +{ + "foreign": { + "downcalls": [ + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint", + "jlong", + "void*" + ], + "options": { + "captureCallState": true, + "firstVariadicArg": 2 + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint", + "void*", + "jint", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "void*", + "parameterTypes": [] + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "jint" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint", + "jint", + "jint", + "void*" + ], + "options": { + "captureCallState": true + } + } + ] + } +} \ No newline at end of file diff --git a/examples/monitor_native/config/macos/reachability-metadata.json b/examples/monitor_native/config/macos/reachability-metadata.json new file mode 100644 index 00000000..113fb587 --- /dev/null +++ b/examples/monitor_native/config/macos/reachability-metadata.json @@ -0,0 +1,318 @@ +{ + "foreign": { + "upcalls": [ + { + "returnType": "void", + "parameterTypes": [ + "void*", + "jint", + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "jint", + "void*", + "void*" + ] + }, + { + "returnType": "void", + "parameterTypes": [ + "void*", + "jint" + ] + } + ], + "downcalls": [ + { + "returnType": "void", + "parameterTypes": [ + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "void", + "parameterTypes": [ + "void*", + "struct(jlong,jlong)", + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "void*", + "jlong" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jint", + "void*", + "jdouble", + "jdouble", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*", + "void*", + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint" + ] + }, + { + "returnType": "jlong", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint", + "void*", + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "jint", + "void*", + "void*", + "jint" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "jint", + "void*", + "void*" + ] + }, + { + "returnType": "jbyte", + "parameterTypes": [ + "void*", + "jlong", + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "struct(jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte)" + ] + }, + { + "returnType": "void", + "parameterTypes": [] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte", + "void*", + "jint", + "jint", + "jint", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte", + "void*", + "void*", + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "jlong", + "parameterTypes": [] + }, + { + "returnType": "void", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint", + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "jint" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "void*", + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte", + "void*", + "jint", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "struct(jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte)", + "void*" + ] + }, + { + "returnType": "struct(jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte)", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "void", + "parameterTypes": [ + "void*", + "jint" + ] + } + ] + }, + "reflection": [ + { + "type": "net.codecrete.usb.macos.gen.corefoundation.CFMessagePortCreateLocal$callout$Function", + "methods": [ + { + "name": "apply", + "parameterTypes": [ + "java.lang.foreign.MemorySegment", + "int", + "java.lang.foreign.MemorySegment", + "java.lang.foreign.MemorySegment" + ] + } + ] + }, + { + "type": "net.codecrete.usb.macos.gen.iokit.IOServiceAddMatchingNotification$callback$Function", + "methods": [ + { + "name": "apply", + "parameterTypes": [ + "java.lang.foreign.MemorySegment", + "int" + ] + } + ] + } + ] +} diff --git a/examples/monitor_native/config/windows/reachability-metadata.json b/examples/monitor_native/config/windows/reachability-metadata.json new file mode 100644 index 00000000..f3abe566 --- /dev/null +++ b/examples/monitor_native/config/windows/reachability-metadata.json @@ -0,0 +1,377 @@ +{ + "foreign": { + "downcalls": [ + { + "returnType": "jint", + "parameterTypes": [ + "jint", + "void*", + "jint", + "jint", + "void*", + "jint", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jlong" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jlong", + "jint", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jlong", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jlong", + "void*", + "jint", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jlong", + "void*", + "void*", + "jint", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jlong", + "void*", + "void*", + "jint", + "void*", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jlong", + "void*", + "void*", + "void*", + "void*", + "jint", + "void*", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte", + "jint", + "jint", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte", + "void*", + "jint", + "void*", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jint", + "void*", + "jint", + "void*", + "jint", + "void*", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*", + "jint", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*", + "void*", + "void*", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*", + "void*", + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "jlong", + "parameterTypes": [ + "void*", + "jint", + "jlong", + "jlong" + ] + }, + { + "returnType": "jlong", + "parameterTypes": [ + "void*", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jlong", + "parameterTypes": [ + "void*", + "void*", + "void*", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jshort", + "parameterTypes": [ + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "void*", + "parameterTypes": [ + "jint", + "void*", + "void*", + "jint", + "jint", + "jint", + "jint", + "jint", + "void*", + "void*", + "void*", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "void*", + "parameterTypes": [ + "jlong", + "void*", + "jint", + "jint", + "jint", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "jint", + "jint", + "void*", + "jint", + "jint", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "void*", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "void*", + "jlong", + "jint" + ], + "options": { + "captureCallState": true + } + } + ], + "upcalls": [ + { + "returnType": "jlong", + "parameterTypes": [ + "void*", + "jint", + "jlong", + "jlong" + ] + } + ] + }, + "reflection": [ + { + "type": "windows.win32.ui.windowsandmessaging.WNDPROC$Function", + "methods": [ + { + "name": "invoke", + "parameterTypes": [ + "java.lang.foreign.MemorySegment", + "int", + "long", + "long" + ] + } + ] + } + ] +} diff --git a/examples/monitor_native/pom.xml b/examples/monitor_native/pom.xml new file mode 100644 index 00000000..68e930de --- /dev/null +++ b/examples/monitor_native/pom.xml @@ -0,0 +1,85 @@ + + 4.0.0 + + net.codecrete.usb.examples + monitor_native + jar + 1.0-SNAPSHOT + monitor_native + https://www.github.com/manuelbl/java-does-usb + + + 25 + 25 + UTF-8 + 0.11.0 + 1.3.0 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.14.0 + + true + + + + org.apache.maven.plugins + maven-jar-plugin + 3.4.2 + + + + net.codecrete.usb.examples.App + true + + + + + + + + + + net.codecrete.usb + java-does-usb + ${java-does-usb.version} + + + junit + junit + 3.8.1 + test + + + + + + native + + + + org.graalvm.buildtools + native-maven-plugin + ${native.maven.plugin.version} + true + + + build-native + + compile-no-fork + + package + + + + + + + + + diff --git a/examples/monitor_native/src/main/java/net/codecrete/usb/examples/App.java b/examples/monitor_native/src/main/java/net/codecrete/usb/examples/App.java new file mode 100644 index 00000000..3bb1ccb9 --- /dev/null +++ b/examples/monitor_native/src/main/java/net/codecrete/usb/examples/App.java @@ -0,0 +1,38 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.examples; + +import net.codecrete.usb.Usb; +import net.codecrete.usb.UsbDevice; + +import java.io.IOException; + +/** + * Sample application monitoring USB devices as they are connected and disconnected. + */ +public class App { + + static void main() throws IOException { + // register callbacks for events + Usb.setOnDeviceConnected(device -> printDetails(device, "Connected")); + Usb.setOnDeviceDisconnected(device -> printDetails(device, "Disconnected")); + + // display the already present USB devices + for (var device : Usb.getDevices()) + printDetails(device, "Present"); + + // wait for ENTER to quit program + System.out.println("Monitoring... Press ENTER to quit."); + System.in.read(); + } + + private static void printDetails(UsbDevice device, String event) { + System.out.printf("%-14s", event + ":"); + System.out.println(device.toString()); + } +} \ No newline at end of file diff --git a/examples/monitor_native/src/main/resources/META-INF/native-image/net.codecrete.usb.examples/monitor_native/native-image.properties b/examples/monitor_native/src/main/resources/META-INF/native-image/net.codecrete.usb.examples/monitor_native/native-image.properties new file mode 100644 index 00000000..cf6e3f9a --- /dev/null +++ b/examples/monitor_native/src/main/resources/META-INF/native-image/net.codecrete.usb.examples/monitor_native/native-image.properties @@ -0,0 +1 @@ +Args = --enable-native-access=ALL-UNNAMED -H:ConfigurationFileDirectories=config/windows diff --git a/examples/stm_dfu/.mvn/wrapper/maven-wrapper.jar b/examples/stm_dfu/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 00000000..cb28b0e3 Binary files /dev/null and b/examples/stm_dfu/.mvn/wrapper/maven-wrapper.jar differ diff --git a/examples/stm_dfu/.mvn/wrapper/maven-wrapper.properties b/examples/stm_dfu/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..f3283b08 --- /dev/null +++ b/examples/stm_dfu/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar diff --git a/examples/stm_dfu/README.md b/examples/stm_dfu/README.md new file mode 100644 index 00000000..8c5285d0 --- /dev/null +++ b/examples/stm_dfu/README.md @@ -0,0 +1,73 @@ +# Device Firmware Upload (DFU) for STM32 + +This sample programs implements firmware upload for STM32 microcontrollers with the built-in DFU mode. + +Even though the DFU + +## Prerequisites + +- Java 22 +- Apache Maven +- 64-bit operating system (Windows, macOS, Linux) + +## How to run + +### Install Java 22 or higher + +Check that Java 22 or higher is installed: + +```shell +$ java -version +``` + +If not, download and install it, e.g. from [Azul](https://www.azul.com/downloads/?package=jdk). + +### Install Maven + +Check that *Maven* is installed: + +```shell +$ mvn -version +``` + +If it is not present, install it, typically using package manager like *Homebrew* on macOS, *Chocolately* on Windows and *apt* on Linux. + +### Build the application + +```shell +$ mvn clean package +``` + +### Put the STM32 development board in DFU mode + +- Connect an STM32 development board (like a BlackPill board) to your computer while pressing the *Boot* button. +- Ensure that it is DFU mode by checking macOS *System Information* or Windows *Device Manager*. The device should appear as "STM32 BOOTLOADER". + +On Windows, the *WinUSB* driver must be installed. See https://github.com/manuelbl/JavaDoesUSB/wiki/DFU-on-Windows for additional information. + +On many Linux distributions, the default permissions of USB devices do not allow access. To change it, create a file `/etc/udev/rules.d/50-stm-dfu.rules` with the below content: + +```text +SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", MODE="0666" +``` + + +### Run the application + +Run the command below (adapting the file path depending on your specific board): + +```shell +$ mvn package +$ java --enable-native-access=ALL-UNNAMED -jar target/stm_dfu-1.3.0.jar ../../test-devices/loopback-stm32/bin/blackpill-f401cc.bin +DFU device found with serial 35A737883336. +Target memory segment: Internal Flash +Erasing page at 0x8000000 (size 0x4000) +Writing data at 0x8000000 (size 0x800) +Writing data at 0x8000800 (size 0x800) +Writing data at 0x8001000 (size 0x800) +Writing data at 0x8001800 (size 0x800) +Writing data at 0x8002000 (size 0x800) +Writing data at 0x8002800 (size 0x1f4) +Firmware successfully downloaded and verified +DFU mode exited and firmware started +``` diff --git a/examples/stm_dfu/mvnw b/examples/stm_dfu/mvnw new file mode 100755 index 00000000..8d937f4c --- /dev/null +++ b/examples/stm_dfu/mvnw @@ -0,0 +1,308 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.2.0 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "$(uname)" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && + JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=$(which readlink) + if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then + if $darwin ; then + javaHome="$(dirname "\"$javaExecutable\"")" + javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" + else + javaExecutable="$(readlink -f "\"$javaExecutable\"")" + fi + javaHome="$(dirname "\"$javaExecutable\"")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=$(cd "$wdir/.." || exit 1; pwd) + fi + # end of workaround + done + printf '%s' "$(cd "$basedir" || exit 1; pwd)" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + # Remove \r in case we run on Windows within Git Bash + # and check out the repository with auto CRLF management + # enabled. Otherwise, we may read lines that are delimited with + # \r\n and produce $'-Xarg\r' rather than -Xarg due to word + # splitting rules. + tr -s '\r\n' ' ' < "$1" + fi +} + +log() { + if [ "$MVNW_VERBOSE" = true ]; then + printf '%s\n' "$1" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname "$0")") +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +log "$MAVEN_PROJECTBASEDIR" + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" +if [ -r "$wrapperJarPath" ]; then + log "Found $wrapperJarPath" +else + log "Couldn't find $wrapperJarPath, downloading it ..." + + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + fi + while IFS="=" read -r key value; do + # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) + safeValue=$(echo "$value" | tr -d '\r') + case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; + esac + done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget > /dev/null; then + log "Found wget ... using wget" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + log "Found curl ... using curl" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + fi + else + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +# If specified, validate the SHA-256 sum of the Maven wrapper jar file +wrapperSha256Sum="" +while IFS="=" read -r key value; do + case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; + esac +done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" +if [ -n "$wrapperSha256Sum" ]; then + wrapperSha256Result=false + if command -v sha256sum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + elif command -v shasum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." + echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." + exit 1 + fi + if [ $wrapperSha256Result = false ]; then + echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 + echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 + echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 + exit 1 + fi +fi + +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --windows "$CLASSPATH") + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +# shellcheck disable=SC2086 # safe args +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/examples/stm_dfu/mvnw.cmd b/examples/stm_dfu/mvnw.cmd new file mode 100644 index 00000000..f80fbad3 --- /dev/null +++ b/examples/stm_dfu/mvnw.cmd @@ -0,0 +1,205 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.2.0 +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file +SET WRAPPER_SHA_256_SUM="" +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B +) +IF NOT %WRAPPER_SHA_256_SUM%=="" ( + powershell -Command "&{"^ + "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ + "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ + " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ + " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ + " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ + " exit 1;"^ + "}"^ + "}" + if ERRORLEVEL 1 goto error +) + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/examples/stm_dfu/pom.xml b/examples/stm_dfu/pom.xml new file mode 100644 index 00000000..0e3aa46d --- /dev/null +++ b/examples/stm_dfu/pom.xml @@ -0,0 +1,90 @@ + + + 4.0.0 + + net.codecrete.usb.examples + stm_dfu + 1.3.0 + + stm_dfu + https://github.com/manuelbl/JavaDoesUSB/examples/stm_dfu + + + UTF-8 + 22 + 22 + 1.3.0 + + + + + net.codecrete.usb + java-does-usb + ${java-does-usb.version} + + + + + + + + maven-clean-plugin + 3.3.2 + + + + maven-resources-plugin + 3.3.1 + + + maven-compiler-plugin + 3.12.1 + + 22 + 22 + 22 + + + + maven-jar-plugin + 3.3.0 + + + maven-surefire-plugin + 3.2.5 + + --enable-native-access=ALL-UNNAMED + + + + maven-shade-plugin + 3.5.1 + + + package + + shade + + + + + + + *:* + + module-info.class + META-INF/MANIFEST.MF + + + + + + net.codecrete.usb.dfu.DFU + + + false + + + + + diff --git a/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DFU.java b/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DFU.java new file mode 100644 index 00000000..4ca4d2a3 --- /dev/null +++ b/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DFU.java @@ -0,0 +1,77 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.dfu; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * Command line application for uploading firmware to STM32 microcontrollers. + *

+ * Only the STM32 variant of the DFU protocol is supported. + * Only binary firmware format is supported (no .hex or .dfu files). + *

+ */ +public class DFU { + + /** + * Main function + * @param args arguments (path to firmware) + */ + public static void main(String[] args) { + // check for single parameter + if (args.length != 1) { + System.err.println("Usage: dfu_upload "); + System.exit(1); + return; + } + + // read firmware file + byte[] firmware; + try { + firmware = Files.readAllBytes(Path.of(args[0])); + } catch (IOException e) { + System.err.printf("Error: Cannot read firmware file %s%n", args[0]); + System.exit(2); + return; + } + + // check for single DFU device + var devices = DFUDevice.getAll(); + if (devices.isEmpty()) { + System.err.println("Error: No STM32 DFU device connected (or not in DFU mode)"); + System.exit(4); + return; + } else if (devices.size() > 1) { + System.err.println("Error: Multiple STM32 DFU devices connected. Please connect only one."); + System.exit(4); + return; + } + var device = devices.getFirst(); + System.out.printf("DFU device found with serial %s.%n", device.getSerialNumber()); + + // download and verify firmware + try { + device.open(); + device.download(firmware); + device.verify(firmware); + System.out.println("Firmware successfully downloaded and verified"); + + device.startApplication(); + device.waitForDisconnect(); + System.out.println("DFU mode ended and firmware started"); + + device.close(); + + } catch (DFUException e) { + System.err.println("Error: " + e.getMessage()); + System.exit(3); + } + } +} diff --git a/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DFUDevice.java b/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DFUDevice.java new file mode 100644 index 00000000..4678e066 --- /dev/null +++ b/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DFUDevice.java @@ -0,0 +1,396 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.dfu; + +import net.codecrete.usb.*; + +import java.util.Arrays; +import java.util.List; + +import static net.codecrete.usb.UsbRecipient.INTERFACE; +import static net.codecrete.usb.UsbRequestType.CLASS; + +/** + * DFU device. + *

+ * Implements the main DFU operations like download, upload etc. + *

+ */ +public class DFUDevice { + + private final UsbDevice usbDevice; + private final int interfaceNumber; + private final int transferSize; + private final Version dfuVersion; + + private List segments; + + /** + * Gets all connected DFU devices + * @return List of DFU devices + */ + public static List getAll() { + return Usb.findDevices(DFUDevice::hasDFUDescriptor) + .stream() + .map(DFUDevice::new) + .toList(); + } + + /** + * Checks if the device has a DFU functional descriptor. + * @param device USB device + * @return {@code true} if it has a DFU descriptor, {@code false} otherwise + */ + public static boolean hasDFUDescriptor(UsbDevice device) { + return getDFUDescriptorOffset(device.getConfigurationDescriptor()) > 0 + && getDFUInterfaceNumber(device) >= 0; + } + + /** + * Gets the offset of the DFU functional descriptor within the USB configuration descriptor. + * @param descriptor USB configuration descriptor + * @return descriptor offset (in bytes), or -1 if not found + */ + public static int getDFUDescriptorOffset(byte[] descriptor) { + int offset = 0; + while (offset < descriptor.length) { + if (descriptor[offset + 1] == 0x21) + return offset; + offset += descriptor[offset] & 255; + } + return -1; + } + + /** + * Gets the DFU interface number. + * @param device the USB device + * @return the interface number, of -1 if not found + */ + public static int getDFUInterfaceNumber(UsbDevice device) { + for (var intf : device.getInterfaces()) { + var alt = intf.getCurrentAlternate(); + if (alt.getClassCode() == 0xFE && alt.getSubclassCode() == 0x01 && alt.getProtocolCode() == 0x02) + return intf.getNumber(); + } + + return -1; + } + + /** + * Creates a new DFUDevice instance. + *

+ * The specified USB device must have a DFU descriptor and a DFU interface. + *

+ * @param usbDevice the USB device + */ + public DFUDevice(UsbDevice usbDevice) { + this.usbDevice = usbDevice; + interfaceNumber = getDFUInterfaceNumber(usbDevice); + + var configDesc = usbDevice.getConfigurationDescriptor(); + int offset = getDFUDescriptorOffset(configDesc); + assert offset > 0; + + transferSize = getInt16(configDesc, offset + 5); + dfuVersion = new Version(getInt16(configDesc, offset + 7)); + } + + /** + * Gets the DFU protocol version. + * @return the protocol version + */ + public Version getDfuVersion() { + return dfuVersion; + } + + /** + * Gets the device serial number. + * @return the serial number + */ + public String getSerialNumber() { + return usbDevice.getSerialNumber(); + } + + /** + * Opens the DFU device for communication. + */ + public void open() { + usbDevice.open(); + usbDevice.claimInterface(interfaceNumber); + segments = Segment.getSegments(usbDevice, interfaceNumber); + clearErrorIfNeeded(); + } + + /** + * Closes the DFU device. + */ + public void close() { + usbDevice.close(); + } + + /** + * Waits until the device disconnects. + *

+ * Disconnection is a side effect of leaving DFU mode. + *

+ *

+ * If the device does not disconnect after 5 seconds, + * an exception will be thrown. + *

+ */ + public void waitForDisconnect() { + var waitingTime = 5000; + while (waitingTime > 0 && usbDevice.isConnected()) { + sleep(100); + waitingTime -= 100; + } + + if (usbDevice.isConnected()) + throw new DFUException("Device did not restart (try disconnecting and reconnecting it)"); + } + + /** + * Clears an error status. + */ + public void clearStatus() { + var transfer = createDfuControlTransfer(DFURequest.CLEAR_STATUS, 0); + usbDevice.controlTransferOut(transfer, null); + } + + /** + * Aborts the download mode. + */ + public void abort() { + var transfer = createDfuControlTransfer(DFURequest.ABORT, 0); + usbDevice.controlTransferOut(transfer, null); + } + + /** + * Gets the full DFU status + * @return the status + */ + public DFUStatus getStatus() { + var transfer = createDfuControlTransfer(DFURequest.GET_STATUS, 0); + var response = usbDevice.controlTransferIn(transfer, 6); + if (response.length != 6) + throw new DFUException("Invalid response from GET_STATUS request"); + return DFUStatus.fromBytes(response); + } + + /** + * Reads from the device flash memory. + * @param address the memory start address + * @param length the length of memory to read + * @return the read data + */ + public byte[] read(int address, int length) { + expectState(DeviceState.DFU_IDLE, DeviceState.DFU_DNLOAD_IDLE); + setAddress(address); + exitMode(); + expectState(DeviceState.DFU_IDLE, DeviceState.DFU_UPLOAD_IDLE); + + var result = new byte[length]; + + // read full chunks + int offset = 0; + int blockNum = 2; + while (offset < length) { + int chunkSize = Math.min(transferSize, length - offset); + var transfer = createDfuControlTransfer(DFURequest.UPLOAD, blockNum); + var chunk = usbDevice.controlTransferIn(transfer, chunkSize); + System.arraycopy(chunk, 0, result, offset, chunkSize); + offset += chunkSize; + blockNum += 1; + } + + exitMode(); + + return result; + } + + public void verify(byte[] firmware) { + byte[] firmware2 = read(STM32.FLASH_BASE_ADDRESS, firmware.length); + if (!Arrays.equals(firmware, firmware2)) + throw new DFUException("Verification failed - content differs"); + } + + public void download(byte[] firmware) { + int length = firmware.length; + + // validate that start and end address exist and are writable + int startAddress = STM32.FLASH_BASE_ADDRESS; + var firstPage = getWritablePage(startAddress); + getWritablePage(startAddress + length); + + usbDevice.selectAlternateSetting(interfaceNumber, firstPage.segment().getAltSetting()); + System.out.printf("Target memory segment: %s%n", firstPage.segment().getName()); + + // erase if needed + if (firstPage.isErasable()) + erase(startAddress, length); + + // download firmware + setAddress(startAddress); + + int offset = 0; + int transaction = 2; + while (offset < length) { + int chunkSize = Math.min(length - offset, transferSize); + + byte[] chunk = new byte[chunkSize]; + System.arraycopy(firmware, offset, chunk, 0, chunkSize); + + System.out.printf("Writing data at 0x%x (size 0x%x)%n", startAddress + offset, chunkSize); + var transfer = createDfuControlTransfer(DFURequest.DOWNLOAD, transaction); + usbDevice.controlTransferOut(transfer, chunk); + + finishDownloadCommand("writing data"); + + offset += chunkSize; + transaction += 1; + } + + exitMode(); + } + + /** + * Erases the specified range. + *

+ * Only applicable to erasable sector, i.e. flash memory. + *

+ *

+ * Only entire pages can be erased. If start and end address to not fall onto + * page boundaries, this method will extend the range to be erased. + *

+ * @param startAddress the start address of the range + * @param length the length of the range + */ + public void erase(int startAddress, int length) { + int endAddress = startAddress + length; + + while (startAddress < endAddress) { + var page = findPage(startAddress); + if (page == null) + throw new DFUException(String.format("No valid memory segment at address 0x%x", startAddress)); + if (!page.isErasable()) + throw new DFUException(String.format("Page at address 0x%x is not erasable", startAddress)); + + System.out.printf("Erasing page at 0x%x (size 0x%x)%n", page.startAddress(), page.pageSize()); + erasePage(page.startAddress()); + startAddress = page.getEndAddress(); + } + } + + public void erasePage(int address) { + executeSpecialCommand((byte) 0x41, "erasing page", address); + } + + public void setAddress(int address) { + executeSpecialCommand((byte) 0x21, "setting address", address); + } + + private void executeSpecialCommand(byte command, String action, int address) { + var transfer = createDfuControlTransfer(DFURequest.DOWNLOAD, 0); + var data = new byte[] { + command, + (byte) address, + (byte) (address >> 8), + (byte) (address >> 16), + (byte) (address >> 24) + }; + usbDevice.controlTransferOut(transfer, data); + + finishDownloadCommand(action); + } + + private void finishDownloadCommand(String action) { + var status = getStatus(); + if (status.state() != DeviceState.DFU_DNBUSY) + throw new DFUException("Unexpected state for " + action); + + sleep(status.pollTimeout()); + + status = getStatus(); + if (status.status() != DeviceStatus.OK) + throw new DFUException("Unexpected state after " + action); + + sleep(status.pollTimeout()); + } + + private Page getWritablePage(int address) { + var page = findPage(address); + if (page == null) + throw new DFUException(String.format("No valid memory segment at address 0x%x", address)); + if (!page.isWritable()) + throw new DFUException(String.format("Page at address 0x%x is not writable", address)); + return page; + } + + private Page findPage(int address) { + return Segment.findPage(segments, address); + } + + private void exitMode() { + abort(); + + var status = getStatus(); + if (status.state() != DeviceState.DFU_IDLE) + throw new DFUException("Unexpected state after exiting from download mode"); + + sleep(status.pollTimeout()); + } + + public void startApplication() { + expectState(DeviceState.DFU_IDLE, DeviceState.DFU_DNLOAD_IDLE); + + // By sending a zero-length download packet and querying the status, + // the device will leave DFU mode and restart. + var transfer = createDfuControlTransfer(DFURequest.DOWNLOAD, 0); + usbDevice.controlTransferOut(transfer, null); + + var status = getStatus(); + if (status.state() != DeviceState.DFU_MANIFEST) + throw new DFUException("Exiting DFU mode and starting firmware has failed"); + } + + private void clearErrorIfNeeded() { + var status = getStatus(); + if (status.status() != DeviceStatus.OK) { + clearStatus(); + sleep(status.pollTimeout()); + status = getStatus(); + if (status.status() != DeviceStatus.OK) + throw new DFUException("Cannot clear error status"); + } + } + + private void expectState(DeviceState state1, DeviceState state2) { + var status = getStatus(); + if (status.state() != state1 && status.state() != state2) + throw new DFUException( + String.format("Expected state %s or %s but got %s", state1, state2, status.state())); + } + + private int getInt16(byte[] config, int offset) { + return (config[offset] & 0xff) + 256 * (config[offset + 1] & 0xff); + } + + private static void sleep(int millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new DFUException("Sleep failed", e); + } + } + + private UsbControlTransfer createDfuControlTransfer(DFURequest request, int value) { + return new UsbControlTransfer(CLASS, INTERFACE, request.ordinal(), value, interfaceNumber); + } + +} diff --git a/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DFUException.java b/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DFUException.java new file mode 100644 index 00000000..eaa4a111 --- /dev/null +++ b/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DFUException.java @@ -0,0 +1,31 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.dfu; + +/** + * DFU exception thrown if a DFU operation fails. + */ +public class DFUException extends RuntimeException { + + /** + * Creates a new instance with the given message + * @param message the message + */ + DFUException(String message) { + super(message); + } + + /** + * Creates a new instance with the given message and cause + * @param message the message + * @param cause the exception causing the DFU operation to fail + */ + DFUException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DFURequest.java b/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DFURequest.java new file mode 100644 index 00000000..c927e105 --- /dev/null +++ b/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DFURequest.java @@ -0,0 +1,56 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.dfu; + +/** + * DFU request. + *

+ * See ST Microelectronics, application note AN3156. + *

+ */ +public enum DFURequest { + /** + * Requests the device to leave DFU mode and enter the application. + *

+ * The Detach request is not meaningful in the case of the bootloader. The bootloader starts + * with a system reset depending on the boot mode configuration settings, which means that + * no other application is running at that time. + *

+ */ + DETACH, + /** + * Requests data transfer from Host to the device in order to load them + * into device internal flash memory. Includes also erase commands. + */ + DOWNLOAD, + /** + * Requests data transfer from device to Host in order to load content + * of device internal flash memory into a Host file. + */ + UPLOAD, + /** + * Requests device to send status report to the Host (including status + * resulting from the last request execution and the state the device + * enters immediately after this request). + */ + GET_STATUS, + /** + * Requests device to clear error status and move to next step. + */ + CLEAR_STATUS, + /** + * Requests the device to send only the state it enters immediately + * after this request. + */ + GET_STATE, + /** + * Requests device to exit the current state/operation and enter idle + * state immediately + */ + ABORT +} diff --git a/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DFUStatus.java b/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DFUStatus.java new file mode 100644 index 00000000..d6b44b00 --- /dev/null +++ b/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DFUStatus.java @@ -0,0 +1,24 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.dfu; + +/** + * DFU GET_STATUS response. + *

+ * See USB Device Class Specification for Device Firmware Upgrade, version 1.1. + *

+ */ +public record DFUStatus(DeviceStatus status, int pollTimeout, DeviceState state) { + + public static DFUStatus fromBytes(byte[] data) { + var status = DeviceStatus.fromValue(data[0]); + var pollTimeout = (data[1] & 0xff) + 256 * (data[2] & 0xff) + 256 * 256 * (data[3] & 0xff); + var state = DeviceState.fromValue(data[4]); + return new DFUStatus(status, pollTimeout, state); + } +} diff --git a/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DeviceState.java b/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DeviceState.java new file mode 100644 index 00000000..35532fd9 --- /dev/null +++ b/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DeviceState.java @@ -0,0 +1,71 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.dfu; + +/** + * DFU device state. + *

+ * See USB Device Class Specification for Device Firmware Upgrade, version 1.1. + *

+ */ +public enum DeviceState { + /** + * Device is running its normal application. + */ + APP_IDLE, + /** + * Device is running its normal application, has received the DFU_DETACH request, + * and is waiting for a USB reset. + */ + APP_DETACH, + /** + * Device is operating in the DFU mode and is waiting for requests. + */ + DFU_IDLE, + /** + * Device has received a block and is waiting for the host to solicit the status via DFU_GETSTATUS. + */ + DFU_DNLOAD_SYNC, + /** + * Device is programming a control-write block into its nonvolatile memories. + */ + DFU_DNBUSY, + /** + * Device is processing a download operation. Expecting DFU_DNLOAD requests. + */ + DFU_DNLOAD_IDLE, + /** + * Device has received the final block of firmware from the host and is waiting for receipt of + * DFU_GETSTATUS to begin the Manifestation phase; or device has completed the Manifestation phase and is + * waiting for receipt of DFU_GETSTATUS. (Devices that can enter this state after the Manifestation phase + * set bmAttributes bit bitManifestationTolerant to 1.) + */ + DFU_MANIFEST_SYNC, + /** + * Device is in the Manifestation phase. (Not all devices will be able to respond to DFU_GETSTATUS + * when in this state. + */ + DFU_MANIFEST, + /** + * Device has programmed its memories and is waiting for a USB reset or a power on reset. + * (Devices that must enter this state clear bitManifestationTolerant to 0.) + */ + DFU_MANIFEST_WAIT_RESET, + /** + * The device is processing an upload operation. Expecting DFU_UPLOAD requests. + */ + DFU_UPLOAD_IDLE, + /** + * An error has occurred. Awaiting the DFU_CLRSTATUS request. + */ + DFU_ERROR; + + public static DeviceState fromValue(byte value) { + return values()[value]; + } +} diff --git a/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DeviceStatus.java b/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DeviceStatus.java new file mode 100644 index 00000000..c702112b --- /dev/null +++ b/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DeviceStatus.java @@ -0,0 +1,86 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.dfu; + +/** + * DFU device status. + *

+ * See USB Device Class Specification for Device Firmware Upgrade, version 1.1. + *

+ */ +public enum DeviceStatus { + + /** + * No error condition is present + */ + OK, + /** + * File is not targeted for use by this device. + */ + ERR_TARGET, + /** + * File is for this device but fails some vendor-specific verification test. + */ + ERR_FILE, + /** + * Device is unable to write memory. + */ + ERR_WRITE, + /** + * Memory erase function failed. + */ + ERR_ERASE, + /** + * Memory erase check failed. + */ + ERR_CHECK_ERASED, + /** + * Program memory function failed. + */ + ERR_PROG, + /** + * Programmed memory failed verification. + */ + ERR_VERIFY, + /** + * Cannot program memory due to received address that is out of range. + */ + ERR_ADDRESS, + /** + * Received DFU_DNLOAD with wLength = 0, but device does not think it has all of the data yet. + */ + ERR_NOTDONE, + /** + * Device’s firmware is corrupt. It cannot return to run-time (non-DFU) operations. + */ + ERR_FIRMWARE, + /** + * iString indicates a vendor-specific error. + */ + ERR_VENDOR, + /** + * Device detected unexpected USB reset signaling. + */ + ERR_USBR, + /** + * Device detected unexpected power on reset. + */ + ERR_POR, + /** + * Something went wrong, but the device does not know what it was. + */ + ERR_UNKNOWN, + /** + * Device stalled an unexpected request. + */ + ERR_STALLEDPKT; + + public static DeviceStatus fromValue(byte value) { + return values()[value]; + } +} diff --git a/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/Page.java b/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/Page.java new file mode 100644 index 00000000..92158670 --- /dev/null +++ b/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/Page.java @@ -0,0 +1,54 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.dfu; + +/** + * Page of flash memory, RAM or other type of memory. + *

+ * If count is > 1, it represents a sector consisting of multiple equal pages. + *

+ * @param segment the memory segment this page belongs to + * @param startAddress start address + * @param count number of pages + * @param pageSize page size (in bytes) + * @param attributes page attributes + */ +public record Page(Segment segment, int startAddress, int count, int pageSize, int attributes) { + + /** + * Gets the end address of the page or sector. + * @return the end address + */ + public int getEndAddress() { + return startAddress + count * pageSize; + } + + /** + * Indicates if the page or sector is readable. + * @return {@code true} if it is readable + */ + public boolean isReadable() { + return (attributes & 1) != 0; + } + + /** + * Indicates if the page or sector is erasable. + * @return {@code true} if it is erasable + */ + public boolean isErasable() { + return (attributes & 2) != 0; + } + + /** + * Indicates if the page or sector is writable. + * @return {@code true} if it is writable + */ + public boolean isWritable() { + return (attributes & 4) != 0; + } +} diff --git a/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/STM32.java b/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/STM32.java new file mode 100644 index 00000000..a9f40b57 --- /dev/null +++ b/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/STM32.java @@ -0,0 +1,18 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.dfu; + +/** + * STM32 specific constants + */ +public class STM32 { + + private STM32() { } + + public static final int FLASH_BASE_ADDRESS = 0x08000000; +} diff --git a/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/Segment.java b/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/Segment.java new file mode 100644 index 00000000..f3fdad2b --- /dev/null +++ b/examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/Segment.java @@ -0,0 +1,154 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.dfu; + +import net.codecrete.usb.UsbControlTransfer; +import net.codecrete.usb.UsbDevice; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +import static net.codecrete.usb.UsbRecipient.DEVICE; +import static net.codecrete.usb.UsbRequestType.STANDARD; + +/** + * Represents a memory segment of the USB device, be it flash memory, RAM or any other type. + */ +public class Segment { + + private static final Pattern SEGMENT_PATTERN = Pattern.compile("@([^/]+)/0x([0-9A-Fa-f]+)/"); + private static final Pattern SECTOR_PATTERN = Pattern.compile(",?(\\d+)\\*(\\d+) ?([BKM]?)(.)"); + + /** + * Decodes the segment information from the USB interface description. + * @param device the USB device + * @param interfaceNumber the number of the DFU USB interface + * @return list of segments + */ + public static List getSegments(UsbDevice device, int interfaceNumber) { + var result = new ArrayList(); + + // STM uses multiple alternate interface settings to represent segments. + // The alternate interface setting name describes the sectors within the segment. + var configDesc = device.getConfigurationDescriptor(); + int offset = 0; + while (offset < configDesc.length) { + if (configDesc[offset + 1] == 4 && (configDesc[offset + 2] & 0xff) == interfaceNumber) { + int altSetting = configDesc[offset + 3] & 0xff; + int stringIndex = configDesc[offset + 8] & 0xff; + var altSettingName = getStringDescriptor(device, stringIndex); + result.add(new Segment(altSetting, altSettingName)); + } + offset += configDesc[offset] & 0xff; + } + + return result; + } + + /** + * Retrieves a USB string. + * @param device the USB device + * @param index the index of the string descriptor + * @return the string + */ + private static String getStringDescriptor(UsbDevice device, int index) { + var setup = new UsbControlTransfer(STANDARD, DEVICE, 6, (3 << 8) | index, 0); + byte[] stringDesc = device.controlTransferIn(setup, 255); + int descLen = stringDesc[0] & 0xff; + return new String(stringDesc, 2, descLen - 2, StandardCharsets.UTF_16LE); + } + + /** + * Gets the page for the specified address. + * @param segments the list of segments + * @param address the address + * @return the page, or {@code null} if not found + */ + public static Page findPage(List segments, int address) { + for (var seg : segments) { + for (var sec : seg.getSectors()) { + if (address >= sec.startAddress() && address < sec.getEndAddress()) { + int offset = address - sec.startAddress(); + int pageNum = offset / sec.pageSize(); + return new Page(seg, sec.startAddress() + pageNum * sec.pageSize(), 1, sec.pageSize(), sec.attributes()); + } + } + } + + return null; + } + + private final int altSetting; + private final String name; + + private final List sectors; + + + /** + * Creates a new instance. + *

+ * The segment descriptor is the name of the USB alternate interface setting. + *

+ * @param altSetting alternate interface setting number + * @param segmentDesc segment descriptor + */ + private Segment(int altSetting, String segmentDesc) { + // The format is described in "UM0424 STM32 USB-FS-Device development kit", ch. 10.3.2 + this.altSetting = altSetting; + sectors = new ArrayList<>(); + + var match = SEGMENT_PATTERN.matcher(segmentDesc); + if (!match.find()) + throw new DFUException("Invalid segment description: " + segmentDesc); + this.name = match.group(1).trim(); + var startAddress = (int) Long.parseLong(match.group(2), 16); + + match = SECTOR_PATTERN.matcher(segmentDesc.substring(match.end())); + while (match.find()) { + var count = Integer.parseInt(match.group(1)); + var size = Integer.parseInt(match.group(2)); + var multiplier = match.group(3); + var attributes = match.group(4).charAt(0) - 0x60; + + if (multiplier.equals("K")) { + size *= 1024; + } else if (multiplier.equals("M")) { + size *= 1024 * 1024; + } + + sectors.add(new Page(this, startAddress, count, size, attributes)); + startAddress += size; + } + } + + /** + * Gets the alternative interface setting number + * @return the setting number + */ + public int getAltSetting() { + return altSetting; + } + + /** + * Gets the segment name. + * @return the name + */ + public String getName() { + return name; + } + + /** + * Gets the sectors withing the segment + * @return list of sectors + */ + public List getSectors() { + return sectors; + } +} diff --git a/java-does-usb/.mvn/wrapper/maven-wrapper.properties b/java-does-usb/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..d58dfb70 --- /dev/null +++ b/java-does-usb/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +wrapperVersion=3.3.2 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip diff --git a/java-does-usb/jextract/README.md b/java-does-usb/jextract/README.md index cfd1632c..79d0f270 100644 --- a/java-does-usb/jextract/README.md +++ b/java-does-usb/jextract/README.md @@ -1,42 +1,37 @@ # Code Generation with *jextract* -Some of the binding code for accessing native functions and data structures is generated with [jextract](https://jdk.java.net/jextract/). The tool is still under construction and has its limitations. +A major part of the binding code for accessing native functions and data structures is generated with [jextract](https://jdk.java.net/jextract/). *jextract* is not bundled with the JDK. The binaries can be downloaded from [jdk.java.net/jextract](https://jdk.java.net/jextract/) -In order to generate the code, the scripts in this directory have to be run (`gen_linux.sh`, `gen_macos.sh` and `gen_win_xxx.cmd`). Each script has to be run on that particular operating system. +In order to generate the code, the scripts in the subdirectories have to be run (`linux/gen_linux.sh`, `macos/gen_macos.sh` and `windows/gen_win.cmd`). Each script has to be run on the particular operating system. The scripts expect the *jextract* binary to be in a *jextract* directory at the same parent directory as the *Java Does USB* project. If that is not the case, the *jextract* path can be modified at the top of the scripts. -The code is generated in directories below `gen`, i.e. `main/java/net/codecrete/usb/linux/gen` and similar for the other operating systems. For each library (`xxx.so` or `xxx.dll`) and each macOS framework, a separate package is created. +The code is generated in directories below `gen`, i.e. `main/java/net/codecrete/usb/linux/gen` and similarly for the other operating systems. For each library (`xxx.so` or `xxx.dll`) and each macOS framework, a separate package is created. -The scripts explicitly specify the functions, structs etc. to include as generating code for entire operating system header files can result in an excessive amount of Java source files and classes. +The scripts explicitly specify the functions, structs etc. to include as generating code for entire operating system header files will result in an excessive amount of Java source files and classes. -The resulting code is then committed to the source code repository. Before the commit, imports are cleaned up to get rid of superfluous imports. Most IDEs provide a convenient command to execute this on entire directories. +The resulting code is then committed to the source code repository. ## General limitations -- Binaries of *jextract* can be downloaded from https://jdk.java.net/jextract/. x64 binaries are available but no ARM64 binaries. According to the mailing list, cross-compiling is not possible, i.e. ARM64 binaries are needed on macOS with Apple Silicon. But so far, the x64 binaries (using the Rosetta2 emulation) have worked without problems. +- According to the jextract mailing list, it would be required to create separate code for Intel x64 and ARM64 architecture. And jextract would need to be run on each architecture separately (no cross-compilation). Fortunately, this doesn't seem to be the case. Linux code generated on Intel x64 also runs on ARM64 without change. The same holds for macOS. However, jextract needs to be run on each operating system separately. -- `typedef` and `struct`: - - 1. If only the `typedef` is included (`--include-typedef`), an empty Java class is generated. - 2. If both *typedef* and the `struct` it refers to are included, the `typedef` class inherits from the `struct` class, which contains all the `struct` members. - 3. If the `typedef` refers to an unnamed `struct`, the generated class contains all the `struct` members. - - Case 1 looks like a bug. +- The *Foreign Function And Memory* API has the abilitiy to save the thread-specific error values (`GetLastError()` on Windows, `errno` on Linux). This is required as the JVM calls operating system functions as well, which overwrite the result values. To save the values, an additional parameter must be added to function calls. Unfortunately, this is not supported by jextract. So a good number of function bindings have to be written manually. - *jextract* is not really transparent about what it does. It often skips elements without providing any information. In particular, it will silently skip a requested element in these cases: - `--include-var myvar` if `myvar` is declared as `static`. - - `--include-var myvar` if `myvar` is an `enum` constant. `enum` constants must be requested with `--include-macro`. - - `--include-macro MYMACRO` if `MYMACRO` is function-like, even if it evaluates to a constant. + - `--include-var myvar` if `myvar` is an `enum` constant. `enum` constants must be requested with `--include-constant`. + - `--include-constant MYCONSTANT` if `MYCONSTANT` is function-like, even if it evaluates to a constant. - `--include-struct mystruct` if `mystruct` is actually a `typedef` to a `struct`. - `--include-typedef mystruct` if `mystruct` is actually a `struct`. - `--include-typedef mytypedef` if `mytypedef` is a `typedef` for a primitive type. +- *jextract* resolves all _typedef_s to their actual types. So this library does not use any _--include-typedef_ option. And there does not seem any obvious use for it beyond cosmetics. ## Linux -To run the script, most likely the header files for *libudev* must be installed (the library itself is most likely already installed): +To run the script, the header files for *libudev* must be present. In most cases, they aren't install by default (in contrast to the library itself): ``` sudo apt-get install libudev-dev @@ -44,64 +39,44 @@ sudo apt-get install libudev-dev On Linux, the limitations are: -- `usbdevice_fs.h`: The macro `USBDEVFS_CONTROL` and all similar ones are not generated. They are probably considered function-like macros. *jextract* does not generate code for function-like macros. But `USBDEVFS_CONTROL` evaluates to a constant. +- `usbdevice_fs.h`: The macro `USBDEVFS_CONTROL` and all similar ones are not generated. They are probably considered function-like macros. *jextract* does not generate code for function-like macros. `USBDEVFS_CONTROL` would evaluate to a constant. - `sd-device.h` (header file for *libsystemd*): *jextract* fails with *"Error: /usr/include/inttypes.h:290:8: error: unknown type name 'intmax_t'"*. The reason is yet unknown. This code is currently not needed as *libudev* is used instead of *libsystemd*. They are related, *libsystemd* is the future solution, but it is missing support for monitoring devices. -- `libudev.h`: After code generation, the class `RuntimeHelper.java` in `.../linux/gen/udev` must be manually modified as the code to access the library does not work for the directory the library is located in. So replace: - -``` -System.loadLibrary("udev"); -SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); -``` - -with: - -``` -SymbolLookup loaderLookup = SymbolLookup.libraryLookup("libudev.so", MemorySession.openImplicit()); -``` ## MacOS -Most of the required native functions on macOS are part of a framework. Framework internally have a more complex file organization of header and binary files than appears on the outside. Thus they require a special logic to locate framework header files. *clang* supports it with the `-F`. *jextract* allows to specify the options via `compiler_flags.txt` file. Since the file must be in the local directory and since it does not apply to Linux and Windows, separate directories must be used for the operating systems. +Most of the required native functions on macOS are part of a framework. Frameworks internally have a more complex file organization of header and binary files than appears from the outside. Thus, they require a special logic to locate framework header files. *clang* supports it with the `-F`. *jextract* allows to specify the options via `compiler_flags.txt` file. Since the file must be in the local directory and since it does not apply to Linux and Windows, separate directories must be used for the operating systems. -The generated code has the same problem as the Linux code for *udev*. It must be manually changed to use `SymbolLookup.libraryLookup()` for the libraries `CoreFoundation.framework/CoreFoundation` and `IOKit.framework/IOKit` respectively. ## Windows -Most Windows SDK header files are not independent. They require that `Windows.h` is included first. So instead of specifying the target header files directly, a helper header file (`windows_headers.h` in this directory) is specified. +The Windows code is not generated with _jextract_ but with [Windows API Generator](https://github.com/manuelbl/WindowsApiGenerator) +instead. It is run as a Maven plugin. The generated code is not committed to GitHub. -The known limitations are: - -- Variable size `struct`: Several Windows struct are of variable size. The last member is an array. The `struct` definition specifies array length 1. But you are expected to allocate more space depending on the actual array size you need. *jextract* generates code for array length 1 and when access array members, the length is checked. So the generated code is difficult to use. Variable size `struct`s are a pain - in any language. - -- `USB_NODE_CONNECTION_INFORMATION_EX`: This struct uses a packed layout without considering alignment. The last four members are on an odd offset even though they are multiple bytes long. *jextract* creates the correct offsets but defines strict alignment constraints for the members. So the memory layout cannot be instantiated as it throws an exception. - -- GUID constants like `GUID_DEVINTERFACE_USB_DEVICE` do not work. While code is generated, the code fails at run-time as it is unable to locate the symbol. This is due to the fact that `GUID_DEVINTERFACE_USB_DEVICE` actually resolve to a variable definition and not to a variable declaration. It is not part of any library. Such constants should be skipped by *jextract*. - -- *jextract* is a batch script and turns off *echo mode*. If a single batch scripts has multiple calls of *jextract*, two things need to be considered: - - - If the regular command interpreter `cmd.exe` is used, *jextract* must be called using `call`, i.e. `call jextract header.h`. - - If *PowerShell* is used instead, `call` is not needed but *PowerShell* must be configured to allow the execution of scripts. - - *jextract* turns off *echo mode*. So the first call will behave differently than the following calls. +Windows API Generator supports call state capturing (`GetLastError()`), structs with a +variable size, GUID and device property key (`DEVPKEY`) constants etc. ## Code Size *jextract* generates a comprehensive set of methods for each function, struct, struct member etc. Most of it will not be used as a typical application just uses a subset of struct members, might only read or write them etc. So a considerable amount of code is generated. For some types, it's a bit excessive. -The worst example is [`IOUSBInterfaceStruct942`](https://github.com/manuelbl/JavaDoesUSB/blob/main/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBDeviceStruct942.java) (macOS). This is a `struct` consisting of about 75 member functions. It's bascially a vtable of a C++ class. *jextract* generates the same number of classes plus a huge class for the struct itself. The total code size (compiled) for this single `struct` is over 300 kByte. +The worst example is [`IOUSBInterfaceStruct190`](https://github.com/manuelbl/JavaDoesUSB/blob/main/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBInterfaceStruct190.java) (macOS). This is a `struct` consisting of about 50 member functions. It's basically a vtable of a C++ class. For this single `struct`, *jextract* generates codes resulting in 100 class files with a total size of 213kByte. + +The table below shows class file size statistics for version 1.2.2 of the library: + +| Operating Systems | Manually Written | % | Generated | % | Total | % | +|-------------------|-----------------:|------:|----------:|------:|----------:|--------:| +| Linux | 58,393 | 4.5% | 154,398 | 11.8% | 212,791 | 16.3% | +| macOS | 81,441 | 6.2% | 427,258 | 32.8% | 508,699 | 39.0% | +| Windows | 84,099 | 6.5% | 384,452 | 29.5% | 468,551 | 35.9% | +| Common | 113,568 | 8.7% | | | 113,568 | 8.7% | +| Grand Total | 337,501 | 25.9% | 966,108 | 74.1% | 1,303,609 | 100.0% | -The table below shows statictics for version 0.2.0 of the library: -| Operating Systems | Manually Created | % | Generated | % | Total | % | -| - | -:| -:| -:| -:| -:| -:| -| Linux | 24,140 | 1.96% | 182,774 | 14.84% | 206,914 | 16.80% | -| macOS | 57,788 | 4.69% | 666,037 | 54.08% | 723,825 | 58.77% | -| Windows | 46,085 | 3.74% | 201,000 | 16.32% | 247,085 | 20.06% | -| Common | 53,774 | 4.37% | 53,774 | 0.00% | | 4.37% | -| Grand Total | 181,787 | 14.76% | 1,049,811 | 85.24% | 1,231,598 | 100.00% | +*Class File Size (compiled), in bytes and percentage of total size* -*Code Size (compiled), in bytes and percentage of total size* +If *jextract* could generate code for error state capturing, there would be even more generated and less manually written code. diff --git a/java-does-usb/jextract/linux/epoll.h b/java-does-usb/jextract/linux/epoll.h new file mode 100644 index 00000000..c7c68b45 --- /dev/null +++ b/java-does-usb/jextract/linux/epoll.h @@ -0,0 +1,3 @@ +typedef unsigned int uint32_t; +typedef unsigned long int uint64_t; +#include diff --git a/java-does-usb/jextract/linux/gen_linux.sh b/java-does-usb/jextract/linux/gen_linux.sh index ce4b94c1..7e5eb5ec 100755 --- a/java-does-usb/jextract/linux/gen_linux.sh +++ b/java-does-usb/jextract/linux/gen_linux.sh @@ -1,40 +1,41 @@ #!/bin/sh -JEXTRACT=../../../../jextract-19/bin/jextract +JEXTRACT=../../../../jextract/bin/jextract -# sd-device.h (install libsystemd-dev if file is missing) -# Error: /usr/include/inttypes.h:290:8: error: unknown type name 'intmax_t' -#$JEXTRACT --source --output ../src/main/java \ -#$JEXTRACT --source --output ../src/main/java \ -# --header-class-name sd_device \ -# --target-package net.codecrete.usb.linux.gen.sd-device -# /usr/include/systemd/sd-device.h +rm -rf ../../src/main/java/net/codecrete/usb/linux/gen # errno.h -$JEXTRACT --source --output ../../src/main/java \ +$JEXTRACT --output ../../src/main/java \ --header-class-name errno \ --target-package net.codecrete.usb.linux.gen.errno \ - --include-function __errno_location \ + --include-constant EPIPE \ + --include-constant EAGAIN \ + --include-constant EBADF \ + --include-constant ECANCELED \ + --include-constant EINVAL \ + --include-constant ENODEV \ + --include-constant EINTR \ + --include-constant ENOENT \ /usr/include/errno.h -# ioctl.h -$JEXTRACT --source --output ../../src/main/java \ - --header-class-name ioctl \ - --target-package net.codecrete.usb.linux.gen.ioctl \ - --include-function ioctl \ - /usr/include/x86_64-linux-gnu/sys/ioctl.h +# string.h +$JEXTRACT --output ../../src/main/java \ + --header-class-name string \ + --target-package net.codecrete.usb.linux.gen.string \ + --include-function strerror \ + /usr/include/string.h # fcntl.h -$JEXTRACT --source --output ../../src/main/java \ +$JEXTRACT --output ../../src/main/java \ --header-class-name fcntl \ --target-package net.codecrete.usb.linux.gen.fcntl \ - --include-function open \ - --include-macro O_CLOEXEC \ - --include-macro O_RDWR \ + --include-constant O_CLOEXEC \ + --include-constant O_RDWR \ + --include-constant FD_CLOEXEC \ /usr/include/fcntl.h # unistd.h -$JEXTRACT --source --output ../../src/main/java \ +$JEXTRACT --output ../../src/main/java \ --header-class-name unistd \ --target-package net.codecrete.usb.linux.gen.unistd \ --include-function close \ @@ -42,22 +43,29 @@ $JEXTRACT --source --output ../../src/main/java \ # usbdevice_fs.h # Missing constants like USBDEVFS_CLAIMINTERFACE -$JEXTRACT --source --output ../../src/main/java \ +$JEXTRACT --output ../../src/main/java \ --header-class-name usbdevice_fs \ --target-package net.codecrete.usb.linux.gen.usbdevice_fs \ --include-struct usbdevfs_bulktransfer \ --include-struct usbdevfs_ctrltransfer \ - --include-macro USBDEVFS_CONTROL \ - --include-macro USBDEVFS_BULK \ - --include-macro USBDEVFS_CLAIMINTERFACE \ - --include-macro USBDEVFS_RELEASEINTERFACE \ + --include-struct usbdevfs_setinterface \ + --include-struct usbdevfs_urb \ + --include-struct usbdevfs_disconnect_claim \ + --include-struct usbdevfs_ioctl \ + --include-struct usbdevfs_iso_packet_desc \ + --include-constant USBDEVFS_URB_TYPE_INTERRUPT \ + --include-constant USBDEVFS_URB_TYPE_CONTROL \ + --include-constant USBDEVFS_URB_TYPE_BULK \ + --include-constant USBDEVFS_URB_TYPE_ISO \ + --include-constant USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER \ /usr/include/linux/usbdevice_fs.h # libudev.h -$JEXTRACT --source --output ../../src/main/java \ +# (install libudev-dev if file is missing) +$JEXTRACT --output ../../src/main/java \ --header-class-name udev \ --target-package net.codecrete.usb.linux.gen.udev \ - -l udev \ + -l :libudev.so.1 \ --include-function udev_new \ --include-function udev_enumerate_new \ --include-function udev_enumerate_add_match_subsystem \ @@ -79,10 +87,14 @@ $JEXTRACT --source --output ../../src/main/java \ --include-function udev_monitor_get_fd \ /usr/include/libudev.h -# select.h -$JEXTRACT --source --output ../../src/main/java \ - --header-class-name select \ - --target-package net.codecrete.usb.linux.gen.select \ - --include-function select \ - --include-typedef fd_set \ - /usr/include/x86_64-linux-gnu/sys/select.h +# epoll.h +$JEXTRACT --output ../../src/main/java \ + --header-class-name epoll \ + --target-package net.codecrete.usb.linux.gen.epoll \ + --include-constant EPOLL_CTL_ADD \ + --include-constant EPOLL_CTL_DEL \ + --include-constant EPOLLIN \ + --include-constant EPOLLOUT \ + --include-constant EPOLLWAKEUP \ + epoll.h + diff --git a/java-does-usb/jextract/macos/gen_macos.sh b/java-does-usb/jextract/macos/gen_macos.sh index c90d5cbe..d74e38c2 100755 --- a/java-does-usb/jextract/macos/gen_macos.sh +++ b/java-does-usb/jextract/macos/gen_macos.sh @@ -1,17 +1,19 @@ #!/bin/sh -JEXTRACT=../../../../jextract-19/bin/jextract +JEXTRACT=../../../../jextract/bin/jextract # If SDK_DIR is changed, it needs to be changed in compile_flags.txt as well. SDK_DIR=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk +rm -rf ../../src/main/java/net/codecrete/usb/macos/gen + # CoreFoundation -$JEXTRACT --source --output ../../src/main/java \ +$JEXTRACT --output ../../src/main/java \ -I $SDK_DIR/usr/include \ - -lCoreFoundation.framework \ + -l :/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation \ --header-class-name CoreFoundation \ --target-package net.codecrete.usb.macos.gen.corefoundation \ - --include-typedef CFRange \ - --include-typedef CFUUIDBytes \ + --include-struct CFRange \ + --include-struct CFUUIDBytes \ --include-function CFUUIDCreateFromUUIDBytes \ --include-function CFRelease \ --include-function CFStringGetLength \ @@ -23,24 +25,31 @@ $JEXTRACT --source --output ../../src/main/java \ --include-function CFNumberGetValue \ --include-function CFRunLoopGetCurrent \ --include-function CFRunLoopAddSource \ + --include-function CFRunLoopRemoveSource \ --include-function CFRunLoopRun \ + --include-function CFMessagePortCreateLocal \ + --include-function CFMessagePortCreateRunLoopSource \ + --include-function CFMessagePortCreateRemote \ + --include-function CFMessagePortSendRequest \ + --include-function CFDataCreate \ + --include-function CFDataGetBytePtr \ --include-function CFUUIDGetUUIDBytes \ - --include-macro kCFNumberSInt32Type \ + --include-constant kCFNumberSInt32Type \ cf_helper.h # IOKit -$JEXTRACT --source --output ../../src/main/java \ +$JEXTRACT --output ../../src/main/java \ -I $SDK_DIR/usr/include \ - -lIOKit.framework \ + -l :/System/Library/Frameworks/IOKit.framework/IOKit \ --header-class-name IOKit \ --target-package net.codecrete.usb.macos.gen.iokit \ --include-var kIOMasterPortDefault \ - --include-macro kIOUSBDeviceClassName \ - --include-macro kIOFirstMatchNotification \ - --include-macro kIOTerminatedNotification \ + --include-constant kIOUSBDeviceClassName \ + --include-constant kIOFirstMatchNotification \ + --include-constant kIOTerminatedNotification \ + --include-constant kIOReturnExclusiveAccess \ --include-var kCFRunLoopDefaultMode \ --include-struct IOCFPlugInInterfaceStruct \ - --include-typedef IOCFPlugInInterface \ --include-function IOObjectRelease \ --include-function IOIteratorNext \ --include-function IOCreatePlugInInterfaceForService \ @@ -50,19 +59,27 @@ $JEXTRACT --source --output ../../src/main/java \ --include-function IORegistryEntryGetRegistryEntryID \ --include-function IOServiceAddMatchingNotification \ --include-function IOServiceMatching \ - --include-struct IOUSBDeviceStruct942 \ - --include-typedef IOUSBDeviceInterface \ - --include-macro kIOUSBFindInterfaceDontCare \ - --include-typedef IOUSBFindInterfaceRequest \ - --include-typedef IOUSBDevRequest \ - --include-struct IOUSBInterfaceStruct942 \ - --include-typedef IOUSBInterfaceInterface \ + --include-struct IOUSBDeviceStruct187 \ + --include-constant kIOUSBFindInterfaceDontCare \ + --include-struct IOUSBFindInterfaceRequest \ + --include-struct IOUSBDevRequest \ + --include-struct IOUSBInterfaceStruct190 \ + --include-constant kIOUSBTransactionTimeout \ + --include-constant kIOReturnAborted \ + --include-constant kIOUSBPipeStalled \ + --include-constant kUSBReEnumerateCaptureDeviceMask \ + --include-constant kUSBReEnumerateReleaseDeviceMask \ + --include-struct CFUUIDBytes \ iokit_helper.h # mach.h -$JEXTRACT --source --output ../../src/main/java \ +$JEXTRACT --output ../../src/main/java \ -I $SDK_DIR/usr/include \ --header-class-name mach \ --target-package net.codecrete.usb.macos.gen.mach \ --include-function mach_error_string \ $SDK_DIR/usr/include/mach/mach.h + +sed -i '' -E -f remove_fp_upcall.sed ../../src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBDeviceStruct187.java +sed -i '' -E -f remove_fp_upcall.sed ../../src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBInterfaceStruct190.java +sed -i '' -E -f remove_fp_upcall.sed ../../src/main/java/net/codecrete/usb/macos/gen/iokit/IOCFPlugInInterfaceStruct.java diff --git a/java-does-usb/jextract/macos/remove_fp_upcall.sed b/java-does-usb/jextract/macos/remove_fp_upcall.sed new file mode 100644 index 00000000..d90e01d3 --- /dev/null +++ b/java-does-usb/jextract/macos/remove_fp_upcall.sed @@ -0,0 +1,6 @@ +/The function pointer signature/,/ \}/ { + s/^.*function pointer signature.*$/ *\//p + d + +} +/MethodHandle UP\$MH = /,/ \}/d diff --git a/java-does-usb/jextract/windows/gen_win.cmd b/java-does-usb/jextract/windows/gen_win.cmd deleted file mode 100644 index c69865d2..00000000 --- a/java-does-usb/jextract/windows/gen_win.cmd +++ /dev/null @@ -1,154 +0,0 @@ -set JEXTRACT=..\..\..\..\jextract-19\bin\jextract.bat -set SDK_DIR=C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0 - -call %JEXTRACT% --source --output ../../src/main/java ^ - -D _AMD64_ -D _M_AMD64=100 -D UNICODE -D _UNICODE ^ - -I "%SDK_DIR%\um" ^ - -I "%SDK_DIR%\shared" ^ - -l Kernel32 ^ - --header-class-name Kernel32 ^ - --target-package net.codecrete.usb.windows.gen.kernel32 ^ - --include-function CreateFileW ^ - --include-function CloseHandle ^ - --include-function DeviceIoControl ^ - --include-function GetLastError ^ - --include-function GetModuleHandleW ^ - --include-function FormatMessageW ^ - --include-function LocalFree ^ - --include-macro ERROR_SUCCESS ^ - --include-macro ERROR_NO_MORE_ITEMS ^ - --include-macro ERROR_MORE_DATA ^ - --include-macro ERROR_INSUFFICIENT_BUFFER ^ - --include-macro ERROR_FILE_NOT_FOUND ^ - --include-macro GENERIC_READ ^ - --include-macro GENERIC_WRITE ^ - --include-macro FILE_SHARE_READ ^ - --include-macro FILE_SHARE_WRITE ^ - --include-macro FILE_ATTRIBUTE_NORMAL ^ - --include-macro FILE_FLAG_OVERLAPPED ^ - --include-macro OPEN_EXISTING ^ - --include-macro FORMAT_MESSAGE_ALLOCATE_BUFFER ^ - --include-macro FORMAT_MESSAGE_FROM_SYSTEM ^ - --include-macro FORMAT_MESSAGE_IGNORE_INSERTS ^ - --include-struct _GUID ^ - --include-typedef GUID ^ - windows_headers.h - -call %JEXTRACT% --source --output ../../src/main/java ^ - -D _AMD64_ -D _M_AMD64=100 -D UNICODE -D _UNICODE ^ - -I "%SDK_DIR%\um" ^ - -I "%SDK_DIR%\shared" ^ - -l SetupAPI ^ - --header-class-name SetupAPI ^ - --target-package net.codecrete.usb.windows.gen.setupapi ^ - --include-function SetupDiGetClassDevsW ^ - --include-function SetupDiDestroyDeviceInfoList ^ - --include-function SetupDiEnumDeviceInfo ^ - --include-function SetupDiEnumDeviceInterfaces ^ - --include-function SetupDiGetDeviceInterfaceDetailW ^ - --include-function SetupDiGetDeviceRegistryPropertyW ^ - --include-function SetupDiGetDevicePropertyW ^ - --include-function SetupDiOpenDeviceInterfaceW ^ - --include-function SetupDiCreateDeviceInfoList ^ - --include-function SetupDiOpenDeviceInfoW ^ - --include-function SetupDiOpenDevRegKey ^ - --include-function SetupDiDeleteDeviceInterfaceData ^ - --include-struct _SP_DEVINFO_DATA ^ - --include-typedef SP_DEVINFO_DATA ^ - --include-struct _SP_DEVICE_INTERFACE_DATA ^ - --include-typedef SP_DEVICE_INTERFACE_DATA ^ - --include-struct _SP_DEVICE_INTERFACE_DETAIL_DATA_W ^ - --include-typedef SP_DEVICE_INTERFACE_DETAIL_DATA_W ^ - --include-macro DIGCF_PRESENT ^ - --include-macro DIGCF_DEVICEINTERFACE ^ - --include-macro SPDRP_ADDRESS ^ - --include-macro DEVPROP_TYPE_UINT32 ^ - --include-macro DEVPROP_TYPE_STRING ^ - --include-macro DEVPROP_TYPEMOD_LIST ^ - --include-macro DICS_FLAG_GLOBAL ^ - --include-macro DIREG_DEV ^ - windows_headers.h - -call %JEXTRACT% --source --output ../../src/main/java ^ - -D _AMD64_ -D _M_AMD64=100 -D UNICODE -D _UNICODE ^ - --header-class-name StdLib ^ - --target-package net.codecrete.usb.windows.gen.stdlib ^ - --include-function wcslen ^ - windows_headers.h - -call %JEXTRACT% --source --output ../../src/main/java ^ - -D _AMD64_ -D _M_AMD64=100 -D UNICODE -D _UNICODE ^ - -I "%SDK_DIR%\um" ^ - -I "%SDK_DIR%\shared" ^ - --header-class-name USBIoctl ^ - --target-package net.codecrete.usb.windows.gen.usbioctl ^ - --include-macro IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX ^ - --include-macro IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION ^ - windows_headers.h - -call %JEXTRACT% --source --output ../../src/main/java ^ - -D _AMD64_ -D _M_AMD64=100 -D UNICODE -D _UNICODE ^ - -I "%SDK_DIR%\um" ^ - -I "%SDK_DIR%\shared" ^ - -l User32 ^ - --header-class-name User32 ^ - --target-package net.codecrete.usb.windows.gen.user32 ^ - --include-function RegisterClassExW ^ - --include-function CreateWindowExW ^ - --include-function RegisterDeviceNotificationW ^ - --include-function GetMessageW ^ - --include-function DefWindowProcW ^ - --include-macro DEVICE_NOTIFY_WINDOW_HANDLE ^ - --include-macro HWND_MESSAGE ^ - --include-macro WM_DEVICECHANGE ^ - --include-macro DBT_DEVICEARRIVAL ^ - --include-macro DBT_DEVICEREMOVECOMPLETE ^ - --include-macro DBT_DEVTYP_DEVICEINTERFACE ^ - --include-struct tagMSG ^ - --include-typedef MSG ^ - --include-struct tagWNDCLASSEXW ^ - --include-typedef WNDCLASSEXW ^ - --include-struct _DEV_BROADCAST_HDR ^ - --include-typedef DEV_BROADCAST_HDR ^ - --include-struct _DEV_BROADCAST_DEVICEINTERFACE_W ^ - --include-typedef DEV_BROADCAST_DEVICEINTERFACE_W ^ - windows_headers.h - -call %JEXTRACT% --source --output ../../src/main/java ^ - -D _AMD64_ -D _M_AMD64=100 -D UNICODE -D _UNICODE ^ - -I "%SDK_DIR%\um" ^ - -I "%SDK_DIR%\shared" ^ - -l Winusb ^ - --header-class-name WinUSB ^ - --target-package net.codecrete.usb.windows.gen.winusb ^ - --include-function WinUsb_Initialize ^ - --include-function WinUsb_Free ^ - --include-function WinUsb_GetDescriptor ^ - --include-function WinUsb_ControlTransfer ^ - --include-function WinUsb_WritePipe ^ - --include-function WinUsb_ReadPipe ^ - --include-function WinUsb_GetAssociatedInterface ^ - windows_headers.h - -call %JEXTRACT% --source --output ../../src/main/java ^ - -D _AMD64_ -D _M_AMD64=100 -D UNICODE -D _UNICODE ^ - -I "%SDK_DIR%\um" ^ - -I "%SDK_DIR%\shared" ^ - -l Advapi32 ^ - --header-class-name Advapi32 ^ - --target-package net.codecrete.usb.windows.gen.advapi32 ^ - --include-function RegQueryValueExW ^ - --include-function RegCloseKey ^ - --include-macro REG_MULTI_SZ ^ - --include-macro KEY_READ ^ - windows_headers.h - -call %JEXTRACT% --source --output ../../src/main/java ^ - -D _AMD64_ -D _M_AMD64=100 -D UNICODE -D _UNICODE ^ - -I "%SDK_DIR%\um" ^ - -I "%SDK_DIR%\shared" ^ - -l Ole32 ^ - --header-class-name Ole32 ^ - --target-package net.codecrete.usb.windows.gen.ole32 ^ - --include-function CLSIDFromString ^ - windows_headers.h diff --git a/java-does-usb/jextract/windows/windows_headers.h b/java-does-usb/jextract/windows/windows_headers.h deleted file mode 100644 index fbc95ecb..00000000 --- a/java-does-usb/jextract/windows/windows_headers.h +++ /dev/null @@ -1,6 +0,0 @@ -#include -#include -#include -#include -#include -#include diff --git a/java-does-usb/mvnw b/java-does-usb/mvnw new file mode 100755 index 00000000..19529ddf --- /dev/null +++ b/java-does-usb/mvnw @@ -0,0 +1,259 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.2 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/java-does-usb/mvnw.cmd b/java-does-usb/mvnw.cmd new file mode 100644 index 00000000..249bdf38 --- /dev/null +++ b/java-does-usb/mvnw.cmd @@ -0,0 +1,149 @@ +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.2 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' +$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" +if ($env:MAVEN_USER_HOME) { + $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" +} +$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/java-does-usb/pom.xml b/java-does-usb/pom.xml index 5b6c4ace..e8ad6aec 100644 --- a/java-does-usb/pom.xml +++ b/java-does-usb/pom.xml @@ -6,14 +6,17 @@ net.codecrete.usb java-does-usb - 0.3.0 + 1.3.1-SNAPSHOT - 19 - 19 + 25 + 25 UTF-8 + 0.8.0 + jar + Java Does USB https://github.com/manuelbl/JavaDoesUSB Access USB devices from Java without additional libraries @@ -38,42 +41,113 @@ https://github.com/manuelbl/JavaDoesUSB - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - + + net.codecrete.windows-api + windowsapi-maven-plugin + + + + windows-api + + + + CLSIDFromString + CreateFileW + CreateIoCompletionPort + CreateWindowExW + DefWindowProcW + DeviceIoControl + FormatMessageW + GetMessageW + GetModuleHandleW + GetQueuedCompletionStatus + LocalFree + RegCloseKey + RegQueryValueExW + RegisterClassExW + RegisterDeviceNotificationW + SetupDiCreateDeviceInfoList + SetupDiDeleteDeviceInterfaceData + SetupDiDestroyDeviceInfoList + SetupDiEnumDeviceInfo + SetupDiEnumDeviceInterfaces + SetupDiGetClassDevsW + SetupDiGetDeviceInterfaceDetailW + SetupDiGetDevicePropertyW + SetupDiOpenDevRegKey + SetupDiOpenDeviceInfoW + SetupDiOpenDeviceInterfaceW + WinUsb_AbortPipe + WinUsb_Free + WinUsb_GetAssociatedInterface + WinUsb_Initialize + WinUsb_ReadPipe + WinUsb_ResetPipe + WinUsb_SetCurrentAlternateSetting + WinUsb_SetPipePolicy + WinUsb_WritePipe + + + DEV_BROADCAST_DEVICEINTERFACE_W + DEV_BROADCAST_HDR + USB_DESCRIPTOR_REQUEST + USB_NODE_CONNECTION_INFORMATION_EX + + + DEV_BROADCAST_HDR_DEVICE_TYPE + FORMAT_MESSAGE_OPTIONS + GENERIC_ACCESS_RIGHTS + REG_SAM_FLAGS + SETUP_DI_PROPERTY_CHANGE_SCOPE + + + DBT_DEVICEARRIVAL + DBT_DEVICEREMOVECOMPLETE + DEVPKEY_Device_Address + DEVPKEY_Device_Children + DEVPKEY_Device_HardwareIds + DEVPKEY_Device_InstanceId + DEVPKEY_Device_Parent + DEVPKEY_Device_Service + DIREG_DEV + GUID_DEVINTERFACE_USB_DEVICE + GUID_DEVINTERFACE_USB_HUB + HWND_MESSAGE + INFINITE + IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION + IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX + STATUS_UNSUCCESSFUL + USB_REQUEST_GET_DESCRIPTOR + WM_DEVICECHANGE + + + + + org.apache.maven.plugins maven-compiler-plugin - 3.10.1 + 3.12.1 - 19 - --enable-preview - 19 - 19 + 25 + 25 + 25 org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M7 + 3.2.5 - --enable-preview --enable-native-access=ALL-UNNAMED + --enable-native-access=ALL-UNNAMED org.apache.maven.plugins maven-javadoc-plugin - 3.4.1 + 3.6.3 attach-javadocs @@ -83,15 +157,15 @@ - 19 - --enable-preview + 25 ${java.home}/bin/javadoc + net.codecrete.usb.linux.gen.*:net.codecrete.usb.macos.gen.*:windows.*:system org.apache.maven.plugins maven-gpg-plugin - 3.0.1 + 3.1.0 sign-artifacts @@ -105,7 +179,7 @@ org.apache.maven.plugins maven-source-plugin - 3.2.1 + 3.3.0 attach-sources @@ -115,30 +189,58 @@ + + org.sonatype.central + central-publishing-maven-plugin + ${sonatype-central-publishing.version} + true + + central + true + + - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.13 - true - - ossrh - https://s01.oss.sonatype.org/ - true - + net.codecrete.windows-api + windowsapi-maven-plugin + 0.8.5 + + org.jetbrains + annotations + 24.1.0 + compile + org.junit.jupiter junit-jupiter - 5.9.0 + 5.10.2 + test + + + org.assertj + assertj-core + 3.25.3 + test + + + org.tinylog + tinylog-impl + 2.7.0 + test + + + org.tinylog + jsl-tinylog + 2.7.0 test diff --git a/java-does-usb/src/main/java/module-info.java b/java-does-usb/src/main/java/module-info.java index cf4daec9..496c87ec 100644 --- a/java-does-usb/src/main/java/module-info.java +++ b/java-does-usb/src/main/java/module-info.java @@ -9,5 +9,6 @@ * Java Does USB – work with USB devices */ module net.codecrete.usb { + requires org.jetbrains.annotations; exports net.codecrete.usb; } diff --git a/java-does-usb/src/main/java/net/codecrete/usb/USB.java b/java-does-usb/src/main/java/net/codecrete/usb/USB.java deleted file mode 100644 index d39007c0..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/USB.java +++ /dev/null @@ -1,126 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// - -package net.codecrete.usb; - -import net.codecrete.usb.common.USBDeviceRegistry; -import net.codecrete.usb.linux.LinuxUSBDeviceRegistry; -import net.codecrete.usb.macos.MacosUSBDeviceRegistry; -import net.codecrete.usb.windows.WindowsUSBDeviceRegistry; - -import java.util.List; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -/** - * Provides access to USB devices. - */ -public class USB { - - private static USBDeviceRegistry createInstance() { - String osName = System.getProperty("os.name"); - String osArch = System.getProperty("os.arch"); - - USBDeviceRegistry impl; - if (osName.equals("Mac OS X") && (osArch.equals("x86_64") || osArch.equals("aarch64"))) { - impl = new MacosUSBDeviceRegistry(); - } else if (osName.startsWith("Windows") && osArch.equals("amd64")) { - impl = new WindowsUSBDeviceRegistry(); - } else if (osName.equals("Linux") && (osArch.equals("amd64") || osArch.equals("aarch64"))) { - impl = new LinuxUSBDeviceRegistry(); - } else { - throw new UnsupportedOperationException(String.format("JavaCanDoUsb is not implemented for architecture " + "%s/%s", osName, osArch)); - } - return impl; - } - - private static USBDeviceRegistry _instance = null; - - private static synchronized USBDeviceRegistry instance() { - if (_instance == null) { - _instance = createInstance(); - _instance.start(); - } - return _instance; - } - - // Private, so no instance can be created - private USB() { - } - - /** - * Gets a list of all connected USB devices. - * - *

- * Depending on the operating system, the list might or might not include - * USB hubs and USB host controllers. - *

- * - * @return list of USB devices - */ - public static List getAllDevices() { - return instance().getAllDevices(); - } - - /** - * Gets a list of connected USB devices matching the specified filter. - * - * @param filter device filter - * @return list of USB devices - */ - public static List getDevices(USBDeviceFilter filter) { - return instance().getAllDevices().stream().filter(filter::matches).collect(Collectors.toList()); - } - - /** - * Gets a list of connected USB devices matching any of the specified filters. - * - * @param filters list of device filters - * @return list of USB devices - */ - public static List getDevices(List filters) { - return instance().getAllDevices().stream().filter(dev -> USBDeviceFilter.matchesAny(dev, filters)).collect(Collectors.toList()); - } - - /** - * Gets the first connected USB device matching the specified filter. - * - * @param filter device filter - * @return USB device, or {@code null} if no device matches - */ - public static USBDevice getDevice(USBDeviceFilter filter) { - return instance().getAllDevices().stream().filter(filter::matches).findFirst().orElse(null); - } - - /** - * Gets the first connected USB device matching any of the specified filters. - * - * @param filters list of device filters - * @return USB device, or {@code null} if no device matches - */ - public static USBDevice getDevice(List filters) { - return instance().getAllDevices().stream().filter(dev -> USBDeviceFilter.matchesAny(dev, filters)).findFirst().orElse(null); - } - - /** - * Sets the handler to be called when a USB device is connected. - * - * @param handler handler function, or {@code null} to remove a previous handler - */ - public static void setOnDeviceConnected(Consumer handler) { - instance().setOnDeviceConnected(handler); - } - - /** - * Sets the handler to be called when a USB device is disconnected. - * - * @param handler handler function, or {@code null} to remove a previous handler - */ - public static void setOnDeviceDisconnected(Consumer handler) { - instance().setOnDeviceDisconnected(handler); - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/USBControlTransfer.java b/java-does-usb/src/main/java/net/codecrete/usb/USBControlTransfer.java deleted file mode 100644 index ba5678f1..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/USBControlTransfer.java +++ /dev/null @@ -1,24 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// - -package net.codecrete.usb; - -/** - * USB control transfer parameters. - *

- * See USB specification for additional information - *

- * - * @param requestType request type - * @param recipient recipient - * @param request request code - * @param value value - * @param index index - */ -public record USBControlTransfer(USBRequestType requestType, USBRecipient recipient, byte request, short value, - short index) { -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/USBDevice.java b/java-does-usb/src/main/java/net/codecrete/usb/USBDevice.java deleted file mode 100644 index 5b664945..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/USBDevice.java +++ /dev/null @@ -1,210 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// - -package net.codecrete.usb; - -import java.util.List; - -/** - * USB device. - *

- * In order to make control requests and transfer data, the device must be - * opened and an interface must be claimed. In the open state, this current - * process has exclusive access to the device. - *

- *

- * Information about the device can be queried in both the open and the - * closed state. - *

- */ -public interface USBDevice { - - /** - * USB product ID. - * - * @return product ID - */ - int productId(); - - /** - * USB vendor ID. - * - * @return vendor ID - */ - int vendorId(); - - /** - * Product name. - * - * @return product name or {@code null} if not provided by the device - */ - String product(); - - /** - * Manufacturer name - * - * @return manufacturer name or {@code null} if not provided by the device - */ - String manufacturer(); - - /** - * Serial number - *

- * Even though this is supposed to be a human-readable string, - * some devices are known to provide binary data. - *

- * - * @return serial number or {@code null} if not provided by the device - */ - String serialNumber(); - - /** - * USB device class code ({@code bDeviceClass} from device descriptor). - * - * @return class code - */ - int classCode(); - - /** - * USB device subclass code ({@code bDeviceSubClass} from device descriptor). - * - * @return subclass code - */ - int subclassCode(); - - /** - * USB device protocol ({@code bDeviceProtocol} from device descriptor). - * - * @return protocol code - */ - int protocolCode(); - - /** - * USB protocol version supported by this device. - * - * @return version - */ - Version usbVersion(); - - /** - * Device version (as declared by the manufacturer). - * - * @return version - */ - Version deviceVersion(); - - /** - * Opens the device for communication. - */ - void open(); - - /** - * Indicates if the device is open. - * - * @return {@code true} if the device is open, {@code false} if it is closed. - */ - boolean isOpen(); - - /** - * Closes the device. - */ - void close(); - - /** - * Gets the interfaces of this device. - * - * @return a list of USB interfaces - */ - List interfaces(); - - /** - * Claims the specified interface for exclusive use. - * - * @param interfaceNumber the interface number - */ - void claimInterface(int interfaceNumber); - - /** - * Releases the specified interface from exclusive use. - * - * @param interfaceNumber the interface number - */ - void releaseInterface(int interfaceNumber); - - /** - * Requests data from the control endpoint. - *

- * This method blocks until the device has responded or an error has occurred. - *

- *

- * The control transfer request is sent to endpoint 0. The transfer is expected to - * have a Data In stage. - *

- *

- * Requests with an interface or an endpoint as recipient are expected to - * have the interface and endpoint number, respectively, in the lower byte of - * {@code wIndex}. This convention is enforced by Windows. The addressed interface - * or the interface of the addressed endpoint must have been claimed. - *

- * - * @param setup control transfer setup parameters - * @param length maximum length of expected data - * @return received data. - */ - byte[] controlTransferIn(USBControlTransfer setup, int length); - - /** - * Executes a control transfer request and optionally sends data. - *

- * This method blocks until the device has acknowledge the request or an error has occurred. - *

- *

- * The control transfer request is sent to endpoint 0. The transfer is expected to either have - * no data stage or a Data Out stage. - *

- *

- * Requests with an interface or an endpoint as recipient are expected to - * have the interface and endpoint number, respectively, in the lower byte of - * {@code wIndex}. This convention is enforced by Windows. The addressed interface - * or the interface of the addressed endpoint must have been claimed. - *

- * - * @param setup control transfer setup parameters - * @param data data to send, or {@code null} if the transfer has no data stage. - */ - void controlTransferOut(USBControlTransfer setup, byte[] data); - - /** - * Sends data to this device. - *

- * This method blocks until the data has been sent or an error has occurred. - *

- *

- * This method can send data to bulk and interrupt endpoints. - *

- * - * @param endpointNumber endpoint number (in the range between 1 and 127) - * @param data data to send - */ - void transferOut(int endpointNumber, byte[] data); - - /** - * Receives data from this device. - *

- * This method blocks until at least a packet has been received or an error has occurred. - * The minimum value for {@code maxLength} is the maximum size of packets sent on the endpoint. - *

- *

- * This method can receive data from bulk and interrupt endpoints. - *

- * - * @param endpointNumber endpoint number (in the range between 1 and 127, i.e. without the direction bit) - * @param maxLength the maximum data length to receive (in number of bytes) - * @return received data - */ - byte[] transferIn(int endpointNumber, int maxLength); -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/USBDeviceFilter.java b/java-does-usb/src/main/java/net/codecrete/usb/USBDeviceFilter.java deleted file mode 100644 index b7f784d0..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/USBDeviceFilter.java +++ /dev/null @@ -1,199 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// - -package net.codecrete.usb; - -import java.util.List; - -/** - * Filter condition for matching USB devices. - *

- * In order to match this condition, all non-null properties - * of this instance must be equal to the same properties of the USB device. - *

- *

- * For a well implemented USB device, the combination of vendor ID, - * product ID and serial number is globally unique. - *

- */ -public class USBDeviceFilter { - private Integer vendorId_; - private Integer productId_; - private Integer classCode_; - private Integer subclassCode_; - private Integer protocolCode_; - private String serialNumber_; - - /** - * Creates a new instance. - */ - public USBDeviceFilter() { - } - - /** - * Creates a new instance that matches the specified vendor and product ID. - * - * @param vendorId vendor ID - * @param productId product ID - */ - public USBDeviceFilter(int vendorId, int productId) { - vendorId_ = vendorId; - productId_ = productId; - } - - /** - * Creates a new instance that matches the specified vendor ID, product ID and serial number. - * - * @param vendorId vendor ID - * @param productId product ID - * @param serialNumber serial number - */ - public USBDeviceFilter(int vendorId, int productId, String serialNumber) { - vendorId_ = vendorId; - productId_ = productId; - serialNumber_ = serialNumber; - } - - /** - * Gets the USB vendor ID. - * - * @return vendor ID, or {@code null} if the vendor ID is not relevant for matching - */ - public Integer vendorId() { - return vendorId_; - } - - /** - * Sets the USB vendor ID. - * - * @param vendorId vendor ID, or {@code null} if the vendor ID is not relevant for matching - */ - public void setVendorId(Integer vendorId) { - vendorId_ = vendorId; - } - - /** - * Gets the USB product ID. - * - * @return product ID, or {@code null} if the product ID is not relevant for matching - */ - public Integer productId() { - return productId_; - } - - /** - * Sets the USB product ID. - * - * @param productId product ID, or {@code null} if the product ID is not relevant for matching - */ - public void setProductId(Integer productId) { - productId_ = productId; - } - - /** - * Gets the USB device class code. - * - * @return class code, or {@code null} if the class code is not relevant for matching - */ - public Integer classCode() { - return classCode_; - } - - /** - * Sets the USB device class code. - * - * @param classCode class code, or {@code null} if the class code is not relevant for matching - */ - public void setClassCode(Integer classCode) { - classCode_ = classCode; - } - - /** - * Gets the USB device subclass code. - * - * @return subclass code, or {@code null} if the subclass code is not relevant for matching - */ - public Integer subclassCode() { - return subclassCode_; - } - - /** - * Sets the USB device subclass code. - * - * @param subclassCode subclass code, or {@code null} if the subclass code is not relevant for matching - */ - public void setSubclassCode(Integer subclassCode) { - subclassCode_ = subclassCode; - } - - /** - * Gets the USB device protocol code. - * - * @return protocol code, or {@code null} if the protocol code is not relevant for matching - */ - public Integer protocolCode() { - return protocolCode_; - } - - /** - * Sets the USB device protocol code. - * - * @param protocolCode protocol code, or {@code null} if the protocol code is not relevant for matching - */ - public void setProtocolCode_(Integer protocolCode) { - protocolCode_ = protocolCode; - } - - /** - * Gets the device serial number. - * - * @return serial number, or {@code null} if the serial number is not relevant for matching - */ - public String serialNumber() { - return serialNumber_; - } - - /** - * Sets the device serial number. - * - * @param serialNumber serial number, or {@code null} if the serial number is not relevant for matching - */ - public void setSerialNumber(String serialNumber) { - serialNumber_ = serialNumber; - } - - /** - * Tests if the specified USB device matches this filter. - * - * @param device USB device - * @return {@code true} if it matches, {@code false} otherwise - */ - public boolean matches(USBDevice device) { - if (vendorId_ != null && device.vendorId() != vendorId_) - return false; - if (productId_ != null && device.productId() != productId_) - return false; - if (serialNumber_ != null && !serialNumber_.equals(device.serialNumber())) - return false; - if (classCode_ != null && device.classCode() != classCode_) - return false; - if (subclassCode_ != null && device.subclassCode() != subclassCode_) - return false; - return protocolCode_ == null || device.protocolCode() == protocolCode_; - } - - /** - * Test if the USB devices matches any of the filter conditions. - * - * @param device the USB device - * @param filters a list of filter conditions - * @return {@code true} if it matches, {@code false} otherwise - */ - public static boolean matchesAny(USBDevice device, List filters) { - return filters.stream().anyMatch(filter -> filter.matches(device)); - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/Usb.java b/java-does-usb/src/main/java/net/codecrete/usb/Usb.java new file mode 100644 index 00000000..b17fc604 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/Usb.java @@ -0,0 +1,153 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb; + +import net.codecrete.usb.common.UsbDeviceRegistry; +import net.codecrete.usb.linux.LinuxUsbDeviceRegistry; +import net.codecrete.usb.macos.MacosUsbDeviceRegistry; +import net.codecrete.usb.windows.WindowsUsbDeviceRegistry; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; + +/** + * Provides access to USB devices. + */ +public class Usb { + + @SuppressWarnings("java:S1192") + private static UsbDeviceRegistry createInstance() { + var osName = System.getProperty("os.name"); + var osArch = System.getProperty("os.arch"); + + UsbDeviceRegistry impl; + if (osName.equals("Mac OS X") && (osArch.equals("x86_64") || osArch.equals("aarch64"))) { + impl = new MacosUsbDeviceRegistry(); + } else if (osName.startsWith("Windows") && (osArch.equals("amd64") || osArch.equals("aarch64"))) { + impl = new WindowsUsbDeviceRegistry(); + } else if (osName.equals("Linux") && (osArch.equals("amd64") || osArch.equals("aarch64"))) { + impl = new LinuxUsbDeviceRegistry(); + } else { + throw new UnsupportedOperationException(String.format( + "The \"Java Does USB\" library has no implementation for JRE/JDK %s/%s", + osName, osArch)); + } + return impl; + } + + private static UsbDeviceRegistry singletonInstance = null; + + private static synchronized UsbDeviceRegistry instance() { + if (singletonInstance == null) { + singletonInstance = createInstance(); + singletonInstance.start(); + } + return singletonInstance; + } + + // Private, so no instance can be created + private Usb() { + } + + /** + * Gets a list of all connected USB devices. + * + *

+ * Depending on the operating system, the list might or might not include + * USB hubs and USB host controllers. + *

+ * + * @return list of USB devices + */ + public static @NotNull @Unmodifiable Collection getDevices() { + return Collections.unmodifiableCollection(instance().getAllDevices()); + } + + /** + * Gets a list of connected USB devices matching the specified predicate. + * + * @param predicate device predicate + * @return list of USB devices + */ + public static @NotNull @Unmodifiable List findDevices(@NotNull UsbDevicePredicate predicate) { + return instance().getAllDevices().stream().filter(predicate::matches).toList(); + } + + /** + * Gets the first connected USB device matching the specified predicate. + * + * @param predicate device predicate + * @return optional USB device + */ + public static Optional findDevice(@NotNull UsbDevicePredicate predicate) { + return instance().getAllDevices().stream().filter(predicate::matches).findFirst(); + } + + /** + * Gets the first connected USB device with the specified vendor and product ID. + * + * @param vendorId vendor ID + * @param productId product ID + * @return optional USB device + */ + public static Optional findDevice(int vendorId, int productId) { + return findDevice(device -> device.getVendorId() == vendorId && device.getProductId() == productId); + } + + /** + * Sets the handler to be called when a USB device is connected. + *

+ * The handler is called from a background thread. + *

+ *

+ * The handler should not execute any time-consuming operations but rather return quickly. + * While the handler is being executed, maintaining the list of connected devices is paused, + * methods of this class (such as {@link #getDevices()}) will possibly work with an outdated list + * of connected devices and handlers for connect and disconnect events will not be called. + *

+ * + * @param handler handler function, or {@code null} to remove a previous handler + */ + public static void setOnDeviceConnected(@Nullable Consumer handler) { + instance().setOnDeviceConnected(handler); + } + + /** + * Sets the handler to be called when a USB device is disconnected. + *

+ * The handler is called from a background thread. + *

+ *

+ * When the handler is called, the {@link UsbDevice} instance has already been closed. + * Descriptive information (such as vendor and product ID, serial number, interfaces, endpoints) + * can still be accessed. + *

+ *

+ * If the application was communicating with the device when it was disconnected, it will also receive + * an error for those operations. Due to the concurrency of the USB stack, there is no particular order + * for the disconnect event and the transmission errors. + *

+ *

+ * The handler should not execute any time-consuming operations but rather return quickly. + * While the handler is being executed, maintaining the list of connected devices is paused, + * methods of this class (such as {@link #getDevices()}) will possibly work with an outdated list + * of connected devices and handlers for connect and disconnect events will not be called. + *

+ * + * @param handler handler function, or {@code null} to remove a previous handler + */ + public static void setOnDeviceDisconnected(@Nullable Consumer handler) { + instance().setOnDeviceDisconnected(handler); + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/USBAlternateInterface.java b/java-does-usb/src/main/java/net/codecrete/usb/UsbAlternateInterface.java similarity index 59% rename from java-does-usb/src/main/java/net/codecrete/usb/USBAlternateInterface.java rename to java-does-usb/src/main/java/net/codecrete/usb/UsbAlternateInterface.java index 40041360..835f68aa 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/USBAlternateInterface.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/UsbAlternateInterface.java @@ -7,6 +7,9 @@ package net.codecrete.usb; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Unmodifiable; + import java.util.List; /** @@ -15,16 +18,17 @@ * Instances of this class describe an alternate setting of a USB interface. *

*/ -public interface USBAlternateInterface { +public interface UsbAlternateInterface { /** * Gets the alternate setting number. *

* It is equal to the {@code bAlternateSetting} field of the interface descriptor. + *

* * @return the alternate setting number */ - int number(); + int getNumber(); /** * Gets the interface class. @@ -34,7 +38,7 @@ public interface USBAlternateInterface { * * @return the interface class */ - int classCode(); + int getClassCode(); /** * Gets the interface subclass. @@ -44,7 +48,7 @@ public interface USBAlternateInterface { * * @return the interface subclass */ - int subclassCode(); + int getSubclassCode(); /** * Gets the interface protocol. @@ -54,16 +58,32 @@ public interface USBAlternateInterface { * * @return the interface protocol */ - int protocolCode(); + int getProtocolCode(); /** - * Gets the endpoints of this alternate interface settings. + * Gets the endpoints of this alternate interface setting. *

* The endpoint list does not include endpoint 0, which * is always available and reserved for control transfers. *

+ *

+ * The returned list is sorted by endpoint number. + *

* * @return a list of endpoints. */ - List endpoints(); + @NotNull + @Unmodifiable + List getEndpoints(); + + /** + * Gets the endpoint with the specified number and direction. + * + * @param endpointNumber endpoint number (in the range between 1 and 127, without the direction bit) + * @param direction endpoint direction + * @return the endpoint + * @exception UsbException if the endpoint does not exist + */ + @NotNull UsbEndpoint getEndpoint(int endpointNumber, UsbDirection direction); + } diff --git a/java-does-usb/src/main/java/net/codecrete/usb/UsbControlTransfer.java b/java-does-usb/src/main/java/net/codecrete/usb/UsbControlTransfer.java new file mode 100644 index 00000000..dbc2e042 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/UsbControlTransfer.java @@ -0,0 +1,34 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb; + +/** + * USB control transfer parameters. + *

+ * See description of "Setup Data" in the chapter "USB Device Requests" of the + * USB specification for additional information. + *

+ *

+ * For control requests directed to an interface or endpoint, + * the lower byte of {@code index} contains the interface or endpoint number. + *

+ * + * @param requestType request type (bits 5 and 6 of {@code bmRequestType}) + * @param recipient recipient (bits 0–4 of {@code bmRequestType}) + * @param request request code (value between 0 and 255, called {@code bRequest} in the USB specification) + * @param value value (value between 0 and 65535, called {@code wValue} in the USB specification) + * @param index index (value between 0 and 65535, called {@code wIndex} in the USB specification) + */ +public record UsbControlTransfer( + UsbRequestType requestType, + UsbRecipient recipient, + int request, + int value, + int index +) { +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/UsbDevice.java b/java-does-usb/src/main/java/net/codecrete/usb/UsbDevice.java new file mode 100644 index 00000000..786956ba --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/UsbDevice.java @@ -0,0 +1,490 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Unmodifiable; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; + +/** + * USB device. + *

+ * In order to make control requests and transfer data, the device must be + * opened and an interface must be claimed. In the open state, the current + * process has exclusive access to the device. + *

+ *

+ * Information about the device can be queried in both the open and the + * closed state. + *

+ */ +public interface UsbDevice { + + /** + * USB product ID. + * + * @return product ID + */ + int getProductId(); + + /** + * USB vendor ID. + * + * @return vendor ID + */ + int getVendorId(); + + /** + * Product name. + * + * @return product name or {@code null} if not provided by the device + */ + String getProduct(); + + /** + * Manufacturer name. + * + * @return manufacturer name or {@code null} if not provided by the device + */ + String getManufacturer(); + + /** + * Serial number. + *

+ * Even though this is supposed to be a human-readable string, + * some devices are known to provide binary data. + *

+ * + * @return serial number or {@code null} if not provided by the device + */ + String getSerialNumber(); + + /** + * USB device class code ({@code bDeviceClass} from device descriptor). + * + * @return class code + */ + int getClassCode(); + + /** + * USB device subclass code ({@code bDeviceSubClass} from device descriptor). + * + * @return subclass code + */ + int getSubclassCode(); + + /** + * USB device protocol ({@code bDeviceProtocol} from device descriptor). + * + * @return protocol code + */ + int getProtocolCode(); + + /** + * USB protocol version supported by this device. + * + * @return version + */ + @NotNull Version getUsbVersion(); + + /** + * Device version (as declared by the manufacturer). + * + * @return version + */ + @NotNull Version getDeviceVersion(); + + /** + * Detaches the standard operating-system drivers of this device. + *

+ * By detaching the standard drivers, the operating system releases the exclusive access to the device + * and/or some or all of the device's interfaces. This allows the application to open the device and claim + * interfaces. It is relevant for device and interfaces implementing standard USB classes, such as HID, CDC + * and mass storage. + *

+ *

+ * This method should be called before the device is opened. After the device has been closed, + * {@link #attachStandardDrivers()} should be called to restore the previous state. + *

+ *

+ * On macOS, all device drivers are immediately detached from the device. To execute it, the application must + * be run as root. Without root privileges, the method does nothing. + *

+ *

+ * On Linux, this method changes the behavior of {@link #claimInterface(int)} for this device. The standard drivers + * will be detached interface by interface when the interface is claimed. + *

+ *

+ * On Windows, this method does nothing. It is not possible to temporarily change the drivers. + *

+ */ + void detachStandardDrivers(); + + /** + * Reattaches the standard operating-system drivers to this device. + *

+ * By attaching the standard drivers, the operating system claims the device and/or its interfaces if they + * implement standard USB classes, such as HID, CDC and mass storage. It is used to restore the state before + * calling {@link #detachStandardDrivers()}. + *

+ *

+ * This method should be called after the device has been closed. + *

+ *

+ * On macOS, the application must be run as root. Without root privileges, the method does nothing. + *

+ *

+ * On Linux, this method changes the behavior of {@link #claimInterface(int)}. Standard drivers will no longer be + * detached when the interface is claimed. Standard drivers are automatically reattached when the interfaces + * are released, at the latest when the device is closed. + *

+ *

+ * On Windows, this method does nothing. + *

+ */ + void attachStandardDrivers(); + + /** + * Indicates if the device is connected. + *

+ * When a {@link UsbDevice} instance is initially returned by {@link Usb#getDevices()} and related methods, + * it is connected. When the user unplugs the device, the application can still hold on to instance of + * {@link UsbDevice} even though the actual USB device is gone. This method can be used to check if the + * device is still connected. + *

+ * @return {@code true} if the device is connected, {@code false} if it is no longer connected + */ + boolean isConnected(); + + /** + * Opens the device for communication. + */ + void open(); + + /** + * Indicates if the device is open. + * + * @return {@code true} if the device is open, {@code false} if it is closed. + */ + boolean isOpened(); + + /** + * Closes the device. + */ + void close(); + + /** + * Gets the interfaces of this device. + *

+ * The returned list is sorted by interface number. + *

+ * + * @return a list of USB interfaces + */ + @NotNull + @Unmodifiable + List getInterfaces(); + + /** + * Gets the interface with the specified number. + * + * @param interfaceNumber the interface number + * @return the interface + * @exception UsbException if the interface does not exist + */ + @NotNull UsbInterface getInterface(int interfaceNumber); + + /** + * Gets the endpoint with the specified number. + * + * @param direction the endpoint direction + * @param endpointNumber the endpoint number (between 1 and 127) + * @return the endpoint + * @exception UsbException if the endpoint does not exist + */ + @NotNull UsbEndpoint getEndpoint(UsbDirection direction, int endpointNumber); + + /** + * Claims the specified interface for exclusive use. + * + * @param interfaceNumber the interface number + */ + void claimInterface(int interfaceNumber); + + /** + * Selects the alternate settings for the specified interface. + *

+ * The device must be open and the interface must be claimed for exclusive access. + *

+ * + * @param interfaceNumber interface number + * @param alternateNumber alternate setting number + */ + void selectAlternateSetting(int interfaceNumber, int alternateNumber); + + /** + * Releases the specified interface from exclusive use. + * + * @param interfaceNumber the interface number + */ + void releaseInterface(int interfaceNumber); + + /** + * Requests data from the control endpoint. + *

+ * This method blocks until the device has responded or an error has occurred. + *

+ *

+ * The control transfer request is sent to endpoint 0. The transfer is expected to + * have a Data In stage. + *

+ *

+ * Requests with an interface or an endpoint as recipient are expected to + * have the interface and endpoint number, respectively, in the lower byte of + * {@code wIndex}. This convention is enforced by Windows. The addressed interface + * or the interface of the addressed endpoint must have been claimed. + *

+ * + * @param transfer control transfer setup parameters + * @param length maximum length of expected data + * @return received data. + */ + byte @NotNull [] controlTransferIn(@NotNull UsbControlTransfer transfer, int length); + + /** + * Executes a control transfer request and optionally sends data. + *

+ * This method blocks until the device has acknowledged the request or an error has occurred. + *

+ *

+ * The control transfer request is sent to endpoint 0. The transfer is expected to either have + * no data stage or a Data Out stage. + *

+ *

+ * Requests with an interface or an endpoint as recipient are expected to + * have the interface and endpoint number, respectively, in the lower byte of + * {@code wIndex}. This convention is enforced by Windows. The addressed interface + * or the interface of the addressed endpoint must have been claimed. + *

+ * + * @param transfer control transfer setup parameters + * @param data data to send, or {@code null} if the transfer has no data stage. + */ + void controlTransferOut(@NotNull UsbControlTransfer transfer, byte[] data); + + /** + * Sends data to this device. + *

+ * This method blocks until the data has been sent or an error has occurred. + *

+ *

+ * This method can send data to bulk and interrupt endpoints. + *

+ *

+ * If the sent data length is a multiple of the packet size, it is often + * required to send an additional zero-length packet (ZLP) for the device + * to actually process the data. This method will not do it automatically. + *

+ * + * @param endpointNumber endpoint number (in the range between 1 and 127) + * @param data data to send + */ + void transferOut(int endpointNumber, byte @NotNull [] data); + + /** + * Sends data to this device. + *

+ * This method blocks until the data has been sent, the timeout period has expired + * or an error has occurred. If the timeout expires, a {@link UsbTimeoutException} is thrown. + *

+ *

+ * This method can send data to bulk and interrupt endpoints. + *

+ *

+ * If the sent data length is a multiple of the packet size, it is often + * required to send an additional zero-length packet (ZLP) for the device + * to actually process the data. This method will not do it automatically. + *

+ * + * @param endpointNumber the endpoint number (in the range between 1 and 127) + * @param data data to send + * @param timeout the timeout period, in milliseconds (0 for no timeout) + */ + void transferOut(int endpointNumber, byte @NotNull [] data, int timeout); + + /** + * Sends data to this device. + *

+ * This method blocks until the data has been sent, the timeout period has expired + * or an error has occurred. If the timeout expires, a {@link UsbTimeoutException} is thrown. + *

+ *

+ * This method can send data to bulk and interrupt endpoints. + *

+ *

+ * If the sent data length is a multiple of the packet size, it is often + * required to send an additional zero-length packet (ZLP) for the device + * to actually process the data. This method will not do it automatically. + *

+ * + * @param endpointNumber the endpoint number (in the range between 1 and 127) + * @param data buffer containing data to send + * @param offset offset of the first byte to send + * @param length number of bytes to send + * @param timeout the timeout period, in milliseconds (0 for no timeout) + */ + void transferOut(int endpointNumber, byte @NotNull [] data, int offset, int length, int timeout); + + /** + * Receives data from this device. + *

+ * This method blocks until at least a packet has been received or an error has occurred. + *

+ *

+ * The returned data is the payload of a packet. It can have a length of 0 if the USB device + * sends zero-length packets to indicate the end of a data unit. + *

+ *

+ * This method can receive data from bulk and interrupt endpoints. + *

+ * + * @param endpointNumber endpoint number (in the range between 1 and 127, i.e. without the direction bit) + * @return received data + */ + byte @NotNull [] transferIn(int endpointNumber); + + /** + * Receives data from this device. + *

+ * This method blocks until at least a packet has been received, the timeout period has expired + * or an error has occurred. If the timeout expired, a {@link UsbTimeoutException} is thrown. + *

+ *

+ * The returned data is the payload of a packet. It can have a length of 0 if the USB device + * sends zero-length packets to indicate the end of a data unit. + *

+ *

+ * This method can receive data from bulk and interrupt endpoints. + *

+ * + * @param endpointNumber the endpoint number (in the range between 1 and 127, i.e. without the direction bit) + * @param timeout the timeout period, in milliseconds (0 for no timeout) + * @return received data + */ + byte @NotNull [] transferIn(int endpointNumber, int timeout); + + /** + * Opens a new output stream to send data to a bulk endpoint. + *

+ * All data written to this output stream is sent to the specified bulk endpoint. + * Buffering and concurrent IO requests are used to achieve a high throughput. + *

+ *

+ * The stream will insert zero-length packets if {@link OutputStream#flush()} is called + * and the last packet size was equal to maximum packet size of the endpoint. + *

+ *

+ * If {@link #transferOut(int, byte[])} and an output stream or multiple output streams + * are used concurrently for the same endpoint, the behavior is unpredictable. + *

+ * + * @param endpointNumber bulk endpoint number (in the range between 1 and 127) + * @param bufferSize approximate buffer size (in bytes) + * @return the new output stream + */ + @NotNull OutputStream openOutputStream(int endpointNumber, int bufferSize); + + /** + * Opens a new output stream to send data to a bulk endpoint. + *

+ * The buffer is configured with minimal size. In all other aspects, this method + * works like {@link #openOutputStream(int, int)}. + *

+ * @param endpointNumber bulk endpoint number (in the range between 1 and 127) + * @return the new output stream + */ + default @NotNull OutputStream openOutputStream(int endpointNumber) { + return openOutputStream(endpointNumber, 1); + } + + /** + * Opens a new input stream to receive data from a bulk endpoint. + *

+ * All data received from the specified bulk endpoint can be read using this input stream. + * Buffering and concurrent IO requests are used to achieve a high throughput. + *

+ *

+ * If the buffers contain data when the stream is closed, this data will be discarded. + * If {@link #transferIn(int)} and an input stream or multiple input streams + * are used concurrently for the same endpoint, the behavior is unpredictable. + *

+ * + * @param endpointNumber bulk endpoint number (in the range between 1 and 127, i.e. without the direction bit) + * @param bufferSize approximate buffer size (in bytes) + * @return the new input stream + */ + @NotNull InputStream openInputStream(int endpointNumber, int bufferSize); + + /** + * Opens a new input stream to receive data from a bulk endpoint. + *

+ * The buffer is configured with minimal size. In all other aspects, this method + * works like {@link #openInputStream(int, int)}. + *

+ * + * @param endpointNumber bulk endpoint number (in the range between 1 and 127, i.e. without the direction bit) + * @return the new input stream + */ + default @NotNull InputStream openInputStream(int endpointNumber) { + return openInputStream(endpointNumber, 1); + } + + /** + * Aborts all transfers on an endpoint. + *

+ * This operation is not valid on the control endpoint 0. + *

+ * + * @param direction endpoint direction + * @param endpointNumber endpoint number (in the range between 1 and 127) + */ + void abortTransfers(UsbDirection direction, int endpointNumber); + + /** + * Clears an endpoint's halt condition. + *

+ * An endpoint is halted (aka stalled) if an error occurs in the communication. Before the + * communication can resume, the halt condition must be cleared. A halt condition can exist + * in a single direction only. + *

+ *

+ * Control endpoint 0 will never be halted. + *

+ * + * @param direction endpoint direction + * @param endpointNumber endpoint number (in the range between 1 and 127) + */ + void clearHalt(UsbDirection direction, int endpointNumber); + + /** + * Gets the device descriptor. + * + * @return the device descriptor (as a byte array) + */ + byte @NotNull [] getDeviceDescriptor(); + + /** + * Gets the configuration descriptor. + * + * @return the configuration descriptor (as a byte array) + */ + byte @NotNull [] getConfigurationDescriptor(); +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/UsbDevicePredicate.java b/java-does-usb/src/main/java/net/codecrete/usb/UsbDevicePredicate.java new file mode 100644 index 00000000..687db9ed --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/UsbDevicePredicate.java @@ -0,0 +1,40 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * Represents a predicate (boolean-valued function) of one argument, evaluated for a given USB device. + *

+ * This is a functional interface whose functional method is {@link #matches(UsbDevice)}. + *

+ */ +@FunctionalInterface +public interface UsbDevicePredicate { + /** + * Evaluates this predicate on the given USB device. + * + * @param device the USB device + * @return {@code true} if the device matches the predicate, otherwise {@code false} + */ + boolean matches(@NotNull UsbDevice device); + + /** + * Tests whether the USB device matches any of the filter conditions. + * + * @param device the USB device + * @param predicates a list of filter predicates + * @return {@code true} if it matches, {@code false} otherwise + */ + static boolean matchesAny(@NotNull UsbDevice device, @NotNull List predicates) { + return predicates.stream().anyMatch(predicate -> predicate.matches(device)); + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/USBDirection.java b/java-does-usb/src/main/java/net/codecrete/usb/UsbDirection.java similarity index 92% rename from java-does-usb/src/main/java/net/codecrete/usb/USBDirection.java rename to java-does-usb/src/main/java/net/codecrete/usb/UsbDirection.java index 8e2e7981..8e561dc4 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/USBDirection.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/UsbDirection.java @@ -10,7 +10,7 @@ /** * USB endpoint data direction enumeration. */ -public enum USBDirection { +public enum UsbDirection { /** * Direction OUT (host to device) */ diff --git a/java-does-usb/src/main/java/net/codecrete/usb/USBEndpoint.java b/java-does-usb/src/main/java/net/codecrete/usb/UsbEndpoint.java similarity index 87% rename from java-does-usb/src/main/java/net/codecrete/usb/USBEndpoint.java rename to java-does-usb/src/main/java/net/codecrete/usb/UsbEndpoint.java index 8ccd271d..1cebc06b 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/USBEndpoint.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/UsbEndpoint.java @@ -13,7 +13,7 @@ * Instances of this class describe a USB endpoint. *

*/ -public interface USBEndpoint { +public interface UsbEndpoint { /** * Gets the USB endpoint number. @@ -24,12 +24,12 @@ public interface USBEndpoint { *

*

* Use this number when calling any of the transfer methods of a - * {@link USBDevice} instace. + * {@link UsbDevice} instance. *

* * @return the endpoint number */ - int number(); + int getNumber(); /** * Gets the direction of the endpoint. @@ -40,14 +40,14 @@ public interface USBEndpoint { * * @return the direction */ - USBDirection direction(); + UsbDirection getDirection(); /** * Gets the USB endpoint transfer type. * * @return the transfer type */ - USBTransferType transferType(); + UsbTransferType getTransferType(); /** * Gets the packet size. @@ -58,5 +58,5 @@ public interface USBEndpoint { * * @return the packet size, in bytes. */ - int packetSize(); + int getPacketSize(); } diff --git a/java-does-usb/src/main/java/net/codecrete/usb/USBException.java b/java-does-usb/src/main/java/net/codecrete/usb/UsbException.java similarity index 66% rename from java-does-usb/src/main/java/net/codecrete/usb/USBException.java rename to java-does-usb/src/main/java/net/codecrete/usb/UsbException.java index 31827e62..e0c62acb 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/USBException.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/UsbException.java @@ -7,23 +7,27 @@ package net.codecrete.usb; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + /** * USB exception, thrown if an operation with USB devices fails. */ -public class USBException extends RuntimeException { +public class UsbException extends RuntimeException { /** * Error code. */ - private int errorCode_ = -1; + private final int code; /** * Creates a new instance with a message. * * @param message the message */ - public USBException(String message) { + public UsbException(@NotNull String message) { super(message); + code = -1; } /** @@ -32,9 +36,9 @@ public USBException(String message) { * @param message the message * @param errorCode the error code */ - public USBException(String message, int errorCode) { + public UsbException(@NotNull String message, int errorCode) { super(message + " (error code: " + errorCode + ")"); - errorCode_ = errorCode; + code = errorCode; } /** @@ -43,8 +47,9 @@ public USBException(String message, int errorCode) { * @param message the message * @param cause the causal exception */ - public USBException(String message, Throwable cause) { + public UsbException(@NotNull String message, @Nullable Throwable cause) { super(message, cause); + code = -1; } /** @@ -52,7 +57,7 @@ public USBException(String message, Throwable cause) { * * @return the error code */ - public int errorCode() { - return errorCode_; + public int getErrorCode() { + return code; } } diff --git a/java-does-usb/src/main/java/net/codecrete/usb/USBInterface.java b/java-does-usb/src/main/java/net/codecrete/usb/UsbInterface.java similarity index 50% rename from java-does-usb/src/main/java/net/codecrete/usb/USBInterface.java rename to java-does-usb/src/main/java/net/codecrete/usb/UsbInterface.java index bfb5f68a..67fde72f 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/USBInterface.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/UsbInterface.java @@ -7,6 +7,9 @@ package net.codecrete.usb; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Unmodifiable; + import java.util.List; /** @@ -15,20 +18,20 @@ * Instances of this class describe an interface of a USB device. *

*/ -public interface USBInterface { +public interface UsbInterface { /** * Gets the interface number. *

* It is equal to the {@code bInterfaceNumber} field of the interface descriptor. - *

+ *

* * @return the interface number */ - int number(); + int getNumber(); /** - * Indicates if this interface is currently claimed for exclusive access. + * Indicates if this interface has been claimed by this program for exclusive access. * * @return {@code true} if it is claimed, {@code false} otherwise. */ @@ -37,17 +40,31 @@ public interface USBInterface { /** * Gets the currently selected alternate interface setting. *

- * Initially, the alternate settings with number 0 is selected. + * Initially, the alternate setting with number 0 is selected. *

* * @return the alternate interface setting. */ - USBAlternateInterface alternate(); + @NotNull UsbAlternateInterface getCurrentAlternate(); + + /** + * Gets the alternate interface setting with the specified number. + * + * @param alternateNumber alternate setting number + * @return alternate interface setting + * @throws UsbException if the alternate setting does not exist + */ + @NotNull UsbAlternateInterface getAlternate(int alternateNumber); /** * Gets all alternate settings of this interface. + *

+ * The returned list is sorted by alternate setting number. + *

* * @return a list of the alternate settings */ - List alternates(); + @NotNull + @Unmodifiable + List getAlternates(); } diff --git a/java-does-usb/src/main/java/net/codecrete/usb/USBRecipient.java b/java-does-usb/src/main/java/net/codecrete/usb/UsbRecipient.java similarity index 93% rename from java-does-usb/src/main/java/net/codecrete/usb/USBRecipient.java rename to java-does-usb/src/main/java/net/codecrete/usb/UsbRecipient.java index 250238a2..756027a2 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/USBRecipient.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/UsbRecipient.java @@ -10,7 +10,7 @@ /** * USB control transfer recipient enumeration. */ -public enum USBRecipient { +public enum UsbRecipient { /** * USB device */ diff --git a/java-does-usb/src/main/java/net/codecrete/usb/USBRequestType.java b/java-does-usb/src/main/java/net/codecrete/usb/UsbRequestType.java similarity index 93% rename from java-does-usb/src/main/java/net/codecrete/usb/USBRequestType.java rename to java-does-usb/src/main/java/net/codecrete/usb/UsbRequestType.java index 05ede7e6..08dec0d6 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/USBRequestType.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/UsbRequestType.java @@ -10,7 +10,7 @@ /** * USB control transfer request type enumeration. */ -public enum USBRequestType { +public enum UsbRequestType { /** * Standard request type */ diff --git a/java-does-usb/src/main/java/net/codecrete/usb/UsbStallException.java b/java-does-usb/src/main/java/net/codecrete/usb/UsbStallException.java new file mode 100644 index 00000000..e60da595 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/UsbStallException.java @@ -0,0 +1,32 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb; + +import org.jetbrains.annotations.NotNull; + +/** + * Exception thrown if communication on a USB endpoint fails. + *

+ * If a USB endpoint stalls, it is halted and the halt condition must be cleared + * using {@link UsbDevice#clearHalt(UsbDirection, int)} before communication can resume. + *

+ *

+ * If the control endpoint 0 stalls, this exception is thrown but the endpoint is not halted. + *

+ */ +public class UsbStallException extends UsbException { + + /** + * Creates a new instance with a message. + * + * @param message the message + */ + public UsbStallException(@NotNull String message) { + super(message); + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/UsbTimeoutException.java b/java-does-usb/src/main/java/net/codecrete/usb/UsbTimeoutException.java new file mode 100644 index 00000000..28ae2f72 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/UsbTimeoutException.java @@ -0,0 +1,25 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb; + +import org.jetbrains.annotations.NotNull; + +/** + * Exception thrown if a USB operation times out. + */ +public class UsbTimeoutException extends UsbException { + + /** + * Creates a new instance with a message. + * + * @param message the message + */ + public UsbTimeoutException(@NotNull String message) { + super(message); + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/USBTransferType.java b/java-does-usb/src/main/java/net/codecrete/usb/UsbTransferType.java similarity index 82% rename from java-does-usb/src/main/java/net/codecrete/usb/USBTransferType.java rename to java-does-usb/src/main/java/net/codecrete/usb/UsbTransferType.java index 3acdc2c1..f08a2e40 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/USBTransferType.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/UsbTransferType.java @@ -10,7 +10,11 @@ /** * USB endpoint transfer type enumeration. */ -public enum USBTransferType { +public enum UsbTransferType { + /** + * Control transfer + */ + CONTROL, /** * Bulk transfer */ diff --git a/java-does-usb/src/main/java/net/codecrete/usb/Version.java b/java-does-usb/src/main/java/net/codecrete/usb/Version.java index dc4a314d..90b507f5 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/Version.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/Version.java @@ -14,25 +14,51 @@ public final class Version { private final int bcdVersion; + /** + * Creates a new instance. + *

+ * {@code bcdVersion} contains the version: the high byte is the major + * version. The low byte is split into two nibbles (4 bits), the high one + * is the minor version, the low one is the subminor version. As an example, + * 0x0321 represents the version 3.2.1. + *

+ * + * @param bcdVersion version, encoded as described above + */ public Version(int bcdVersion) { this.bcdVersion = bcdVersion; } - public int major() { + /** + * Major version + * + * @return major version + */ + public int getMajor() { return bcdVersion >> 8; } - public int minor() { + /** + * Minor version + * + * @return minor version + */ + public int getMinor() { return (bcdVersion >> 4) & 0x0f; } - public int subminor() { + /** + * Subminor version + * + * @return subminor version + */ + public int getSubminor() { return bcdVersion & 0x0f; } @Override public String toString() { - return String.format("%d.%d.%d", major(), minor(), subminor()); + return String.format("%d.%d.%d", getMajor(), getMinor(), getSubminor()); } @Override @@ -41,7 +67,7 @@ public boolean equals(Object o) { return true; if (o == null || getClass() != o.getClass()) return false; - Version version = (Version) o; + var version = (Version) o; return bcdVersion == version.bcdVersion; } diff --git a/java-does-usb/src/main/java/net/codecrete/usb/common/CompositeFunction.java b/java-does-usb/src/main/java/net/codecrete/usb/common/CompositeFunction.java new file mode 100644 index 00000000..b6e751a4 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/common/CompositeFunction.java @@ -0,0 +1,84 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.common; + +/** + * Describes a function of a composite USB device. + *

+ * A composite USB device can have multiple functions, e.g. a mass + * storage function and a virtual serial port function. Each function + * will appear as a separate device in Window. + *

+ *

+ * A function consists of one or more interfaces. Functions with + * multiple interfaces must have consecutive interface numbers. The + * interfaces after the first one are called associated interfaces. + *

+ */ +public class CompositeFunction { + private final int firstIntfNumber; + private final int interfaceCount; + private final int functionCode; + private final int functionSubclass; + private final int functionProtocol; + + /** + * Creates a new instance. + * + * @param firstInterfaceNumber the number of the first interface + * @param numInterfaces the number of interfaces + * @param classCode the function class + * @param subclassCode the function subclass + * @param protocolCode the function protocol + */ + public CompositeFunction(int firstInterfaceNumber, int numInterfaces, int classCode, int subclassCode, + int protocolCode) { + firstIntfNumber = firstInterfaceNumber; + interfaceCount = numInterfaces; + functionCode = classCode; + functionSubclass = subclassCode; + functionProtocol = protocolCode; + } + + /** + * Gets the number of the first interface contained in this function. + * @return the interface number + */ + public int firstInterfaceNumber() { + return firstIntfNumber; + } + + /** + * Gets the number of interfaces contained in this function. + * @return the number of interfaces + */ + public int numInterfaces() { + return interfaceCount; + } + + /** + * Indicates if this function contains the specified interface. + * @param interfaceNumber the interface number + * @return {@code true} if it is contained, {@code false} otherwise + */ + public boolean containsInterface(int interfaceNumber) { + return interfaceNumber >= firstIntfNumber && interfaceNumber < firstIntfNumber + interfaceCount; + } + + public int classCode() { + return functionCode; + } + + public int subclassCode() { + return functionSubclass; + } + + public int protocolCode() { + return functionProtocol; + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/common/Configuration.java b/java-does-usb/src/main/java/net/codecrete/usb/common/Configuration.java new file mode 100644 index 00000000..dc5317d1 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/common/Configuration.java @@ -0,0 +1,70 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.common; + +import net.codecrete.usb.UsbInterface; + +import java.util.ArrayList; +import java.util.List; + +/** + * Describes a device configuration. + */ +public class Configuration { + private final List functionList; + private final List interfaceList; + private final int configurationValue; + private final int configurationAttributes; + private final int configurationMaxPower; + + public Configuration(int configValue, int attributes, int maxPower) { + configurationValue = configValue; + configurationAttributes = attributes; + configurationMaxPower = maxPower; + functionList = new ArrayList<>(); + interfaceList = new ArrayList<>(); + } + + public int configValue() { + return configurationValue; + } + + public int attributes() { + return configurationAttributes; + } + + public int maxPower() { + return configurationMaxPower; + } + + public List interfaces() { + return interfaceList; + } + + public List functions() { + return functionList; + } + + public void addInterface(UsbInterface intf) { + interfaceList.add(intf); + } + + public UsbInterfaceImpl findInterfaceByNumber(int number) { + return (UsbInterfaceImpl) interfaceList.stream().filter(intf -> intf.getNumber() == number) + .findFirst().orElse(null); + } + + public void addFunction(CompositeFunction function) { + functionList.add(function); + } + + public CompositeFunction findFunction(int interfaceNumber) { + return functionList.stream().filter(f -> f.containsInterface(interfaceNumber)) + .findFirst().orElse(null); + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/common/ConfigurationParser.java b/java-does-usb/src/main/java/net/codecrete/usb/common/ConfigurationParser.java new file mode 100644 index 00000000..f8023292 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/common/ConfigurationParser.java @@ -0,0 +1,177 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.common; + +import net.codecrete.usb.UsbAlternateInterface; +import net.codecrete.usb.UsbDirection; +import net.codecrete.usb.UsbException; +import net.codecrete.usb.UsbTransferType; +import net.codecrete.usb.usbstandard.ConfigurationDescriptor; +import net.codecrete.usb.usbstandard.EndpointDescriptor; +import net.codecrete.usb.usbstandard.InterfaceAssociationDescriptor; +import net.codecrete.usb.usbstandard.InterfaceDescriptor; + +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; +import java.util.ArrayList; + +import static net.codecrete.usb.usbstandard.Constants.CONFIGURATION_DESCRIPTOR_TYPE; +import static net.codecrete.usb.usbstandard.Constants.ENDPOINT_DESCRIPTOR_TYPE; +import static net.codecrete.usb.usbstandard.Constants.INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE; +import static net.codecrete.usb.usbstandard.Constants.INTERFACE_DESCRIPTOR_TYPE; + +/** + * Parser for USB configuration descriptors. + * + *

+ * It extracts the information about endpoints, interfaces (incl. alternate interfaces) and associations + * between interfaces to derive the functions. Other descriptor types are ignored. + *

+ */ +public class ConfigurationParser { + + /** + * Parses a USB configuration descriptor (incl. interface and endpoint descriptors) + * + * @param desc configuration descriptor + * @return parsed configuration data + */ + public static Configuration parseConfigurationDescriptor(MemorySegment desc) { + var parser = new ConfigurationParser(desc); + return parser.parse(); + } + + private final MemorySegment descriptor; + private Configuration configuration; + + /** + * Creates a new parser for USB configuration descriptors (incl. interface and endpoint descriptors) + * + * @param descriptor configuration descriptor + */ + public ConfigurationParser(MemorySegment descriptor) { + this.descriptor = descriptor; + } + + public Configuration parse() { + parseHeader(); + + UsbAlternateInterfaceImpl lastAlternate = null; + var offset = peekDescLength(0); + + while (offset < descriptor.byteSize()) { + + var descLength = peekDescLength(offset); + var descType = peekDescType(offset); + + if (descType == INTERFACE_DESCRIPTOR_TYPE) { + var intf = parseInterface(offset); + + var parent = configuration.findInterfaceByNumber(intf.getNumber()); + if (parent != null) { + parent.addAlternate(intf.getCurrentAlternate()); + } else { + configuration.addInterface(intf); + } + lastAlternate = (UsbAlternateInterfaceImpl) intf.getCurrentAlternate(); + + var function = configuration.findFunction(intf.getNumber()); + if (function == null) { + function = new CompositeFunction(intf.getNumber(), 1, lastAlternate.getClassCode(), + lastAlternate.getSubclassCode(), lastAlternate.getProtocolCode()); + configuration.addFunction(function); + } + + } else if (descType == ENDPOINT_DESCRIPTOR_TYPE) { + var endpoint = parseEndpoint(offset); + if (lastAlternate != null) + lastAlternate.addEndpoint(endpoint); + + } else if (descType == INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE) { + parseIAD(offset); + } + + offset += descLength; + } + + return configuration; + } + + private void parseHeader() { + var desc = new ConfigurationDescriptor(descriptor); + if (CONFIGURATION_DESCRIPTOR_TYPE != desc.descriptorType()) + throw new UsbException("invalid USB configuration descriptor"); + + var totalLength = desc.totalLength(); + if (descriptor.byteSize() != totalLength) + throw new UsbException("invalid USB configuration descriptor (invalid length)"); + + configuration = new Configuration(desc.configurationValue(), desc.attributes(), desc.maxPower()); + } + + private UsbInterfaceImpl parseInterface(int offset) { + var desc = new InterfaceDescriptor(descriptor, offset); + var alternate = new UsbAlternateInterfaceImpl(desc.alternateSetting(), desc.interfaceClass(), + desc.interfaceSubClass(), desc.interfaceProtocol(), new ArrayList<>()); + var alternates = new ArrayList(); + alternates.add(alternate); + return new UsbInterfaceImpl(desc.interfaceNumber(), alternates); + } + + private void parseIAD(int offset) { + var desc = new InterfaceAssociationDescriptor(descriptor, offset); + var function = new CompositeFunction(desc.firstInterface(), desc.interfaceCount(), desc.functionClass(), + desc.functionSubClass(), desc.functionProtocol()); + configuration.addFunction(function); + } + + private UsbEndpointImpl parseEndpoint(int offset) { + var desc = new EndpointDescriptor(descriptor, offset); + var address = desc.endpointAddress(); + return new UsbEndpointImpl(getEndpointNumber(address), getEndpointDirection(address), + getEndpointType(desc.attributes()), desc.maxPacketSize()); + } + + private static UsbDirection getEndpointDirection(int address) { + return (address & 0x80) != 0 ? UsbDirection.IN : UsbDirection.OUT; + } + + private static int getEndpointNumber(int address) { + return address & 0x7f; + } + + private static UsbTransferType getEndpointType(int attributes) { + return switch (attributes & 0x3) { + case 1 -> UsbTransferType.ISOCHRONOUS; + case 2 -> UsbTransferType.BULK; + case 3 -> UsbTransferType.INTERRUPT; + default -> null; + }; + } + + /** + * Get descriptor length. + * + * @param offset offset to the descriptor of interest + * @return descriptor length (in bytes) + */ + private int peekDescLength(int offset) { + return 0xff & descriptor.get(ValueLayout.JAVA_BYTE, offset); + } + + /** + * Get descriptor type. + * + * @param offset offset to the descriptor of interest + * @return descriptor type + */ + private int peekDescType(int offset) { + return 0xff & descriptor.get(ValueLayout.JAVA_BYTE, offset + 1L); + } + +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/common/DescriptorParser.java b/java-does-usb/src/main/java/net/codecrete/usb/common/DescriptorParser.java deleted file mode 100644 index f6f7ab6c..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/common/DescriptorParser.java +++ /dev/null @@ -1,167 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// - -package net.codecrete.usb.common; - -import net.codecrete.usb.*; - -import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; -import java.util.ArrayList; -import java.util.List; - -/** - * Parser for USB descriptors - */ -public class DescriptorParser { - - /** - * Parse a USB configuration descriptor (incl. interface and endpoint descriptors) - * - * @param desc configuration descriptor - * @return parsed configuration data - */ - public static Configuration parseConfigurationDescriptor(MemorySegment desc, int vendorID, int productID) { - var config = parseConfiguration(desc); - - USBAlternateInterfaceImpl lastAlternate = null; - USBEndpointImpl lastEndpoint; - int offset = peekDescLength(desc, 0); - - while (offset < desc.byteSize()) { - - int descLength = peekDescLength(desc, offset); - int descType = peekDescType(desc, offset); - - if (descType == USBDescriptors.INTERFACE_DESCRIPTOR_TYPE) { - var intf = parseInterface(desc, offset); - var parent = config.findInterfaceByNumber(intf.number()); - if (parent != null) { - parent.addAlternate(intf.alternate()); - } else { - config.addInterface(intf); - } - lastAlternate = (USBAlternateInterfaceImpl) intf.alternate(); - - } else if (descType == USBDescriptors.ENDPOINT_DESCRIPTOR_TYPE) { - lastEndpoint = parseEndpoint(desc, offset); - if (lastAlternate != null) - lastAlternate.addEndpoint(lastEndpoint); - - } else //noinspection StatementWithEmptyBody - if (descType == USBDescriptors.INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE) { - // TODO: interface associations - - } else //noinspection StatementWithEmptyBody - if (descType == USBDescriptors.HID_DESCRIPTOR_TYPE || descType == USBDescriptors.CS_INTERFACE_DESCRIPTOR_TYPE || descType == USBDescriptors.CS_ENDPOINT_DESCRIPTOR_TYPE) { - // known descriptor but not relevant - - } else { - // TODO: Remove warning once the relevant descriptors are processed - System.err.printf("Info: [JavaDoesUSB] unsupported USB descriptor type 0x%02x of device " + - "0x%04x/0x%04x - ignoring descriptor%n", descType, vendorID, productID); - } - - offset += descLength; - } - - return config; - } - - private static Configuration parseConfiguration(MemorySegment descriptor) { - var desc = descriptor.asSlice(0, USBDescriptors.Configuration.byteSize()); - var config = new Configuration(); - if (USBDescriptors.CONFIGURATION_DESCRIPTOR_TYPE != (byte) USBDescriptors.Configuration_bDescriptorType.get(desc)) - throw new USBException("Invalid USB configuration descriptor"); - - short totalLength = (short) USBDescriptors.Configuration_wTotalLength.get(desc); - if (descriptor.byteSize() != totalLength) - throw new USBException("Invalid USB configuration descriptor length"); - - config.configValue = (byte) USBDescriptors.Configuration_bConfigurationValue.get(desc); - config.attributes = (byte) USBDescriptors.Configuration_bmAttributes.get(desc); - config.maxPower = (byte) USBDescriptors.Configuration_bMaxPower.get(desc); - config.interfaces = new ArrayList<>(); - return config; - } - - private static USBInterfaceImpl parseInterface(MemorySegment descriptor, int offset) { - var desc = descriptor.asSlice(offset, USBDescriptors.Interface.byteSize()); - var number = 255 & (byte) USBDescriptors.Interface_bInterfaceNumber.get(desc); - var altSetting = 255 & (byte) USBDescriptors.Interface_bAlternateSetting.get(desc); - var classCode = 255 & (byte) USBDescriptors.Interface_bInterfaceClass.get(desc); - var subclassCode = 255 & (byte) USBDescriptors.Interface_bInterfaceSubClass.get(desc); - var protocol = 255 & (byte) USBDescriptors.Interface_bInterfaceProtocol.get(desc); - var alternate = new USBAlternateInterfaceImpl(altSetting, classCode, subclassCode, protocol, new ArrayList<>()); - var alternates = new ArrayList(); - alternates.add(alternate); - return new USBInterfaceImpl(number, alternates); - } - - private static USBEndpointImpl parseEndpoint(MemorySegment descriptor, int offset) { - var desc = descriptor.asSlice(offset, USBDescriptors.Endpoint.byteSize()); - var address = (byte) USBDescriptors.Endpoint_bEndpointAddress.get(desc); - var attributes = (byte) USBDescriptors.Endpoint_bmAttributes.get(desc); - var maxPacketSize = (short) USBDescriptors.Endpoint_wMaxPacketSize.get(desc); - return new USBEndpointImpl(getEndpointNumber(address), getEndpointDirection(address), - getEndpointType(attributes), maxPacketSize); - } - - private static USBDirection getEndpointDirection(byte address) { - return (address & 0x80) != 0 ? USBDirection.IN : USBDirection.OUT; - } - - private static int getEndpointNumber(byte address) { - return address & 0x7f; - } - - private static USBTransferType getEndpointType(byte attributes) { - return switch (attributes & 0x3) { - case 1 -> USBTransferType.ISOCHRONOUS; - case 2 -> USBTransferType.BULK; - case 3 -> USBTransferType.INTERRUPT; - default -> null; - }; - } - - /** - * Get descriptor length. - * - * @param desc byte array containing multiple descriptors - * @param offset offset to the descriptor of interest - * @return descriptor length (in bytes) - */ - private static int peekDescLength(MemorySegment desc, int offset) { - return 255 & desc.get(ValueLayout.JAVA_BYTE, offset); - } - - /** - * Get descriptor type. - * - * @param desc byte array containing multiple descriptors - * @param offset offset to the descriptor of interest - * @return descriptor type - */ - private static int peekDescType(MemorySegment desc, int offset) { - return 255 & desc.get(ValueLayout.JAVA_BYTE, offset + 1); - } - - public static class Configuration { - public List interfaces; - public byte configValue; - public byte attributes; - public byte maxPower; - - void addInterface(USBInterface intf) { - interfaces.add(intf); - } - - public USBInterfaceImpl findInterfaceByNumber(int number) { - return (USBInterfaceImpl) interfaces.stream().filter((intf) -> intf.number() == number).findFirst().orElse(null); - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/common/EndpointInputStream.java b/java-does-usb/src/main/java/net/codecrete/usb/common/EndpointInputStream.java new file mode 100644 index 00000000..167b041c --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/common/EndpointInputStream.java @@ -0,0 +1,298 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.common; + +import net.codecrete.usb.UsbDirection; +import net.codecrete.usb.UsbException; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.util.Objects; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.TimeUnit; + +import static net.codecrete.usb.common.EndpointStreams.toIOException; + +import static java.lang.System.Logger.Level.WARNING; +import static java.lang.foreign.ValueLayout.JAVA_BYTE; + +/** + * Input stream for bulk endpoints – optimized for high throughput. + * + *

+ * Multiple asynchronous transfers are submitted to achieve a good + * degree of concurrency between the USB communication handled by the operating + * system and the consuming application code. + *

+ *

+ * For thread synchronization (between the background thread handling IO completions + * and the consuming application thread) a blocking queue is used. When an transfer + * completes, the background thread adds it to the queue. The consuming code + * waits for the next item in the queue. + *

+ */ +public abstract class EndpointInputStream extends InputStream { + + private static final System.Logger LOG = System.getLogger(EndpointInputStream.class.getName()); + + // Maximum time (ms) to wait for outstanding transfers to complete during teardown. + // A completion that is never delivered (e.g. after an unplug) then degrades to a + // logged warning instead of a permanent hang. + private static final long TEARDOWN_TIMEOUT_MS = 1000; + + protected UsbDeviceImpl device; + protected final int endpointNumber; + // Arena to allocate buffers and completion handlers + protected final Arena arena; + // Transfer size (multiple of packet size) + protected final int transferSize; + // Queue of completed transfers + private final ArrayBlockingQueue completedTransferQueue; + // Number of outstanding transfers (includes transfers pending with the + // operating system and transfers in the completed queue) + private int numOutstandingTransfers; + // Transfer and associated buffer being currently read from + private Transfer currentTransfer; + // Read offset within current transfer buffer + private int readOffset; + + /** + * Creates a new instance + * + * @param device USB device + * @param endpointNumber endpoint number + * @param bufferSize approximate buffer size (in bytes) + */ + protected EndpointInputStream(UsbDeviceImpl device, int endpointNumber, int bufferSize) { + this.device = device; + this.endpointNumber = endpointNumber; + //arena = Arena.ofShared(); // not supported by GraalVM + arena = Arena.ofAuto(); + + var packetSize = device.getEndpoint(UsbDirection.IN, endpointNumber).getPacketSize(); + + // use between 4 and 32 packets per transfer (256B to 2KB for FS, 2KB to 16KB for HS) + var numPacketsPerTransfer = (int) Math.round(Math.sqrt((double) bufferSize / packetSize)); + numPacketsPerTransfer = Math.clamp(numPacketsPerTransfer, 4, 32); + transferSize = numPacketsPerTransfer * packetSize; + + // use at least 2 outstanding transfers (3 in total) + var maxOutstandingTransfers = Math.max((bufferSize + transferSize / 2) / transferSize, 3); + + configureEndpoint(); + + completedTransferQueue = new ArrayBlockingQueue<>(maxOutstandingTransfers); + + // create all transfers, and submit them except one + try { + for (var i = 0; i < maxOutstandingTransfers; i++) { + final var transfer = device.createTransfer(); + transfer.setData(arena.allocate(transferSize, 8)); + transfer.setDataSize(transferSize); + transfer.setCompletion(this::onCompletion); + + if (i == 0) { + currentTransfer = transfer; + } else { + submitTransfer(transfer); + } + } + } catch (Exception t) { + collectOutstandingTransfers(); + throw t; + } + } + + private boolean isClosed() { + return device == null; + } + + @SuppressWarnings("RedundantThrows") + @Override + public void close() throws IOException { + if (isClosed()) + return; + + // abort all transfers on endpoint + try { + device.abortTransfers(UsbDirection.IN, endpointNumber); + + } catch (UsbException _) { + // If aborting the transfer is not possible, the device has + // likely been closed or unplugged. So all outstanding + // transfers will terminate anyway. + } + device = null; + + collectOutstandingTransfers(); + } + + @Override + public int read() throws IOException { + ensureOpen(); + + try { + if (bufferedBytes() == 0) + receiveMoreData(); + + var b = currentTransfer.data().get(JAVA_BYTE, readOffset) & 0xff; + readOffset += 1; + return b; + + } catch (UsbException e) { + throw toIOException(e); + } + } + + @Override + public int read(byte @NotNull [] b, int off, int len) throws IOException { + Objects.checkFromIndexSize(off, len, b.length); + ensureOpen(); + if (len == 0) + return 0; + + try { + var numRead = 0; + do { + if (bufferedBytes() == 0) + receiveMoreData(); + + // copy data to receiving buffer + var n = Math.min(len - numRead, currentTransfer.resultSize() - readOffset); + MemorySegment.copy(currentTransfer.data(), readOffset, MemorySegment.ofArray(b), (long) off + numRead, n); + readOffset += n; + numRead += n; + + } while (numRead < len && hasMoreTransfers()); + + return numRead; + + } catch (UsbException e) { + throw toIOException(e); + } + } + + @Override + public int available() throws IOException { + ensureOpen(); + return bufferedBytes(); + } + + // Bytes buffered in the current transfer, without a closed-stream check. + // Callers on the read path guard with ensureOpen() first. + private int bufferedBytes() { + return currentTransfer.resultSize() - readOffset; + } + + private void ensureOpen() throws IOException { + if (isClosed()) + throw new IOException("input stream has been closed"); + } + + private boolean hasMoreTransfers() { + return !completedTransferQueue.isEmpty(); + } + + private void receiveMoreData() throws IOException { + try { + // loop until non-ZLP has been received + do { + // the current transfer has no more data to process and + // can be submitted to read more data + submitTransfer(currentTransfer); + + currentTransfer = waitForCompletedTransfer(); + readOffset = 0; + + // check for error + if (currentTransfer.resultCode() != 0) + device.throwOSException(currentTransfer.resultCode(), "error occurred while reading from endpoint %d", + endpointNumber); + + } while (currentTransfer.resultSize() <= 0); + + } catch (Exception t) { + close(); + throw t; + } + } + + private Transfer waitForCompletedTransfer() { + // Defer interruption: keep a local flag instead of re-asserting the interrupt + // inside the loop (which would make the next take() throw immediately and + // busy-spin). Re-assert once the completion has actually arrived. + var wasInterrupted = false; + try { + while (true) { + try { + var transfer = completedTransferQueue.take(); + numOutstandingTransfers -= 1; + return transfer; + } catch (InterruptedException _) { + wasInterrupted = true; + } + } + } finally { + if (wasInterrupted) + Thread.currentThread().interrupt(); + } + } + + private void submitTransfer(Transfer transfer) { + submitTransferIn(transfer); + numOutstandingTransfers += 1; + } + + private void onCompletion(Transfer transfer) { + completedTransferQueue.add(transfer); + } + + @SuppressWarnings("java:S2142") + private void collectOutstandingTransfers() { + // Wait until the completion handlers have been called. This is a teardown path, + // so the wait is bounded: if a completion is never delivered (device unplugged, + // or the completion is lost in a source-removal race), abandon the transfer and + // log a warning instead of blocking the application thread forever. + var deadline = System.currentTimeMillis() + TEARDOWN_TIMEOUT_MS; + var wasInterrupted = false; + + while (numOutstandingTransfers > 0) { + var remaining = deadline - System.currentTimeMillis(); + if (remaining <= 0) { + LOG.log(WARNING, + "abandoning {0} outstanding transfer(s) during input stream teardown - no completion within {1} ms", + numOutstandingTransfers, TEARDOWN_TIMEOUT_MS); + break; + } + + try { + if (completedTransferQueue.poll(remaining, TimeUnit.MILLISECONDS) != null) + numOutstandingTransfers -= 1; + } catch (InterruptedException _) { + // defer the interrupt: keep polling the remaining time without re-setting + // the flag (avoids a busy-spin), then re-assert it once we are done + wasInterrupted = true; + } + } + + if (wasInterrupted) + Thread.currentThread().interrupt(); + + completedTransferQueue.clear(); + currentTransfer = null; + //arena.close(); + } + + protected abstract void submitTransferIn(Transfer transfer); + + protected void configureEndpoint() { + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/common/EndpointOutputStream.java b/java-does-usb/src/main/java/net/codecrete/usb/common/EndpointOutputStream.java new file mode 100644 index 00000000..c371939e --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/common/EndpointOutputStream.java @@ -0,0 +1,414 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.common; + +import net.codecrete.usb.UsbDirection; +import net.codecrete.usb.UsbException; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.util.Arrays; +import java.util.Objects; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.TimeUnit; + +import static net.codecrete.usb.common.EndpointStreams.toIOException; + +import static java.lang.System.Logger.Level.WARNING; +import static java.lang.foreign.ValueLayout.JAVA_BYTE; + +/** + * Output stream for bulk endpoints – optimized for high throughput. + * + *

+ * Multiple asynchronous transfers are submitted to achieve a good + * degree of concurrency between the USB communication handled by the operating + * system and the producing application code. The number of concurrent transfers is limited + * in order to retain flow control and keep memory usage at a reasonable size. + *

+ *

+ * For thread synchronization (between the background thread handling IO completion + * and the producing application thread) a blocking queue is used. It is prefilled with + * transfer instances ready to be used. The background thread adds the completed + * instances back to this queue. The producing application thread waits until a transfer instance + * is available for use. + *

+ */ +public abstract class EndpointOutputStream extends OutputStream { + + private static final System.Logger LOG = System.getLogger(EndpointOutputStream.class.getName()); + + // Maximum time (ms) to wait for outstanding transfers to complete during teardown. + // A completion that is never delivered (e.g. after an unplug) then degrades to a + // logged warning instead of a permanent hang. + private static final long TEARDOWN_TIMEOUT_MS = 1000; + + protected UsbDeviceImpl device; + protected final int endpointNumber; + protected final Arena arena; + // Endpoint packet size + private final int packetSize; + // Transfer size (multiple of packet size) + private final int transferSize; + // Blocking queue of available transfers (to limit the number of submitted transfers) + private final ArrayBlockingQueue availableTransferQueue; + private boolean needsZlp; + private Transfer currentTransfer; + private int writeOffset; + private int numOutstandingTransfers; + private boolean hasError; + + + /** + * Creates a new instance + * + * @param device USB device + * @param endpointNumber endpoint number + * @param bufferSize approximate buffer size (in bytes) + */ + protected EndpointOutputStream(UsbDeviceImpl device, int endpointNumber, int bufferSize) { + this.device = device; + this.endpointNumber = endpointNumber; + //arena = Arena.ofShared(); // not supported by GraalVM + arena = Arena.ofAuto(); + + packetSize = device.getEndpoint(UsbDirection.OUT, endpointNumber).getPacketSize(); + + // use between 4 and 32 packets per transfer (256B to 2KB for FS, 2KB to 16KB for HS) + var numPacketsPerTransfer = (int) Math.round(Math.sqrt((double) bufferSize / packetSize)); + numPacketsPerTransfer = Math.clamp(numPacketsPerTransfer, 4, 32); + transferSize = numPacketsPerTransfer * packetSize; + + // use at least 2 outstanding transfers (3 in total) + var maxOutstandingTransfers = Math.max((bufferSize + transferSize / 2) / transferSize, 3); + + configureEndpoint(); + + availableTransferQueue = new ArrayBlockingQueue<>(maxOutstandingTransfers); + + // prefill transfer queue + for (var i = 0; i < maxOutstandingTransfers; i++) { + final var transfer = device.createTransfer(); + transfer.setData(arena.allocate(transferSize, 8)); + transfer.setCompletion(this::onCompletion); + + if (i == 0) { + currentTransfer = transfer; + } else { + availableTransferQueue.add(transfer); + } + } + } + + private boolean isClosed() { + return device == null; + } + + @Override + public void close() throws IOException { + if (isClosed()) + return; + + // Teardown path: every wait is bounded by a single deadline so a lost completion + // (device unplugged, or completion dropped in a source-removal race) degrades to a + // logged warning instead of hanging the application thread. Unlike the public + // flush(), this must not route through the unbounded waits. + var deadline = System.currentTimeMillis() + TEARDOWN_TIMEOUT_MS; + + try { + if (!hasError) { + // best-effort: transmit any remaining buffered data (and a ZLP if needed) + if (writeOffset > 0) + submitForClose(writeOffset, deadline); + if (needsZlp && currentTransfer != null) + submitForClose(0, deadline); + } + + drainOutstandingTransfers(deadline); + + } catch (Exception e) { + // teardown must not fail; data-path errors are already surfaced by write()/flush() + LOG.log(WARNING, "error while closing output stream - ignoring", e); + + } finally { + device = null; + availableTransferQueue.clear(); + currentTransfer = null; + //arena.close(); + } + } + + /** + * Submits the current transfer during teardown and acquires a replacement, + * both bounded by the given deadline. + *

+ * Unlike {@link #submitTransfer(int)} this does not recurse into {@link #close()} on error, + * and it does not block indefinitely when acquiring the next transfer instance. + *

+ * + * @param size size of data to be transmitted + * @param deadline absolute deadline (ms since epoch) for acquiring the next transfer + */ + private void submitForClose(int size, long deadline) { + currentTransfer.setDataSize(size); + submitTransferOut(currentTransfer); + + synchronized (this) { + numOutstandingTransfers += 1; + } + + needsZlp = size == packetSize; + writeOffset = 0; + // if no transfer becomes available within the deadline, currentTransfer stays null, + // the drain below still bounded-waits for the in-flight transfer to complete + currentTransfer = pollAvailableTransfer(deadline); + } + + /** + * Waits for all outstanding transfers to complete, bounded by the given deadline. + *

+ * If a completion is not delivered in time, the remaining transfers are abandoned and a + * warning is logged. + *

+ * + * @param deadline absolute deadline (ms since epoch) + */ + private void drainOutstandingTransfers(long deadline) { + int numTransfers; + synchronized (this) { + numTransfers = numOutstandingTransfers + availableTransferQueue.size(); + } + + for (var i = 0; i < numTransfers; i++) { + if (pollAvailableTransfer(deadline) == null) { + int abandoned; + synchronized (this) { + abandoned = numOutstandingTransfers; + } + LOG.log(WARNING, + "abandoning {0} outstanding transfer(s) during output stream teardown - no completion within {1} ms", + abandoned, TEARDOWN_TIMEOUT_MS); + break; + } + } + } + + /** + * Waits until a transfer instance is available for use, bounded by the given deadline. + * + * @param deadline absolute deadline (ms since epoch) + * @return transfer instance ready for use, or {@code null} if the deadline expired + */ + private Transfer pollAvailableTransfer(long deadline) { + var wasInterrupted = false; + try { + while (true) { + var remaining = deadline - System.currentTimeMillis(); + if (remaining <= 0) + return null; + + try { + var transfer = availableTransferQueue.poll(remaining, TimeUnit.MILLISECONDS); + if (transfer == null) + return null; + + // surface a transfer error unless we are already in the error path + var result = transfer.resultCode(); + if (result != 0 && !hasError) { + transfer.setResultCode(0); + device.throwOSException(result, "error occurred while transmitting to endpoint %d", endpointNumber); + } + + return transfer; + + } catch (InterruptedException _) { + // defer the interrupt: keep polling the remaining time without re-setting + // the flag (avoids a busy-spin), then re-assert it once we are done + wasInterrupted = true; + } + } + } finally { + if (wasInterrupted) + Thread.currentThread().interrupt(); + } + } + + @Override + public void write(int b) throws IOException { + ensureOpen(); + + try { + currentTransfer.data().set(JAVA_BYTE, writeOffset, (byte) b); + writeOffset += 1; + if (writeOffset == transferSize) + submitTransfer(writeOffset); + + } catch (UsbException e) { + throw toIOException(e); + } + } + + @Override + public void write(byte @NotNull [] b, int off, int len) throws IOException { + Objects.checkFromIndexSize(off, len, b.length); + ensureOpen(); + + try { + while (len > 0) { + var chunkSize = Math.min(len, transferSize - writeOffset); + MemorySegment.copy(b, off, currentTransfer.data(), JAVA_BYTE, writeOffset, chunkSize); + writeOffset += chunkSize; + off += chunkSize; + len -= chunkSize; + + if (writeOffset == transferSize) + submitTransfer(writeOffset); + } + + } catch (UsbException e) { + throw toIOException(e); + } + } + + @Override + public void flush() throws IOException { + ensureOpen(); + + try { + if (writeOffset > 0) + submitTransfer(writeOffset); + + if (needsZlp) + submitTransfer(0); + + waitForOutstandingTransfers(); + + } catch (UsbException e) { + throw toIOException(e); + } + } + + /** + * Submits a transfer and set a new transfer instance + * as the current one, possibly waiting until one is ready. + *

+ * Throws an exception if the transfer to be reused has completed with an error on the + * previous operation. The exception is suppressed if {@code hasError} flag is set. + *

+ * + * @param size size of data to be transmitted + */ + private void submitTransfer(int size) throws IOException { + try { + currentTransfer.setDataSize(size); + submitTransferOut(currentTransfer); + + synchronized (this) { + numOutstandingTransfers += 1; + } + + needsZlp = size == packetSize; + writeOffset = 0; + currentTransfer = waitForAvailableTransfer(); + + } catch (Exception t) { + hasError = true; + close(); + throw t; + } + } + + /** + * Wait until all outstanding transfers have been completed. + *

+ * Throws an exception if any of the transfers has completed with an error. + * The exception is suppressed if {@code hasError} flag is set. + *

+ */ + private void waitForOutstandingTransfers() { + // Wait until all buffers have been transmitted by removing them from the + // queue and reinserting them. + + int numTransfers; + synchronized (this) { + numTransfers = numOutstandingTransfers + availableTransferQueue.size(); + } + + if (numTransfers == 0) + return; + + var transfers = new Transfer[numTransfers]; + for (var i = 0; i < numTransfers; i++) + transfers[i] = waitForAvailableTransfer(); + + // reinsert the transfer instances + if (!hasError) + availableTransferQueue.addAll(Arrays.asList(transfers)); + } + + /** + * Wait until one of the allocated transfer instances is available for use. + *

+ * Throws an exception if the transfer to be reused has completed with an error on the + * previous operation. The exception is suppressed if {@code hasError} flag is set. + *

+ * + * @return transfer instance ready for use + */ + private Transfer waitForAvailableTransfer() { + // Defer interruption: keep a local flag instead of re-asserting the interrupt + // inside the loop (which would make the next take() throw immediately and + // busy-spin). Re-assert once a transfer has actually become available. + var wasInterrupted = false; + try { + while (true) { + try { + var transfer = availableTransferQueue.take(); + + // check for error + var result = transfer.resultCode(); + if (result != 0 && !hasError) { + transfer.setResultCode(0); + device.throwOSException(result, "error occurred while transmitting to endpoint %d", endpointNumber); + } + + return transfer; + + } catch (InterruptedException _) { + wasInterrupted = true; + } + } + } finally { + if (wasInterrupted) + Thread.currentThread().interrupt(); + } + } + + /** + * Called by the asynchronous IO completion handler. + * + * @param transfer the completed request + */ + private synchronized void onCompletion(Transfer transfer) { + availableTransferQueue.add(transfer); + numOutstandingTransfers -= 1; + } + + protected abstract void submitTransferOut(Transfer request); + + protected void configureEndpoint() { + } + + private void ensureOpen() throws IOException { + if (isClosed()) + throw new IOException("output stream has been closed"); + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/common/EndpointStreams.java b/java-does-usb/src/main/java/net/codecrete/usb/common/EndpointStreams.java new file mode 100644 index 00000000..5e9fda43 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/common/EndpointStreams.java @@ -0,0 +1,44 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.common; + +import net.codecrete.usb.UsbException; +import net.codecrete.usb.UsbTimeoutException; + +import java.io.IOException; +import java.io.InterruptedIOException; + +/** + * Helpers shared by {@link EndpointInputStream} and {@link EndpointOutputStream}. + */ +final class EndpointStreams { + + private EndpointStreams() { + } + + /** + * Wraps a USB error in an {@link IOException} so it is surfaced through the + * {@link java.io.InputStream}/{@link java.io.OutputStream} contract. + *

+ * A transfer timeout is mapped to {@link InterruptedIOException} (java.io's "I/O timed out"). + * All other USB errors become a plain {@link IOException} with the {@link UsbException} as cause, + * which preserves the USB error code and any stall/timeout subtype for callers that inspect it. + *

+ * + * @param e the USB error + * @return the corresponding I/O exception + */ + static IOException toIOException(UsbException e) { + if (e instanceof UsbTimeoutException) { + var ioException = new InterruptedIOException(e.getMessage()); + ioException.initCause(e); + return ioException; + } + return new IOException(e.getMessage(), e); + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/common/ForeignMemory.java b/java-does-usb/src/main/java/net/codecrete/usb/common/ForeignMemory.java index 8f3b53ed..1102cba3 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/common/ForeignMemory.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/common/ForeignMemory.java @@ -7,36 +7,38 @@ package net.codecrete.usb.common; +import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; -import java.util.function.Consumer; + +import static java.lang.foreign.ValueLayout.ADDRESS; /** - * Helper functions for the foreign memory API. + * Helper functions for accessing native memory. */ public class ForeignMemory { + private ForeignMemory() { + } + + /** + * Dereferences the address at the start of given memory segment and returns the result as + * a memory segment with the length suitable for the specified layout. + * + * @param segment the memory segment with the pointer at offset 0 + * @param layout layout for determining size of memory segment + * @return the dereferenced memory segment + */ + public static MemorySegment dereference(MemorySegment segment, MemoryLayout layout) { + return segment.get(ADDRESS, 0).reinterpret(layout.byteSize()); + } /** - * Adds a custom cleanup action which will be execution when the session of the - * specified memory segment ends. - *

- * Note that the {@code segment}'s session will already be closed. The {@code segment} - * can no longer be used for function call. For that reason, the cleanup - * action is passed a copy of the segment (separate Java instance pointing to the - * same native memory). - *

- * @param segment the segment to be accessed during the cleanup - * @param action the cleanup action + * Dereferences the address at the start of given memory segment and returns the result as + * a memory segment of size 0. + * + * @param segment the memory segment with the pointer at offset 0 + * @return the dereferenced memory segment */ - public static void addCloseAction(MemorySegment segment, Consumer action) { - var size = segment.byteSize(); - var address = segment.address(); - Runnable closeAction = () -> { - try (MemorySession closingSession = MemorySession.openConfined()) { - var dup = MemorySegment.ofAddress(address, size, closingSession); - action.accept(dup); - } - }; - segment.session().addCloseAction(closeAction); + public static MemorySegment dereference(MemorySegment segment) { + return segment.get(ADDRESS, 0); } } diff --git a/java-does-usb/src/main/java/net/codecrete/usb/common/ScopeCleanup.java b/java-does-usb/src/main/java/net/codecrete/usb/common/ScopeCleanup.java new file mode 100644 index 00000000..44f0d8ce --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/common/ScopeCleanup.java @@ -0,0 +1,52 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.common; + +import java.util.ArrayList; +import java.util.List; + +/** + * Auto closeable object for clean up actions. + * + *

+ * Multiple cleanup actions can be registered and will + * be executed when this object is closed. They are run + * in the reverse order they are registered. + *

+ *

+ * Use with {@code try (...)} clause: + *

+ *
+ * try (var cleanup = new ScopeCleanup()) {
+ *     var service = ...;
+ *     cleanup.add(() -> releaseService(service));
+ *
+ *     ...more code...
+ * }
+ * 
+ */ +public class ScopeCleanup implements AutoCloseable { + + private final List cleanupActions = new ArrayList<>(); + + /** + * Registers a cleanup action to be run later. + * + * @param cleanupAction cleanup action + */ + public void add(Runnable cleanupAction) { + cleanupActions.add(cleanupAction); + } + + @Override + public void close() { + var size = cleanupActions.size(); + for (var i = size - 1; i >= 0; i--) + cleanupActions.get(i).run(); + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/common/Transfer.java b/java-does-usb/src/main/java/net/codecrete/usb/common/Transfer.java new file mode 100644 index 00000000..0be15f99 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/common/Transfer.java @@ -0,0 +1,117 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.common; + +import java.lang.foreign.MemorySegment; + +/** + * Asynchronous USB endpoint transfer. + */ +public class Transfer { + private MemorySegment data; + private int dataSize; + private int resultCode; + private int resultSize; + private TransferCompletion completion; + + /** + * Gets the with data to transfer (in or out). + * + * @return buffer + */ + public MemorySegment data() { + return data; + } + + /** + * Sets buffer with data to transfer (in or out) + * + * @param data buffer + */ + public void setData(MemorySegment data) { + this.data = data; + } + + /** + * Gets length of data to transfer. + * + * @return length (in bytes) + */ + public int dataSize() { + return dataSize; + } + + /** + * Sets length of data to transfer. + * + * @param dataSize length (in bytes) + */ + public void setDataSize(int dataSize) { + this.dataSize = dataSize; + } + + /** + * Gets result code (operating system specific). + *

+ * 0 represents success, all other values represent an error. + *

+ * + * @return result code + */ + public int resultCode() { + return resultCode; + } + + /** + * Sets result code (operating system specific). + *

+ * 0 represents success, all other values represent an error. + *

+ * + * @param resultCode result code + */ + public void setResultCode(int resultCode) { + this.resultCode = resultCode; + } + + /** + * Gets length of transferred data. + * + * @return length (in bytes) + */ + public int resultSize() { + return resultSize; + } + + /** + * Sets length of transferred data. + * + * @param resultSize length (in bytes) + */ + public void setResultSize(int resultSize) { + this.resultSize = resultSize; + } + + /** + * Gets completion handler to call when the transfer is complete. + * + * @return completion handler + */ + public TransferCompletion completion() { + return completion; + } + + /** + * Sets completion handler to call when the transfer is complete. + * + * @param completion completion handler + */ + public void setCompletion(TransferCompletion completion) { + this.completion = completion; + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/common/TransferCompletion.java b/java-does-usb/src/main/java/net/codecrete/usb/common/TransferCompletion.java new file mode 100644 index 00000000..98a555e6 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/common/TransferCompletion.java @@ -0,0 +1,30 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.common; + +/** + * Functional interface used to notify when an asynchronous USB transfer operation has completed. + */ +@FunctionalInterface +public interface TransferCompletion { + + /** + * Called when the asynchronous transfer has completed. + *

+ * When this function has been called, {@link Transfer#resultSize()} and + * {@link Transfer#resultSize()} have been filled in. + *

+ *

+ * Since there is a single background task calling all completion functions, + * this function should only execute little work and return quickly. + *

+ * + * @param transfer completed transfer request + */ + void completed(Transfer transfer); +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/common/USBAlternateInterfaceImpl.java b/java-does-usb/src/main/java/net/codecrete/usb/common/USBAlternateInterfaceImpl.java deleted file mode 100644 index 4bc5c9d2..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/common/USBAlternateInterfaceImpl.java +++ /dev/null @@ -1,61 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// - -package net.codecrete.usb.common; - -import net.codecrete.usb.USBAlternateInterface; -import net.codecrete.usb.USBEndpoint; - -import java.util.Collections; -import java.util.List; - -public class USBAlternateInterfaceImpl implements USBAlternateInterface { - - private final int number_; - private final int classCode_; - private final int subclassCode_; - private final int protocolCode_; - private final List endpoints_; - - public USBAlternateInterfaceImpl(int number, int classCode, int subclassCode, int protocolCode, - List endpoints) { - number_ = number; - classCode_ = classCode; - subclassCode_ = subclassCode; - protocolCode_ = protocolCode; - endpoints_ = endpoints; - } - - @Override - public int number() { - return number_; - } - - @Override - public int classCode() { - return classCode_; - } - - @Override - public int subclassCode() { - return subclassCode_; - } - - @Override - public int protocolCode() { - return protocolCode_; - } - - @Override - public List endpoints() { - return Collections.unmodifiableList(endpoints_); - } - - void addEndpoint(USBEndpoint endpoint) { - endpoints_.add(endpoint); - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/common/USBDescriptors.java b/java-does-usb/src/main/java/net/codecrete/usb/common/USBDescriptors.java deleted file mode 100644 index 2ead6d77..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/common/USBDescriptors.java +++ /dev/null @@ -1,182 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// - -package net.codecrete.usb.common; - -import java.lang.foreign.GroupLayout; -import java.lang.invoke.VarHandle; - -import static java.lang.foreign.MemoryLayout.PathElement.groupElement; -import static java.lang.foreign.MemoryLayout.structLayout; -import static java.lang.foreign.ValueLayout.JAVA_BYTE; -import static java.lang.foreign.ValueLayout.JAVA_SHORT; - -/** - * Memory layout of USB descriptors. - */ -public class USBDescriptors { - - public static final byte DEVICE_DESCRIPTOR_TYPE = 0x01; - public static final byte CONFIGURATION_DESCRIPTOR_TYPE = 0x02; - public static final byte STRING_DESCRIPTOR_TYPE = 0x03; - public static final byte INTERFACE_DESCRIPTOR_TYPE = 0x04; - public static final byte ENDPOINT_DESCRIPTOR_TYPE = 0x05; - public static final byte DEVICE_QUALIFIER_DESCRIPTOR_TYPE = 0x06; - public static final byte OTHER_SPEED_CONFIGURATION_DESCRIPTOR_TYPE = 0x07; - public static final byte INTERFACE_POWER_DESCRIPTOR_TYPE = 0x08; - public static final byte OTG_DESCRIPTOR_TYPE = 0x09; - public static final byte DEBUG_DESCRIPTOR_TYPE = 0x0a; - public static final byte INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE = 0x0b; - public static final byte BOS_DESCRIPTOR_TYPE = 0x0f; - public static final byte DEVICE_CAPABILITY_DESCRIPTOR_TYPE = 0x10; - public static final byte HID_DESCRIPTOR_TYPE = 0x21; - public static final byte CS_INTERFACE_DESCRIPTOR_TYPE = 0x24; - public static final byte CS_ENDPOINT_DESCRIPTOR_TYPE = 0x25; - public static final byte USB_20_HUB_DESCRIPTOR_TYPE = 0x29; - public static final byte USB_30_HUB_DESCRIPTOR_TYPE = 0x2a; - public static final byte SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR_TYPE = 0x30; - public static final byte SUPERSPEEDPLUS_ISOCH_ENDPOINT_COMPANION_DESCRIPTOR_TYPE = 0x31; - - public static final short DEFAULT_LANGUAGE = 0x0409; - - // typedef struct { - // UCHAR bLength; - // UCHAR bDescriptorType; - // USHORT bcdUSB; - // UCHAR bDeviceClass; - // UCHAR bDeviceSubClass; - // UCHAR bDeviceProtocol; - // UCHAR bMaxPacketSize0; - // USHORT idVendor; - // USHORT idProduct; - // USHORT bcdDevice; - // UCHAR iManufacturer; - // UCHAR iProduct; - // UCHAR iSerialNumber; - // UCHAR bNumConfigurations; - //} __attribute__((packed)); - public static final GroupLayout Device$Struct = structLayout( - JAVA_BYTE.withName("bLength"), - JAVA_BYTE.withName("bDescriptorType"), - JAVA_SHORT.withName("bcdUSB"), - JAVA_BYTE.withName("bDeviceClass"), - JAVA_BYTE.withName("bDeviceSubClass"), - JAVA_BYTE.withName("bDeviceProtocol"), - JAVA_BYTE.withName("bMaxPacketSize0"), - JAVA_SHORT.withName("idVendor"), - JAVA_SHORT.withName("idProduct"), - JAVA_SHORT.withName("bcdDevice"), - JAVA_BYTE.withName("iManufacturer"), - JAVA_BYTE.withName("iProduct"), - JAVA_BYTE.withName("iSerialNumber"), - JAVA_BYTE.withName("bNumConfigurations") - ); - - public static final VarHandle Device_bcdUSB = Device$Struct.varHandle(groupElement("bcdUSB")); - public static final VarHandle Device_bDeviceClass = Device$Struct.varHandle(groupElement("bDeviceClass")); - public static final VarHandle Device_bDeviceSubClass = Device$Struct.varHandle(groupElement("bDeviceSubClass")); - public static final VarHandle Device_bDeviceProtocol = Device$Struct.varHandle(groupElement("bDeviceProtocol")); - public static final VarHandle Device_idVendor = Device$Struct.varHandle(groupElement("idVendor")); - public static final VarHandle Device_idProduct = Device$Struct.varHandle(groupElement("idProduct")); - public static final VarHandle Device_bcdDevice = Device$Struct.varHandle(groupElement("bcdDevice")); - public static final VarHandle Device_iManufacturer = Device$Struct.varHandle(groupElement("iManufacturer")); - public static final VarHandle Device_iProduct = Device$Struct.varHandle(groupElement("iProduct")); - public static final VarHandle Device_iSerialNumber = Device$Struct.varHandle(groupElement("iSerialNumber")); - - // struct USBConfigurationDescriptor - //{ - // uint8_t bLength; - // uint8_t bDescriptorType; - // uint16_t wTotalLength; - // uint8_t bNumInterfaces; - // uint8_t bConfigurationValue; - // uint8_t iConfiguration; - // uint8_t bmAttributes; - // uint8_t MaxPower; - //} __attribute__((packed)); - - /** - * USB configuration descriptor - */ - public static final GroupLayout Configuration = structLayout(JAVA_BYTE.withName("bLength"), JAVA_BYTE.withName( - "bDescriptorType"), JAVA_SHORT.withName("wTotalLength"), JAVA_BYTE.withName("bNumInterfaces"), - JAVA_BYTE.withName("bConfigurationValue"), JAVA_BYTE.withName("iConfiguration"), JAVA_BYTE.withName( - "bmAttributes"), JAVA_BYTE.withName("bMaxPower")); - - public static final VarHandle Configuration_bLength = Configuration.varHandle(groupElement("bLength")); - public static final VarHandle Configuration_bDescriptorType = Configuration.varHandle(groupElement( - "bDescriptorType")); - public static final VarHandle Configuration_wTotalLength = Configuration.varHandle(groupElement("wTotalLength")); - public static final VarHandle Configuration_bNumInterfaces = Configuration.varHandle(groupElement("bNumInterfaces" - )); - public static final VarHandle Configuration_bConfigurationValue = Configuration.varHandle(groupElement( - "bConfigurationValue")); - public static final VarHandle Configuration_iConfiguration = Configuration.varHandle(groupElement("iConfiguration" - )); - public static final VarHandle Configuration_bmAttributes = Configuration.varHandle(groupElement("bmAttributes")); - public static final VarHandle Configuration_bMaxPower = Configuration.varHandle(groupElement("bMaxPower")); - - // struct USBInterfaceDescriptor - // { - // uint8_t bLength; - // uint8_t bDescriptorType; - // uint8_t bInterfaceNumber; - // uint8_t bAlternateSetting; - // uint8_t bNumEndpoints; - // uint8_t bInterfaceClass; - // uint8_t bInterfaceSubClass; - // uint8_t bInterfaceProtocol; - // uint8_t iInterface; - // } __attribute__((packed)); - - public static final GroupLayout Interface = structLayout(JAVA_BYTE.withName("bLength"), JAVA_BYTE.withName( - "bDescriptorType"), JAVA_BYTE.withName("bInterfaceNumber"), JAVA_BYTE.withName("bAlternateSetting"), - JAVA_BYTE.withName("bNumEndpoints"), JAVA_BYTE.withName("bInterfaceClass"), JAVA_BYTE.withName( - "bInterfaceSubClass"), JAVA_BYTE.withName("bInterfaceProtocol"), JAVA_BYTE.withName("iInterface")); - - public static final VarHandle Interface_bLength = Interface.varHandle(groupElement("bLength")); - public static final VarHandle Interface_bDescriptorType = Interface.varHandle(groupElement("bDescriptorType")); - public static final VarHandle Interface_bInterfaceNumber = Interface.varHandle(groupElement("bInterfaceNumber")); - public static final VarHandle Interface_bAlternateSetting = Interface.varHandle(groupElement("bAlternateSetting")); - public static final VarHandle Interface_bNumEndpoints = Interface.varHandle(groupElement("bNumEndpoints")); - public static final VarHandle Interface_bInterfaceClass = Interface.varHandle(groupElement("bInterfaceClass")); - public static final VarHandle Interface_bInterfaceSubClass = Interface.varHandle(groupElement("bInterfaceSubClass" - )); - public static final VarHandle Interface_bInterfaceProtocol = Interface.varHandle(groupElement("bInterfaceProtocol" - )); - public static final VarHandle Interface_iInterface = Interface.varHandle(groupElement("iInterface")); - - - // struct USBInterfaceDescriptor - // { - // uint8_t bLength; - // uint8_t bDescriptorType; - // uint8_t bEndpointAddress; - // uint8_t bmAttributes; - // uint16_t wMaxPacketSize; - // uint8_t bInterval; - // } __attribute__((packed)); - - public static final GroupLayout Endpoint = structLayout(JAVA_BYTE.withName("bLength"), JAVA_BYTE.withName( - "bDescriptorType"), JAVA_BYTE.withName("bEndpointAddress"), JAVA_BYTE.withName("bmAttributes"), - JAVA_SHORT.withName("wMaxPacketSize").withBitAlignment(8), JAVA_BYTE.withName("bInterval")); - - public static final VarHandle Endpoint_bLength = Endpoint.varHandle(groupElement("bLength")); - public static final VarHandle Endpoint_bDescriptorType = Endpoint.varHandle(groupElement("bDescriptorType")); - public static final VarHandle Endpoint_bEndpointAddress = Endpoint.varHandle(groupElement("bEndpointAddress")); - public static final VarHandle Endpoint_bmAttributes = Endpoint.varHandle(groupElement("bmAttributes")); - public static final VarHandle Endpoint_wMaxPacketSize = Endpoint.varHandle(groupElement("wMaxPacketSize")); - public static final VarHandle Endpoint_bInterval = Endpoint.varHandle(groupElement("bInterval")); - - - static { - assert Device$Struct.byteSize() == 18; - assert Configuration.byteSize() == 9; - assert Interface.byteSize() == 9; - assert Endpoint.byteSize() == 7; - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/common/USBDeviceImpl.java b/java-does-usb/src/main/java/net/codecrete/usb/common/USBDeviceImpl.java deleted file mode 100644 index 8eb1446b..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/common/USBDeviceImpl.java +++ /dev/null @@ -1,288 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// - -package net.codecrete.usb.common; - -import net.codecrete.usb.*; - -import java.util.Collections; -import java.util.List; - -public abstract class USBDeviceImpl implements USBDevice { - - protected final Object id_; - protected final int vendorId_; - protected final int productId_; - protected final String manufacturer_; - protected final String product_; - protected final String serialNumber_; - protected int classCode_; - protected int subclassCode_; - protected int protocolCode_; - protected Version usbVersion_; - protected Version deviceVersion_; - - protected List interfaces_; - - /** - * Creates a new instance. - * - * @param id unique device ID - * @param vendorId USB vendor ID - * @param productId USB product ID - * @param manufacturer manufacturer name - * @param product product name - * @param serialNumber serial number - */ - protected USBDeviceImpl(Object id, int vendorId, int productId, String manufacturer, String product, - String serialNumber) { - - assert id != null; - - id_ = id; - vendorId_ = vendorId; - productId_ = productId; - manufacturer_ = manufacturer; - product_ = product; - serialNumber_ = serialNumber; - } - - @Override - public abstract void open(); - - @Override - public abstract void close(); - - @Override - public abstract boolean isOpen(); - - protected void checkIsOpen() { - if (!isOpen()) - throw new USBException("The device needs to be open to call this method"); - } - - @Override - public int productId() { - return productId_; - } - - @Override - public int vendorId() { - return vendorId_; - } - - @Override - public String product() { - return product_; - } - - @Override - public String manufacturer() { - return manufacturer_; - } - - @Override - public String serialNumber() { - return serialNumber_; - } - - @Override - public int classCode() { - return classCode_; - } - - @Override - public int subclassCode() { - return subclassCode_; - } - - @Override - public int protocolCode() { - return protocolCode_; - } - - @Override - public Version usbVersion() { return usbVersion_; } - - @Override - public Version deviceVersion() { return deviceVersion_; } - - public Object getUniqueId() { - return id_; - } - - public void setClassCodes(int classCode, int subclassCode, int protocolCode) { - classCode_ = classCode; - subclassCode_ = subclassCode; - protocolCode_ = protocolCode; - } - - public void setVersions(int usbVersion, int deviceVersion) { - usbVersion_ = new Version(usbVersion); - deviceVersion_ = new Version(deviceVersion); - } - - @Override - public List interfaces() { - return Collections.unmodifiableList(interfaces_); - } - - public void setInterfaces(List interfaces) { - interfaces_ = interfaces; - } - - @Override - public abstract void claimInterface(int interfaceNumber); - - @Override - public abstract void releaseInterface(int interfaceNumber); - - public void setClaimed(int interfaceNumber, boolean claimed) { - for (var intf : interfaces_) { - if (intf.number() == interfaceNumber) { - ((USBInterfaceImpl) intf).setClaimed(claimed); - return; - } - } - throw new USBException("Internal error (interface not found)"); - } - - /** - * Returns the interface with the specified number. - * - * @param interfaceNumber the interface number - * @return the interface - */ - protected USBInterfaceImpl getInterface(int interfaceNumber) { - return (USBInterfaceImpl) interfaces_.stream().filter((intf) -> intf.number() == interfaceNumber).findFirst().orElse(null); - } - - /** - * Checks if the specified endpoint is valid for communication and returns the endpoint address. - * - * @param endpointNumber endpoint number (1 to 127) - * @param direction transfer direction - * @param transferType1 transfer type 1 - * @param transferType2 transfer type 2 (or {@code null}) - * @return endpoint address - */ - protected byte getEndpointAddress(int endpointNumber, USBDirection direction, - USBTransferType transferType1, USBTransferType transferType2) { - - checkIsOpen(); - - if (endpointNumber >= 1 && endpointNumber <= 127) { - for (var intf : interfaces_) { - if (intf.isClaimed()) { - for (var ep : intf.alternate().endpoints()) { - if (ep.number() == endpointNumber && ep.direction() == direction - && (ep.transferType() == transferType1 || ep.transferType() == transferType2)) - return (byte) (endpointNumber | (direction == USBDirection.IN ? 0x80 : 0)); - } - } - } - } - - throwInvalidEndpointException(endpointNumber, direction, transferType1, transferType2); - return 0; // will never be reached - } - - /** - * Checks if the specified endpoint is valid for communication and returns the endpoint. - * - * @param endpointNumber endpoint number (1 to 127) - * @param direction transfer direction - * @param transferType1 transfer type 1 - * @param transferType2 transfer type 2 (or {@code null}) - * @return endpoint - */ - protected EndpointInfo getEndpoint(int endpointNumber, USBDirection direction, - USBTransferType transferType1, USBTransferType transferType2) { - - checkIsOpen(); - - if (endpointNumber >= 1 && endpointNumber <= 127) { - for (var intf : interfaces_) { - if (intf.isClaimed()) { - for (var ep : intf.alternate().endpoints()) { - if (ep.number() == endpointNumber && ep.direction() == direction - && (ep.transferType() == transferType1 || ep.transferType() == transferType2)) - return new EndpointInfo(intf.number(), ep.number(), - (byte) (endpointNumber | (direction == USBDirection.IN ? 0x80 : 0))); - } - } - } - } - - throwInvalidEndpointException(endpointNumber, direction, transferType1, transferType2); - return null; // will never be reached - } - - protected void throwInvalidEndpointException(int endpointNumber, USBDirection direction, - USBTransferType transferType1, USBTransferType transferType2) { - String transferTypeDesc; - if (transferType2 == null) - transferTypeDesc = transferType1.name(); - else - transferTypeDesc = String.format("%s or %s", transferType1.name(), transferType2.name()); - throw new USBException(String.format("Endpoint number %d does not exist, is not part of a claimed interface " + - "or is not valid for %s transfer in %s direction", endpointNumber, transferTypeDesc, - direction.name())); - } - - protected int getInterfaceNumber(int endpointNumber) { - if (endpointNumber < 1 || endpointNumber > 127) - return -1; - - for (var intf : interfaces_) { - if (intf.isClaimed()) { - for (var ep : intf.alternate().endpoints()) { - if (ep.number() == endpointNumber) - return intf.number(); - } - } - } - - return -1; - } - - @Override - public abstract byte[] controlTransferIn(USBControlTransfer setup, int length); - - @Override - public abstract void controlTransferOut(USBControlTransfer setup, byte[] data); - - - @Override - public abstract void transferOut(int endpointNumber, byte[] data); - - @Override - public abstract byte[] transferIn(int endpointNumber, int maxLength); - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - USBDeviceImpl that = (USBDeviceImpl) o; - return id_.equals(that.id_); - } - - @Override - public int hashCode() { - return id_.hashCode(); - } - - @Override - public String toString() { - return "VID: 0x" + String.format("%04x", vendorId_) + ", PID: 0x" + String.format("%04x", productId_) + ", " + "manufacturer: " + manufacturer_ + ", product: " + product_ + ", serial: " + serialNumber_ + ", ID: " + id_; - } - - public record EndpointInfo(int interfaceNumber, int endpointNumber, byte endpointAddress) { - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/common/USBEndpointImpl.java b/java-does-usb/src/main/java/net/codecrete/usb/common/USBEndpointImpl.java deleted file mode 100644 index c245a4f3..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/common/USBEndpointImpl.java +++ /dev/null @@ -1,50 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// - -package net.codecrete.usb.common; - -import net.codecrete.usb.USBDirection; -import net.codecrete.usb.USBEndpoint; -import net.codecrete.usb.USBTransferType; - -/** - * Implementation of {@code USBEndpoint} interface. - */ -public class USBEndpointImpl implements USBEndpoint { - - private final int number_; - private final USBDirection direction_; - private final USBTransferType type_; - private final int packetSize_; - - public USBEndpointImpl(int number, USBDirection direction, USBTransferType type, int packetSize) { - number_ = number; - direction_ = direction; - type_ = type; - packetSize_ = packetSize; - } - - @Override - public int number() { - return number_; - } - - @Override - public USBDirection direction() { - return direction_; - } - - @Override - public USBTransferType transferType() { - return type_; - } - - @Override - public int packetSize() { - return packetSize_; - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/common/USBInterfaceImpl.java b/java-does-usb/src/main/java/net/codecrete/usb/common/USBInterfaceImpl.java deleted file mode 100644 index 7ff8c970..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/common/USBInterfaceImpl.java +++ /dev/null @@ -1,57 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// - -package net.codecrete.usb.common; - -import net.codecrete.usb.USBAlternateInterface; -import net.codecrete.usb.USBInterface; - -import java.util.Collections; -import java.util.List; - -public class USBInterfaceImpl implements USBInterface { - - private final int number_; - private final USBAlternateInterface alternate_; - private final List alternates_; - - private boolean isClaimed_; - - public USBInterfaceImpl(int number, List alternates) { - number_ = number; - alternates_ = alternates; - alternate_ = alternates.get(0); - } - - @Override - public int number() { - return number_; - } - - @Override - public boolean isClaimed() { - return isClaimed_; - } - - public void setClaimed(boolean claimed) { - isClaimed_ = claimed; - } - - @Override - public USBAlternateInterface alternate() { - return alternate_; - } - - @Override - public List alternates() { - return Collections.unmodifiableList(alternates_); - } - - void addAlternate(USBAlternateInterface alt) { - alternates_.add(alt); - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/common/USBStructs.java b/java-does-usb/src/main/java/net/codecrete/usb/common/USBStructs.java deleted file mode 100644 index b98d3187..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/common/USBStructs.java +++ /dev/null @@ -1,35 +0,0 @@ -package net.codecrete.usb.common; - -import java.lang.foreign.GroupLayout; -import java.lang.invoke.VarHandle; - -import static java.lang.foreign.MemoryLayout.PathElement.groupElement; -import static java.lang.foreign.MemoryLayout.structLayout; -import static java.lang.foreign.ValueLayout.JAVA_BYTE; -import static java.lang.foreign.ValueLayout.JAVA_SHORT; - -/** - * Memory layout of USB data structures. - */ -public class USBStructs { - // typedef struct { - // uint8_t RequestType; - // uint8_t Request; - // uint8_t Value; - // uint16_t Index; - // uint16_t Length; - //} __attribute__((packed)); - public static final GroupLayout SetupPacket$Struct = structLayout(JAVA_BYTE.withName("bmRequest"), - JAVA_BYTE.withName("bRequest"), JAVA_SHORT.withName("wValue"), JAVA_SHORT.withName("wIndex"), - JAVA_SHORT.withName("wLength")); - - public static final VarHandle SetupPacket_bmRequest = SetupPacket$Struct.varHandle(groupElement("bmRequest")); - public static final VarHandle SetupPacket_bRequest = SetupPacket$Struct.varHandle(groupElement("bRequest")); - public static final VarHandle SetupPacket_wValue = SetupPacket$Struct.varHandle(groupElement("wValue")); - public static final VarHandle SetupPacket_wIndex = SetupPacket$Struct.varHandle(groupElement("wIndex")); - public static final VarHandle SetupPacket_wLength = SetupPacket$Struct.varHandle(groupElement("wLength")); - - static { - assert SetupPacket$Struct.byteSize() == 8; - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/common/UsbAlternateInterfaceImpl.java b/java-does-usb/src/main/java/net/codecrete/usb/common/UsbAlternateInterfaceImpl.java new file mode 100644 index 00000000..9a7d70b1 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/common/UsbAlternateInterfaceImpl.java @@ -0,0 +1,74 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.common; + +import net.codecrete.usb.UsbAlternateInterface; +import net.codecrete.usb.UsbDirection; +import net.codecrete.usb.UsbEndpoint; +import net.codecrete.usb.UsbException; +import org.jetbrains.annotations.NotNull; + +import java.util.Comparator; +import java.util.List; + +import static java.util.Collections.unmodifiableList; + +public class UsbAlternateInterfaceImpl implements UsbAlternateInterface { + + private final int alternateInterfaceNumber; + private final int alternateInterfaceClass; + private final int alternateInterfaceSubclass; + private final int alternateInterfaceProtocol; + private final List endpointList; + + public UsbAlternateInterfaceImpl(int number, int classCode, int subclassCode, int protocolCode, + List endpoints) { + alternateInterfaceNumber = number; + alternateInterfaceClass = classCode; + alternateInterfaceSubclass = subclassCode; + alternateInterfaceProtocol = protocolCode; + endpointList = endpoints; + endpointList.sort(Comparator.comparingInt(UsbEndpoint::getNumber)); + } + + @Override + public int getNumber() { + return alternateInterfaceNumber; + } + + @Override + public int getClassCode() { + return alternateInterfaceClass; + } + + @Override + public int getSubclassCode() { + return alternateInterfaceSubclass; + } + + @Override + public int getProtocolCode() { + return alternateInterfaceProtocol; + } + + @Override + public @NotNull List getEndpoints() { + return unmodifiableList(endpointList); + } + + void addEndpoint(UsbEndpoint endpoint) { + endpointList.add(endpoint); + } + + @Override + public @NotNull UsbEndpoint getEndpoint(int endpointNumber, UsbDirection direction) { + return endpointList.stream() + .filter(ep -> ep.getNumber() == endpointNumber && ep.getDirection() == direction).findFirst() + .orElseThrow(() -> new UsbException(String.format("Endpoint %d (%s) does not exist", endpointNumber, direction))); + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/common/UsbDeviceImpl.java b/java-does-usb/src/main/java/net/codecrete/usb/common/UsbDeviceImpl.java new file mode 100644 index 00000000..41a1fb4f --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/common/UsbDeviceImpl.java @@ -0,0 +1,517 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.common; + +import net.codecrete.usb.UsbDevice; +import net.codecrete.usb.UsbDirection; +import net.codecrete.usb.UsbEndpoint; +import net.codecrete.usb.UsbException; +import net.codecrete.usb.UsbInterface; +import net.codecrete.usb.UsbTimeoutException; +import net.codecrete.usb.UsbTransferType; +import net.codecrete.usb.Version; +import net.codecrete.usb.usbstandard.DeviceDescriptor; +import org.jetbrains.annotations.NotNull; + +import java.lang.foreign.MemorySegment; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.function.IntFunction; + +import static java.lang.System.Logger.Level.WARNING; +import static java.lang.foreign.ValueLayout.JAVA_BYTE; + +@SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") +public abstract class UsbDeviceImpl implements UsbDevice { + + private static final System.Logger LOG = System.getLogger(UsbDeviceImpl.class.getName()); + + // Maximum time (ms) to wait for the completion of a transfer that was aborted due to a + // timeout. If the abort's completion is never delivered (e.g. after an unplug), the wait + // degrades to a logged warning instead of a permanent hang. The transfer is then abandoned. + private static final long ABORT_COMPLETION_TIMEOUT_MS = 1000; + + /** + * Operating system-specific device ID used for {@link #equals(Object)} and {@link #hashCode()}. + *

+ * Can be a String instance (such as a file path) or a Long instance (such as an internal ID). + * It only needs to be valid for the duration the device is connected. + *

+ */ + protected final Object uniqueDeviceId; + + protected List interfaceList; + + protected byte[] rawDeviceDescriptor; + + protected byte[] rawConfigurationDescriptor; + + // Information from the device descriptor + protected final int vid; + protected final int pid; + protected String manufacturerString; + protected String productString; + protected String serialString; + protected int deviceClass; + protected int deviceSubclass; + protected int deviceProtocol; + protected Version versionUsb; + protected Version versionDevice; + // volatile: written by the device monitor thread in disconnect(), read unlocked + // via isConnected() and checkIsClosed() + protected volatile boolean connected; + + /** + * Creates a new instance. + * + * @param id unique device ID + * @param vendorId USB vendor ID + * @param productId USB product ID + */ + protected UsbDeviceImpl(Object id, int vendorId, int productId) { + + assert id != null; + + uniqueDeviceId = id; + vid = vendorId; + pid = productId; + connected = true; + } + + @Override + public void detachStandardDrivers() { + if (isOpened()) + throw new UsbException("detachStandardDrivers() must not be called while the device is open"); + + // default implementation: do nothing + } + + @Override + public void attachStandardDrivers() { + if (isOpened()) + throw new UsbException("attachStandardDrivers() must not be called while the device is open"); + + // default implementation: do nothing + } + + protected void checkIsOpen() { + if (!isOpened()) + throw new UsbException("device needs to be opened first for this operation"); + } + + protected void checkIsClosed(String message) { + if (!connected) + throw new UsbException("device has been disconnected"); + if (isOpened()) + throw new UsbException(message); + } + + protected synchronized void disconnect() { + connected = false; + close(); + } + + @Override + public int getProductId() { + return pid; + } + + @Override + public int getVendorId() { + return vid; + } + + @Override + public String getProduct() { + return productString; + } + + @Override + public String getManufacturer() { + return manufacturerString; + } + + @Override + public String getSerialNumber() { + return serialString; + } + + @Override + public int getClassCode() { + return deviceClass; + } + + @Override + public int getSubclassCode() { + return deviceSubclass; + } + + @Override + public int getProtocolCode() { + return deviceProtocol; + } + + @Override + public @NotNull Version getUsbVersion() { + return versionUsb; + } + + @Override + public @NotNull Version getDeviceVersion() { + return versionDevice; + } + + @Override + public byte @NotNull [] getConfigurationDescriptor() { + return rawConfigurationDescriptor; + } + + @Override + public byte @NotNull [] getDeviceDescriptor() { + return rawDeviceDescriptor; + } + + public Object getUniqueId() { + return uniqueDeviceId; + } + + @Override + public boolean isConnected() { + return connected; + } + + /** + * Sets the class codes and version for the device descriptor. + * + * @param descriptor the device descriptor + */ + public void setFromDeviceDescriptor(MemorySegment descriptor) { + rawDeviceDescriptor = descriptor.toArray(JAVA_BYTE); + var deviceDescriptor = new DeviceDescriptor(descriptor); + deviceClass = deviceDescriptor.deviceClass() & 255; + deviceSubclass = deviceDescriptor.deviceSubClass() & 255; + deviceProtocol = deviceDescriptor.deviceProtocol() & 255; + versionUsb = new Version(deviceDescriptor.usbVersion()); + versionDevice = new Version(deviceDescriptor.deviceVersion()); + } + + /** + * Sets the configuration descriptor and derives the interface and endpoint descriptions. + * + * @param descriptor configuration descriptor + * @return parsed configuration + */ + protected Configuration setConfigurationDescriptor(MemorySegment descriptor) { + rawConfigurationDescriptor = descriptor.toArray(JAVA_BYTE); + var configuration = ConfigurationParser.parseConfigurationDescriptor(descriptor); + interfaceList = configuration.interfaces(); + interfaceList.sort(Comparator.comparingInt(UsbInterface::getNumber)); + return configuration; + } + + /** + * Set the product strings. + * + * @param manufacturer manufacturer name + * @param product product name + * @param serialNumber serial number + */ + public void setProductStrings(String manufacturer, String product, String serialNumber) { + manufacturerString = manufacturer; + productString = product; + serialString = serialNumber; + } + + /** + * Sets the product strings from the device descriptor. + *

+ * To look up the string, a lookup function is provided. It takes the + * string ID and returns the string from the string descriptor. + *

+ * + * @param descriptor device descriptor + * @param stringLookup string lookup function + */ + public void setProductString(MemorySegment descriptor, IntFunction stringLookup) { + var deviceDescriptor = new DeviceDescriptor(descriptor); + manufacturerString = stringLookup.apply(deviceDescriptor.iManufacturer()); + productString = stringLookup.apply(deviceDescriptor.iProduct()); + serialString = stringLookup.apply(deviceDescriptor.iSerialNumber()); + } + + public void setClassCodes(int classCode, int subclassCode, int protocolCode) { + deviceClass = classCode; + deviceSubclass = subclassCode; + deviceProtocol = protocolCode; + } + + public void setVersions(int usbVersion, int deviceVersion) { + versionUsb = new Version(usbVersion); + versionDevice = new Version(deviceVersion); + } + + @Override + public @NotNull List getInterfaces() { + return Collections.unmodifiableList(interfaceList); + } + + public void setClaimed(int interfaceNumber, boolean claimed) { + for (var intf : interfaceList) { + if (intf.getNumber() == interfaceNumber) { + ((UsbInterfaceImpl) intf).setClaimed(claimed); + return; + } + } + throw new UsbException("internal error (interface not found)"); + } + + @Override + public @NotNull UsbInterfaceImpl getInterface(int interfaceNumber) { + return (UsbInterfaceImpl) interfaceList.stream() + .filter(intf -> intf.getNumber() == interfaceNumber).findFirst() + .orElseThrow(() -> new UsbException(String.format("USB device has no interface %d", interfaceNumber))); + } + + public UsbInterfaceImpl getInterfaceWithCheck(int interfaceNumber, boolean isClaimed) { + var intf = getInterface(interfaceNumber); + if (isClaimed && !intf.isClaimed()) { + throw new UsbException(String.format("interface %d must be claimed first", interfaceNumber)); + } else if (!isClaimed && intf.isClaimed()) { + throw new UsbException(String.format("interface %d has already been claimed", interfaceNumber)); + } + return intf; + } + + @Override + public @NotNull UsbEndpoint getEndpoint(UsbDirection direction, int endpointNumber) { + for (var intf : interfaceList) { + for (var endpoint : intf.getCurrentAlternate().getEndpoints()) { + if (endpoint.getDirection() == direction && endpoint.getNumber() == endpointNumber) + return endpoint; + } + } + throw new UsbException(String.format("endpoint %d (%s) does not exist", endpointNumber, direction.name())); + } + + /** + * Checks if the specified endpoint is valid for communication and returns the endpoint. + * + * @param direction transfer direction + * @param endpointNumber endpoint number (1 to 127) + * @param transferType1 transfer type 1 + * @param transferType2 transfer type 2 (or {@code null}) + * @return endpoint + */ + @SuppressWarnings("java:S3776") + protected EndpointInfo getEndpoint(UsbDirection direction, int endpointNumber, UsbTransferType transferType1, + UsbTransferType transferType2) { + + checkIsOpen(); + + if (endpointNumber >= 1 && endpointNumber <= 127) { + for (var intf : interfaceList) { + if (intf.isClaimed()) { + for (var ep : intf.getCurrentAlternate().getEndpoints()) { + if (ep.getNumber() == endpointNumber && ep.getDirection() == direction + && (ep.getTransferType() == transferType1 || ep.getTransferType() == transferType2)) + return new EndpointInfo(intf.getNumber(), ep.getNumber(), + (byte) (endpointNumber | (direction == UsbDirection.IN ? 0x80 : 0)), + ep.getPacketSize(), ep.getTransferType()); + } + } + } + } + + throwInvalidEndpointException(direction, endpointNumber, transferType1, transferType2); + return null; // will never be reached + } + + protected void throwInvalidEndpointException(UsbDirection direction, int endpointNumber, + UsbTransferType transferType1, UsbTransferType transferType2) { + String transferTypeDesc; + if (transferType2 == null) + transferTypeDesc = transferType1.name(); + else + transferTypeDesc = String.format("%s or %s", transferType1.name(), transferType2.name()); + throw new UsbException(String.format( + "endpoint number %d does not exist, is not part of a claimed interface or is not valid for %s transfer in %s direction", + endpointNumber, transferTypeDesc, direction.name())); + } + + protected int getInterfaceNumber(UsbDirection direction, int endpointNumber) { + if (endpointNumber < 1 || endpointNumber > 127) + return -1; + + for (var intf : interfaceList) { + if (intf.isClaimed()) { + for (var ep : intf.getCurrentAlternate().getEndpoints()) { + if (ep.getNumber() == endpointNumber && ep.getDirection() == direction) + return intf.getNumber(); + } + } + } + + return -1; + } + + @Override + public void transferOut(int endpointNumber, byte @NotNull [] data) { + transferOut(endpointNumber, data, 0, data.length, 0); + } + + @Override + public void transferOut(int endpointNumber, byte @NotNull [] data, int timeout) { + transferOut(endpointNumber, data, 0, data.length, timeout); + } + + @Override + public byte @NotNull [] transferIn(int endpointNumber) { + return transferIn(endpointNumber, 0); + } + + protected void waitForTransfer(Transfer transfer, int timeout, UsbDirection direction, int endpointNumber) { + if (timeout <= 0) { + waitNoTimeout(transfer); + + } else { + var hasTimedOut = waitWithTimeout(transfer, timeout); + + // test for timeout + if (hasTimedOut && transfer.resultCode() == 0) { + abortTransfers(direction, endpointNumber); + + // Wait for the abort's completion, but bounded: if it never arrives (device + // vanished such that neither the transfer nor the abort yields a callback), + // abandon the transfer instead of blocking forever. Abandoning is safe because + // buffers reaching this path come from an auto arena and survive a late completion. + var abortCompleted = !waitWithTimeout(transfer, (int) ABORT_COMPLETION_TIMEOUT_MS); + if (!abortCompleted) + LOG.log(WARNING, "abort completion for {0} did not arrive within {1} ms - abandoning transfer", + getOperationDescription(direction, endpointNumber), ABORT_COMPLETION_TIMEOUT_MS); + + throw new UsbTimeoutException(getOperationDescription(direction, endpointNumber) + " aborted due to timeout"); + } + } + + // test for error + if (transfer.resultCode() != 0) { + var operation = getOperationDescription(direction, endpointNumber); + throwOSException(transfer.resultCode(), operation + " failed"); + } + } + + @SuppressWarnings({"java:S2273", "java:S2142"}) + private static void waitNoTimeout(Transfer transfer) { + // wait for transfer. + // Defer interruption: keep a local flag instead of re-asserting the interrupt + // inside the loop (which would make the next wait() throw immediately and + // busy-spin). Re-assert once the transfer has actually completed. + var wasInterrupted = false; + while (transfer.resultSize() == -1) { + try { + transfer.wait(); + } catch (InterruptedException _) { + wasInterrupted = true; + } + } + if (wasInterrupted) + Thread.currentThread().interrupt(); + } + + @SuppressWarnings({"java:S2273", "java:S2142"}) + private static boolean waitWithTimeout(Transfer transfer, int timeout) { + // wait for transfer to complete, or abort when timeout occurs. + // Defer interruption: keep a local flag instead of re-asserting the interrupt + // inside the loop (which would make the next wait() throw immediately and + // busy-spin). The remaining timeout is recomputed on both the normal and the + // interrupted path, so the wait stays bounded by the original expiration. + var expiration = System.currentTimeMillis() + timeout; + long remainingTimeout = timeout; + var wasInterrupted = false; + while (remainingTimeout > 0 && transfer.resultSize() == -1) { + try { + transfer.wait(remainingTimeout); + } catch (InterruptedException _) { + wasInterrupted = true; + } + remainingTimeout = expiration - System.currentTimeMillis(); + } + + if (wasInterrupted) + Thread.currentThread().interrupt(); + + return remainingTimeout <= 0; + } + + protected static String getOperationDescription(UsbDirection direction, int endpointNumber) { + if (endpointNumber == 0) { + return "control transfer"; + } else { + return String.format("transfer %s on endpoint %d", direction.name(), endpointNumber); + } + + } + + /** + * Create a transfer object suitable for this device. + * + * @return transfer object + */ + protected abstract Transfer createTransfer(); + + /** + * Completion handler used for synchronous, blocking transfers. + *

+ * Calls {@link Object#notify()} so the caller can use + * {@link Object#wait()} to wait for completion. + *

+ * + * @param transfer the transfer that has completed + */ + @SuppressWarnings({"java:S2445", "java:S2446"}) + protected static void onSyncTransferCompleted(Transfer transfer) { + synchronized (transfer) { + transfer.notify(); + } + } + + /** + * Throws an exception for the specified operating-specific error code. + * + * @param errorCode error code, operating specific + * @param message exception message format ({@link String#format(String, Object...)} style) + * @param args arguments for exception message + */ + protected abstract void throwOSException(int errorCode, String message, Object... args); + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + var that = (UsbDeviceImpl) o; + return uniqueDeviceId.equals(that.uniqueDeviceId); + } + + @Override + public int hashCode() { + return uniqueDeviceId.hashCode(); + } + + @Override + public String toString() { + return String.format("VID: 0x%04x, PID: 0x%04x, manufacturer: %s, product: %s, serial: %s, ID: %s", + vid, pid, manufacturerString, productString, serialString, uniqueDeviceId); + } + + public record EndpointInfo(int interfaceNumber, int endpointNumber, byte endpointAddress, int packetSize, + UsbTransferType transferType) { + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/common/USBDeviceRegistry.java b/java-does-usb/src/main/java/net/codecrete/usb/common/UsbDeviceRegistry.java similarity index 60% rename from java-does-usb/src/main/java/net/codecrete/usb/common/USBDeviceRegistry.java rename to java-does-usb/src/main/java/net/codecrete/usb/common/UsbDeviceRegistry.java index a7818bfe..ed7999af 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/common/USBDeviceRegistry.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/common/UsbDeviceRegistry.java @@ -7,17 +7,19 @@ package net.codecrete.usb.common; -import net.codecrete.usb.USBDevice; -import net.codecrete.usb.USBException; +import net.codecrete.usb.UsbDevice; +import net.codecrete.usb.UsbException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; +import static java.lang.System.Logger.Level.INFO; +import static java.lang.System.Logger.Level.WARNING; + /** * Base class for USB device registry. *

@@ -30,12 +32,16 @@ * and builds the initial device list. *

*/ -public abstract class USBDeviceRegistry { +@SuppressWarnings("java:S3077") +public abstract class UsbDeviceRegistry { + + private static final System.Logger LOG = System.getLogger(UsbDeviceRegistry.class.getName()); - private volatile List devices; - private volatile Throwable failureCause; - protected Consumer onDeviceConnectedHandler; - protected Consumer onDeviceDisconnectedHandler; + private List devices; + private Throwable failureCause; + // volatile: set by the application thread, read by the device monitor thread + protected volatile Consumer onDeviceConnectedHandler; + protected volatile Consumer onDeviceDisconnectedHandler; private final Lock lock = new ReentrantLock(); private final Condition enumerationComplete = lock.newCondition(); @@ -69,26 +75,43 @@ public void start() { * * @return list of devices */ - public List getAllDevices() { - return Collections.unmodifiableList(devices); + public synchronized List getAllDevices() { + return devices; } - public void setOnDeviceConnected(Consumer handler) { + public void setOnDeviceConnected(Consumer handler) { onDeviceConnectedHandler = handler; } - public void setOnDeviceDisconnected(Consumer handler) { + public void setOnDeviceDisconnected(Consumer handler) { onDeviceDisconnectedHandler = handler; } - protected void emitOnDeviceConnected(USBDevice device) { - if (onDeviceConnectedHandler != null) - onDeviceConnectedHandler.accept(device); + protected void emitOnDeviceConnected(UsbDevice device) { + // read once so a concurrent setOnDeviceConnected(null) cannot fail between check and call + var handler = onDeviceConnectedHandler; + if (handler == null) + return; + + try { + handler.accept(device); + + } catch (Exception e) { + LOG.log(WARNING, "unhandled exception in 'onDeviceConnected' handler - ignoring", e); + } } - protected void emitOnDeviceDisconnected(USBDevice device) { - if (onDeviceDisconnectedHandler != null) - onDeviceDisconnectedHandler.accept(device); + protected void emitOnDeviceDisconnected(UsbDevice device) { + var handler = onDeviceDisconnectedHandler; + if (handler == null) + return; + + try { + handler.accept(device); + + } catch (Exception e) { + LOG.log(WARNING, "unhandled exception in 'onDeviceDisconnected' handler - ignoring", e); + } } /** @@ -102,7 +125,7 @@ protected void emitOnDeviceDisconnected(USBDevice device) { */ protected void startDeviceMonitor(Runnable monitorTask) { // start new thread - Thread t = new Thread(monitorTask, "USB device monitor"); + var t = new Thread(monitorTask, "USB device monitor"); t.setDaemon(true); t.start(); @@ -117,7 +140,7 @@ protected void startDeviceMonitor(Runnable monitorTask) { } if (failureCause != null) - throw new USBException("Initial device enumeration has failed", failureCause); + throw new UsbException("initial device enumeration has failed", failureCause); } /** @@ -150,8 +173,10 @@ protected void enumerationFailed(Throwable e) { * * @param deviceList the device list */ - protected void setInitialDeviceList(List deviceList) { - devices = deviceList; + protected void setInitialDeviceList(List deviceList) { + synchronized (this) { + devices = deviceList; + } signalEnumerationComplete(); } @@ -160,31 +185,33 @@ protected void setInitialDeviceList(List deviceList) { * * @param device device to add */ - protected void addDevice(USBDevice device) { - // check for duplicates - if (findDeviceIndex(devices, ((USBDeviceImpl) device).getUniqueId()) >= 0) - return; + protected void addDevice(UsbDevice device) { + synchronized (this) { + // check for duplicates + if (findDeviceIndex(devices, ((UsbDeviceImpl) device).getUniqueId()) >= 0) + return; - // copy list - var newDeviceList = new ArrayList(devices.size() + 1); - newDeviceList.addAll(devices); - newDeviceList.add(device); - devices = newDeviceList; + // copy list + var newDeviceList = new ArrayList(devices.size() + 1); + newDeviceList.addAll(devices); + newDeviceList.add(device); + devices = newDeviceList; + } // send notification emitOnDeviceConnected(device); } + @SuppressWarnings("java:S106") protected void closeAndRemoveDevice(Object deviceId) { var device = findDevice(deviceId); if (device == null) return; try { - device.close(); - } catch (Throwable e) { - System.err.println("Info: [JavaDoesUSB] failed to close USB device - ignoring exception"); - e.printStackTrace(System.err); + ((UsbDeviceImpl) device).disconnect(); + } catch (Exception e) { + LOG.log(INFO, "failed to close disconnected USB device - ignoring exception", e); } removeDevice(deviceId); @@ -196,16 +223,19 @@ protected void closeAndRemoveDevice(Object deviceId) { * @param deviceId the unique ID of the device to remove */ protected void removeDevice(Object deviceId) { - // locate device to be removed - int index = findDeviceIndex(devices, deviceId); - if (index < 0) - return; // strange + UsbDevice device; + synchronized (this) { + // locate device to be removed + int index = findDeviceIndex(devices, deviceId); + if (index < 0) + return; // strange - // copy list and remove device - var device = devices.get(index); - var newDeviceList = new ArrayList<>(devices); - newDeviceList.remove(index); - devices = newDeviceList; + // copy list and remove device + device = devices.get(index); + var newDeviceList = new ArrayList<>(devices); + newDeviceList.remove(index); + devices = newDeviceList; + } // send notification emitOnDeviceDisconnected(device); @@ -218,9 +248,9 @@ protected void removeDevice(Object deviceId) { * @param deviceId the unique device ID * @return return the index, or -1 if the device is not found */ - protected int findDeviceIndex(List deviceList, Object deviceId) { + protected int findDeviceIndex(List deviceList, Object deviceId) { for (int i = 0; i < deviceList.size(); i++) { - var dev = (USBDeviceImpl) deviceList.get(i); + var dev = (UsbDeviceImpl) deviceList.get(i); if (deviceId.equals(dev.getUniqueId())) return i; } @@ -233,7 +263,7 @@ protected int findDeviceIndex(List deviceList, Object deviceId) { * @param deviceId the unique device ID * @return return device, or {@code null} if not found. */ - protected USBDevice findDevice(Object deviceId) { + protected UsbDevice findDevice(Object deviceId) { int index = findDeviceIndex(devices, deviceId); if (index < 0) return null; diff --git a/java-does-usb/src/main/java/net/codecrete/usb/common/UsbEndpointImpl.java b/java-does-usb/src/main/java/net/codecrete/usb/common/UsbEndpointImpl.java new file mode 100644 index 00000000..f40525ea --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/common/UsbEndpointImpl.java @@ -0,0 +1,50 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.common; + +import net.codecrete.usb.UsbDirection; +import net.codecrete.usb.UsbEndpoint; +import net.codecrete.usb.UsbTransferType; + +/** + * Implementation of {@code UsbEndpoint} interface. + */ +public class UsbEndpointImpl implements UsbEndpoint { + + private final int endpointNumber; + private final UsbDirection transferDirection; + private final UsbTransferType type; + private final int maxPacketSize; + + public UsbEndpointImpl(int number, UsbDirection direction, UsbTransferType type, int packetSize) { + endpointNumber = number; + transferDirection = direction; + this.type = type; + maxPacketSize = packetSize; + } + + @Override + public int getNumber() { + return endpointNumber; + } + + @Override + public UsbDirection getDirection() { + return transferDirection; + } + + @Override + public UsbTransferType getTransferType() { + return type; + } + + @Override + public int getPacketSize() { + return maxPacketSize; + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/common/UsbInterfaceImpl.java b/java-does-usb/src/main/java/net/codecrete/usb/common/UsbInterfaceImpl.java new file mode 100644 index 00000000..64697e87 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/common/UsbInterfaceImpl.java @@ -0,0 +1,75 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.common; + +import net.codecrete.usb.UsbAlternateInterface; +import net.codecrete.usb.UsbException; +import net.codecrete.usb.UsbInterface; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class UsbInterfaceImpl implements UsbInterface { + + private final int interfaceNumber; + private UsbAlternateInterface currentAlternate; + private final List alternateInterfaces; + + private boolean claimed; + + public UsbInterfaceImpl(int number, List alternates) { + interfaceNumber = number; + alternateInterfaces = alternates; + currentAlternate = alternates.getFirst(); + alternateInterfaces.sort(Comparator.comparingInt(UsbAlternateInterface::getNumber)); + } + + @Override + public int getNumber() { + return interfaceNumber; + } + + @Override + public boolean isClaimed() { + return claimed; + } + + public void setClaimed(boolean claimed) { + this.claimed = claimed; + } + + @Override + public @NotNull UsbAlternateInterface getCurrentAlternate() { + return currentAlternate; + } + + @Override + public @NotNull UsbAlternateInterface getAlternate(int alternateNumber) { + return alternateInterfaces.stream() + .filter(alt -> alt.getNumber() == alternateNumber).findFirst() + .orElseThrow(() -> new UsbException(String.format( + "Interface %d does not have an alternate interface setting %d", + interfaceNumber, alternateNumber) + )); + } + + @Override + public @NotNull List getAlternates() { + return Collections.unmodifiableList(alternateInterfaces); + } + + void addAlternate(UsbAlternateInterface alt) { + alternateInterfaces.add(alt); + } + + public void setAlternate(UsbAlternateInterface alternate) { + currentAlternate = alternate; + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/EPoll.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/EPoll.java new file mode 100644 index 00000000..38b9d5bb --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/EPoll.java @@ -0,0 +1,140 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.linux; + +import net.codecrete.usb.linux.gen.errno.errno; + +import java.lang.foreign.Arena; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.GroupLayout; +import java.lang.foreign.Linker; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.VarHandle; + +import static java.lang.foreign.ValueLayout.ADDRESS; +import static java.lang.foreign.ValueLayout.ADDRESS_UNALIGNED; +import static java.lang.foreign.ValueLayout.JAVA_INT; +import static java.lang.foreign.ValueLayout.JAVA_INT_UNALIGNED; +import static java.lang.foreign.ValueLayout.JAVA_LONG_UNALIGNED; +import static net.codecrete.usb.linux.Linux.allocateErrorState; +import static net.codecrete.usb.linux.LinuxUsbException.throwLastError; +import static net.codecrete.usb.linux.gen.epoll.epoll.EPOLL_CTL_ADD; +import static net.codecrete.usb.linux.gen.epoll.epoll.EPOLL_CTL_DEL; + +@SuppressWarnings({"OptionalGetWithoutIsPresent", "SameParameterValue", "java:S100", "java:S1192"}) +public class EPoll { + private EPoll() { } + + private static final boolean IS_AARCH64 = System.getProperty("os.arch").equals("aarch64"); + + private static final GroupLayout DATA$LAYOUT = MemoryLayout.unionLayout( + ADDRESS_UNALIGNED.withName("ptr"), + JAVA_INT_UNALIGNED.withName("fd"), + JAVA_INT_UNALIGNED.withName("u32"), + JAVA_LONG_UNALIGNED.withName("u64") + ).withName("epoll_data"); + + static final GroupLayout EVENT$LAYOUT = IS_AARCH64 ? + MemoryLayout.structLayout( + JAVA_INT.withName("events"), + MemoryLayout.paddingLayout(4), + DATA$LAYOUT.withName("data") + ).withName("epoll_event") : + MemoryLayout.structLayout( + JAVA_INT_UNALIGNED.withName("events"), + DATA$LAYOUT.withName("data") + ).withName("epoll_event"); + + // varhandle to access the "fd" field in an epoll_event array + static final VarHandle EVENT_ARRAY_DATA_FD$VH = EVENT$LAYOUT.arrayElementVarHandle( + MemoryLayout.PathElement.groupElement("data"), + MemoryLayout.PathElement.groupElement("fd") + ); + + // varhandle to access the "fd" field in an epoll_event struct + private static final VarHandle EVENT_DATA_FD$VH = EVENT$LAYOUT.varHandle( + MemoryLayout.PathElement.groupElement("data"), + MemoryLayout.PathElement.groupElement("fd") + ); + + private static final VarHandle EVENTS$VH = EVENT$LAYOUT.varHandle( + MemoryLayout.PathElement.groupElement("events") + ); + + private static final Linker linker = Linker.nativeLinker(); + + private static final FunctionDescriptor epoll_create1$FUNC = FunctionDescriptor.of(JAVA_INT, JAVA_INT); + private static final MethodHandle epoll_create1$MH = linker.downcallHandle(linker.defaultLookup().find( + "epoll_create").get(), epoll_create1$FUNC, Linux.ERRNO_STATE); + + private static final FunctionDescriptor epoll_ctl$FUNC = FunctionDescriptor.of(JAVA_INT, JAVA_INT, JAVA_INT, JAVA_INT, ADDRESS); + private static final MethodHandle epoll_ctl$MH = linker.downcallHandle(linker.defaultLookup().find( + "epoll_ctl").get(), epoll_ctl$FUNC, Linux.ERRNO_STATE); + + private static final FunctionDescriptor epoll_wait$FUNC = FunctionDescriptor.of(JAVA_INT, JAVA_INT, ADDRESS, JAVA_INT, JAVA_INT); + private static final MethodHandle epoll_wait$MH = linker.downcallHandle(linker.defaultLookup().find( + "epoll_wait").get(), epoll_wait$FUNC, Linux.ERRNO_STATE); + + static int epoll_create1(int flags, MemorySegment errno) { + try { + return (int) epoll_create1$MH.invokeExact(errno, flags); + } catch (Throwable ex) { + throw new AssertionError(ex); + } + } + + private static int epoll_ctl(int epfd, int op, int fd, MemorySegment event, MemorySegment errno) { + try { + return (int) epoll_ctl$MH.invokeExact(errno, epfd, op, fd, event); + } catch (Throwable ex) { + throw new AssertionError(ex); + } + } + + static int epoll_wait(int epfd, MemorySegment events, int maxevent, int timeout, MemorySegment errno) { + try { + return (int) epoll_wait$MH.invokeExact(errno, epfd, events, maxevent, timeout); + } catch (Throwable ex) { + throw new AssertionError(ex); + } + } + + static void addFileDescriptor(int epfd, int op, int fd) { + try (var arena = Arena.ofConfined()) { + var errorState = allocateErrorState(arena); + + var event = arena.allocate(EVENT$LAYOUT); + EVENTS$VH.set(event, 0, op); + EVENT_DATA_FD$VH.set(event, 0, fd); + var ret = epoll_ctl(epfd, EPOLL_CTL_ADD(), fd, event, errorState); + if (ret < 0) + throwLastError(errorState, "internal error (epoll_ctl_add)"); + } + } + + static void removeFileDescriptor(int epfd, int fd) { + try (var arena = Arena.ofConfined()) { + var errorState = allocateErrorState(arena); + + var event = arena.allocate(EVENT$LAYOUT); + EVENTS$VH.set(event, 0, 0); + EVENT_DATA_FD$VH.set(event, 0, fd); + var ret = epoll_ctl(epfd, EPOLL_CTL_DEL(), fd, event, errorState); + if (ret < 0) { + var err = Linux.getErrno(errorState); + // ignore ENOENT as this method might be called twice when cleaning up, + // and EBADF as the file descriptor might have been closed concurrently + // (closing deregisters it from epoll anyway) + if (err != errno.ENOENT() && err != errno.EBADF()) + throwLastError(errorState, "internal error (epoll_ctl_del)"); + } + } + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/IO.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/IO.java index 68792634..367deb80 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/IO.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/IO.java @@ -7,23 +7,42 @@ package net.codecrete.usb.linux; -import net.codecrete.usb.linux.gen.errno.errno; - -import java.lang.foreign.MemoryAddress; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.invoke.MethodHandle; +import static java.lang.foreign.ValueLayout.ADDRESS; import static java.lang.foreign.ValueLayout.JAVA_INT; +import static java.lang.foreign.ValueLayout.JAVA_LONG; + +@SuppressWarnings({"OptionalGetWithoutIsPresent", "SameParameterValue", "java:S100"}) +class IO { + + private IO() { + } -public class IO { + private static final Linker linker = Linker.nativeLinker(); + private static final FunctionDescriptor ioctl$FUNC = FunctionDescriptor.of(JAVA_INT, JAVA_INT, JAVA_LONG, ADDRESS); + private static final MethodHandle ioctl$MH = linker.downcallHandle(linker.defaultLookup().find("ioctl").get(), + ioctl$FUNC, Linux.ERRNO_STATE, Linker.Option.firstVariadicArg(2)); + private static final FunctionDescriptor open$FUNC = FunctionDescriptor.of(JAVA_INT, ADDRESS, JAVA_INT); + private static final MethodHandle open$MH = linker.downcallHandle(linker.defaultLookup().find("open").get(), + open$FUNC, Linux.ERRNO_STATE); + + static int ioctl(int fd, long request, MemorySegment segment, MemorySegment errno) { + try { + return (int) ioctl$MH.invokeExact(errno, fd, request, segment); + } catch (Throwable ex) { + throw new AssertionError(ex); + } + } - public static int getErrno() { - try (var session = MemorySession.openConfined()) { - var location = (MemoryAddress) errno.__errno_location(); - var errnoSegment = MemorySegment.ofAddress(location, JAVA_INT.byteSize(), session); - return errnoSegment.get(JAVA_INT, 0); - } catch (Throwable e) { - throw new RuntimeException(e); + static int open(MemorySegment file, int oflag, MemorySegment errno) { + try { + return (int) open$MH.invokeExact(errno, file, oflag); + } catch (Throwable ex) { + throw new AssertionError(ex); } } } diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/Linux.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/Linux.java index 8d759c0e..ab494b91 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/Linux.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/Linux.java @@ -7,30 +7,55 @@ package net.codecrete.usb.linux; -import java.lang.foreign.MemoryAddress; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import net.codecrete.usb.linux.gen.string.string; -import static java.lang.foreign.MemoryAddress.NULL; +import java.lang.foreign.Arena; +import java.lang.foreign.Linker; +import java.lang.foreign.MemoryLayout.PathElement; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.StructLayout; +import java.lang.invoke.VarHandle; /** * Helper functions for Linux */ -public class Linux { +class Linux { + + private Linux() { + } + + /** + * Call state for capturing the {@code errno} value. + */ + static final Linker.Option ERRNO_STATE = Linker.Option.captureCallState("errno"); + private static final StructLayout ERRNO_STATE_LAYOUT = Linker.Option.captureStateLayout(); + private static final VarHandle callState_errno$VH = + ERRNO_STATE_LAYOUT.varHandle(PathElement.groupElement("errno")); + + static MemorySegment allocateErrorState(Arena arena) { + return arena.allocate(ERRNO_STATE_LAYOUT.byteSize()); + } + + /** + * Gets the error message for the specified error code (returned by {@code errno}). + * + * @param err error code + * @return error message + */ + static String getErrorMessage(int err) { + return string.strerror(err).getString(0); + } /** - * Creates a Java string as a copy of the null-terminated UTF-8 string. + * Gets the error code from the memory segment. + *

+ * The memory segment is assumed to have the layout {@link #ERRNO_STATE}. + *

* - * @param str the memory address pointing to string - * @return the Java string, or {@code null} if the memory address is 0 + * @param errorState memory segment with error code + * @return error code */ - public static String createStringFromAddress(MemoryAddress str) { - try (var session = MemorySession.openConfined()) { - if (str == NULL) - return null; - - var ret = MemorySegment.ofAddress(str, 2000, session); - return ret.getUtf8String(0); - } + static int getErrno(MemorySegment errorState) { + return (int) callState_errno$VH.get(errorState, 0); } } diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxAsyncTask.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxAsyncTask.java new file mode 100644 index 00000000..3d6bf71f --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxAsyncTask.java @@ -0,0 +1,399 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.linux; + +import net.codecrete.usb.UsbException; +import net.codecrete.usb.UsbTransferType; +import net.codecrete.usb.linux.gen.errno.errno; +import net.codecrete.usb.linux.gen.usbdevice_fs.usbdevfs_urb; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static java.lang.System.Logger.Level.ERROR; +import static java.lang.foreign.ValueLayout.ADDRESS; +import static net.codecrete.usb.common.ForeignMemory.dereference; +import static net.codecrete.usb.linux.EPoll.epoll_create1; +import static net.codecrete.usb.linux.EPoll.epoll_wait; +import static net.codecrete.usb.linux.Linux.allocateErrorState; +import static net.codecrete.usb.linux.LinuxUsbException.throwException; +import static net.codecrete.usb.linux.LinuxUsbException.throwLastError; +import static net.codecrete.usb.linux.UsbDevFS.DISCARDURB; +import static net.codecrete.usb.linux.UsbDevFS.REAPURBNDELAY; +import static net.codecrete.usb.linux.UsbDevFS.SUBMITURB; +import static net.codecrete.usb.linux.gen.epoll.epoll.EPOLLOUT; +import static net.codecrete.usb.linux.gen.epoll.epoll.EPOLLWAKEUP; +import static net.codecrete.usb.linux.gen.errno.errno.EINTR; +import static net.codecrete.usb.linux.gen.errno.errno.ENODEV; +import static net.codecrete.usb.linux.gen.fcntl.fcntl.FD_CLOEXEC; +import static net.codecrete.usb.linux.gen.usbdevice_fs.usbdevice_fs.USBDEVFS_URB_TYPE_BULK; +import static net.codecrete.usb.linux.gen.usbdevice_fs.usbdevice_fs.USBDEVFS_URB_TYPE_CONTROL; +import static net.codecrete.usb.linux.gen.usbdevice_fs.usbdevice_fs.USBDEVFS_URB_TYPE_INTERRUPT; +import static net.codecrete.usb.linux.gen.usbdevice_fs.usbdevice_fs.USBDEVFS_URB_TYPE_ISO; + +/** + * Background task for handling asynchronous transfers. + *

+ * Each USB device must register its file handle with this task. + *

+ *

+ * The task keeps track of the submitted transfers by indexing them + * by URB address (USB request block). + *

+ *

+ * URBs are allocated but never freed. To limit the memory usage, + * URBs are reused. So the maximum number of outstanding transfers + * determines the number of allocated URBs. + *

+ */ +@SuppressWarnings("java:S6548") +class LinuxAsyncTask { + + private static final System.Logger LOG = System.getLogger(LinuxAsyncTask.class.getName()); + + /** + * Singleton instance of background task. + */ + static final LinuxAsyncTask INSTANCE = new LinuxAsyncTask(); + + private static final int NUM_EVENTS = 5; + + private final Arena urbArena = Arena.ofAuto(); + /// available URBs + private final List availableURBs = new ArrayList<>(); + /// map of URB addresses to transfer (for outstanding transfers) + private final Map transfersByURB = new LinkedHashMap<>(); + /// file descriptor of epoll + private int epollFd = -1; + /// indicates that the background task has terminated due to an unrecoverable error + private boolean taskTerminated; + + /** + * Background task for handling asynchronous IO completions. + *

+ * It polls on all registered file descriptors. If a file descriptor is + * ready, the URB is "reaped". + *

+ */ + @SuppressWarnings({"java:S2189", "java:S135", "java:S3776"}) + private void asyncCompletionTask() { + + try (var arena = Arena.ofConfined()) { + var errorState = allocateErrorState(arena); + var urbPointerHolder = arena.allocate(ADDRESS); + var events = arena.allocate(EPoll.EVENT$LAYOUT, NUM_EVENTS); + + while (true) { + try { + // wait for file descriptor to be ready + var res = epoll_wait(epollFd, events, NUM_EVENTS, -1, errorState); + if (res < 0) { + var err = Linux.getErrno(errorState); + if (err == EINTR()) + continue; // continue on interrupt + throwException(err, "internal error (epoll_wait)"); + } + + // for all ready file descriptors, reap URBs + for (int i = 0; i < res; i++) { + var fd = (int) EPoll.EVENT_ARRAY_DATA_FD$VH.get(events, 0L, i); + reapURBs(fd, urbPointerHolder, errorState); + } + + } catch (Exception e) { + LOG.log(ERROR, "USB async IO thread failed and is terminating; " + + "all outstanding transfers will fail, and no further transfers are possible", e); + failAllPendingTransfers(); + return; + } + } + } + } + + /** + * Fails all outstanding transfers and marks this task as terminated. + *

+ * Called when the background task can no longer dispatch completions. Waiters blocked + * on the failed transfers wake up with an error result instead of hanging forever, + * and future submissions are rejected. + *

+ */ + private void failAllPendingTransfers() { + var failedTransfers = new ArrayList(); + synchronized (this) { + taskTerminated = true; + for (var transfer : transfersByURB.values()) { + transfer.urb = null; + transfer.setResultCode(errno.ECANCELED()); + transfer.setResultSize(0); + failedTransfers.add(transfer); + } + transfersByURB.clear(); + availableURBs.clear(); + } + completeTransfers(failedTransfers); + } + + /** + * Reap all pending URBs and handle the completed transfers. + * + * @param fd file descriptor + * @param urbPointerHolder native memory to receive the URB pointer + * @param errorState native memory to receive the errno + */ + private void reapURBs(int fd, MemorySegment urbPointerHolder, MemorySegment errorState) { + + var completedTransfers = new ArrayList(); + try { + synchronized (this) { + while (true) { + var res = IO.ioctl(fd, REAPURBNDELAY, urbPointerHolder, errorState); + if (res < 0) { + var err = Linux.getErrno(errorState); + if (err == errno.EAGAIN()) + return; // no more pending URBs + if (err == errno.EBADF()) + return; // file descriptor was closed concurrently (deregisters it from epoll) + if (err == errno.ENODEV()) { + // device might have been unplugged + EPoll.removeFileDescriptor(epollFd, fd); + return; + } + // Unexpected error: stop handling this device's completions but keep + // the task alive for all other devices. The file descriptor must be + // deregistered, or epoll would report it ready again immediately, + // resulting in a hot loop. + LOG.log(ERROR, "reaping URBs for file descriptor {0} failed with errno {1}; " + + "no further transfers will complete for this device", fd, err); + EPoll.removeFileDescriptor(epollFd, fd); + return; + } + + var urb = dereference(urbPointerHolder); + completedTransfers.add(getTransferWithResult(urb)); + } + } + } finally { + // Even if reaping fails, the already reaped transfers must be completed. + completeTransfers(completedTransfers); + } + } + + /** + * Calls the completion handlers of the specified transfers. + *

+ * Must be called without holding the lock: handlers acquire other monitors + * (transfer, device), and threads submitting transfers acquire this task's lock + * while holding those monitors, so calling handlers under the lock can deadlock. + *

+ * + * @param transfers completed transfers + */ + private void completeTransfers(List transfers) { + for (var transfer : transfers) { + try { + transfer.completion().completed(transfer); + } catch (Exception e) { + // This method also runs on the process-wide async IO thread. Any exception + // escaping would kill that thread and hang all async transfers for the + // entire library. + LOG.log(ERROR, "Unexpected exception while handling async IO completion", e); + } + } + } + + /** + * Register a device for asynchronous IO completion handling + * + * @param device USB device + */ + synchronized void addForAsyncIOCompletion(LinuxUsbDevice device) { + // start background process if needed + if (epollFd < 0) + startAsyncIOTask(); + + EPoll.addFileDescriptor(epollFd, EPOLLOUT() | EPOLLWAKEUP(), device.fileDescriptor()); + } + + /** + * Unregisters a device from asynchronous IO completion handling. + * + * @param device USB device + */ + void removeFromAsyncIOCompletion(LinuxUsbDevice device) { + int fd = device.fileDescriptor(); + + // remove file descriptor from epoll + synchronized (this) { + EPoll.removeFileDescriptor(epollFd, fd); + } + + // reap outstanding URBs + try (var arena = Arena.ofConfined()) { + var errorState = allocateErrorState(arena); + var urbPointerHolder = arena.allocate(ADDRESS); + reapURBs(fd, urbPointerHolder, errorState); + } + + // reclaim stale URBs + var staleTransfers = new ArrayList(); + synchronized (this) { + transfersByURB.entrySet().removeIf(e -> { + var urb = e.getKey(); + var isMatch = usbdevfs_urb.usercontext(urb).address() == fd; + if (isMatch) { + var transfer = e.getValue(); + transfer.urb = null; + transfer.setResultCode(ENODEV()); + transfer.setResultSize(0); + availableURBs.add(urb); + staleTransfers.add(transfer); + } + return isMatch; + }); + } + completeTransfers(staleTransfers); + } + + synchronized void submitTransfer(LinuxUsbDevice device, int endpointAddress, UsbTransferType transferType, LinuxTransfer transfer) { + if (taskTerminated) + throw new UsbException("USB async IO background thread has terminated due to an unrecoverable error; " + + "USB transfers are no longer possible"); + + linkToUrb(transfer); + var urb = transfer.urb; + + usbdevfs_urb.type(urb, (byte) urbTransferType(transferType)); + usbdevfs_urb.endpoint(urb, (byte) endpointAddress); + usbdevfs_urb.buffer(urb, transfer.data()); + usbdevfs_urb.buffer_length(urb, transfer.dataSize()); + usbdevfs_urb.usercontext(urb, MemorySegment.ofAddress(device.fileDescriptor())); + + try (var arena = Arena.ofConfined()) { + var errorState = allocateErrorState(arena); + if (IO.ioctl(device.fileDescriptor(), SUBMITURB, urb, errorState) < 0) { + submissionFailed(transfer); + var action = endpointAddress >= 128 ? "reading from" : "writing to"; + var endpoint = endpointAddress == 0 ? "control endpoint" : String.format("endpoint %d", endpointAddress); + throwLastError(errorState, "error occurred while %s %s", action, endpoint); + } + } + } + + /** + * Undoes the registration performed by {@link #linkToUrb(LinuxTransfer)}. + *

+ * Must be called if the {@code SUBMITURB} ioctl for a linked transfer fails. In that + * case, the kernel has not queued the URB and it will never be reaped, so its map + * entry would leak and the URB would never return to the pool unless they are + * cleaned up here. + *

+ * + * @param transfer transfer whose submission failed + */ + private void submissionFailed(LinuxTransfer transfer) { + transfersByURB.remove(transfer.urb); + availableURBs.add(transfer.urb); + transfer.urb = null; + } + + private static int urbTransferType(UsbTransferType transferType) { + return switch (transferType) { + case BULK -> USBDEVFS_URB_TYPE_BULK(); + case INTERRUPT -> USBDEVFS_URB_TYPE_INTERRUPT(); + case CONTROL -> USBDEVFS_URB_TYPE_CONTROL(); + case ISOCHRONOUS -> USBDEVFS_URB_TYPE_ISO(); + }; + } + + /** + * Links the specified transfer instance to a URB. + *

+ * The transfer is assigned a URB instance, and a list + * of associations from URB to transfer is maintained. + *

+ * @param transfer the transfer to assign a URB. + */ + private void linkToUrb(LinuxTransfer transfer) { + MemorySegment urb; + var size = availableURBs.size(); + if (size > 0) { + urb = availableURBs.remove(size - 1); + } else { + urb = usbdevfs_urb.allocate(urbArena); + } + + transfer.urb = urb; + transfersByURB.put(urb, transfer); + } + + /** + * Gets the transfer associated with the specified URB and adds the result. + *

+ * The URB is returned into the list of URBs available for further transfers. + *

+ * + * @param urb URB instance + * @return transfer associated with the URB + */ + @SuppressWarnings("java:S2259") + private synchronized LinuxTransfer getTransferWithResult(MemorySegment urb) { + var transfer = transfersByURB.remove(urb); + if (transfer == null) + throwException("internal error (unknown URB)"); + + transfer.setResultCode(-usbdevfs_urb.status(transfer.urb)); + transfer.setResultSize(usbdevfs_urb.actual_length(transfer.urb)); + + availableURBs.add(transfer.urb); + transfer.urb = null; + + return transfer; + } + + @SuppressWarnings("java:S1066") + synchronized void abortTransfers(LinuxUsbDevice device, byte endpointAddress) { + var fd = device.fileDescriptor(); + try (var arena = Arena.ofConfined()) { + + var errorState = allocateErrorState(arena); + + // iterate all URBs and discard the ones for the specified endpoint + transfersByURB.keySet().stream() + .filter(urb -> + usbdevfs_urb.usercontext(urb).address() == fd + && usbdevfs_urb.endpoint(urb) == endpointAddress) + .forEach(urb -> { + if (IO.ioctl(fd, DISCARDURB, urb, errorState) < 0) { + // ignore EINVAL; it occurs if the URB has completed at the same time + if (Linux.getErrno(errorState) != errno.EINVAL()) + throwLastError(errorState, "error occurred while aborting transfer"); + } + } + ); + } + } + + private void startAsyncIOTask() { + try (var arena = Arena.ofConfined()) { + var errorState = allocateErrorState(arena); + epollFd = epoll_create1(FD_CLOEXEC(), errorState); + if (epollFd < 0) + throwLastError(errorState, "internal error (epoll_create)"); + } + + // start background thread for handling IO completion + var thread = new Thread(this::asyncCompletionTask, "USB async IO"); + thread.setDaemon(true); + thread.start(); + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxEndpointInputStream.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxEndpointInputStream.java new file mode 100644 index 00000000..2f48a0e6 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxEndpointInputStream.java @@ -0,0 +1,24 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.linux; + +import net.codecrete.usb.UsbDirection; +import net.codecrete.usb.common.EndpointInputStream; +import net.codecrete.usb.common.Transfer; + +public class LinuxEndpointInputStream extends EndpointInputStream { + + LinuxEndpointInputStream(LinuxUsbDevice device, int endpointNumber, int bufferSize) { + super(device, endpointNumber, bufferSize); + } + + @Override + protected void submitTransferIn(Transfer transfer) { + ((LinuxUsbDevice) device).submitTransfer(UsbDirection.IN, endpointNumber, (LinuxTransfer) transfer); + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxEndpointOutputStream.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxEndpointOutputStream.java new file mode 100644 index 00000000..024b8da0 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxEndpointOutputStream.java @@ -0,0 +1,24 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.linux; + +import net.codecrete.usb.UsbDirection; +import net.codecrete.usb.common.EndpointOutputStream; +import net.codecrete.usb.common.Transfer; + +public class LinuxEndpointOutputStream extends EndpointOutputStream { + + LinuxEndpointOutputStream(LinuxUsbDevice device, int endpointNumber, int bufferSize) { + super(device, endpointNumber, bufferSize); + } + + @Override + protected void submitTransferOut(Transfer transfer) { + ((LinuxUsbDevice) device).submitTransfer(UsbDirection.OUT, endpointNumber, (LinuxTransfer) transfer); + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxTransfer.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxTransfer.java new file mode 100644 index 00000000..4e40a5e6 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxTransfer.java @@ -0,0 +1,22 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.linux; + +import net.codecrete.usb.common.Transfer; +import net.codecrete.usb.linux.gen.usbdevice_fs.usbdevfs_urb; + +import java.lang.foreign.MemorySegment; + +public class LinuxTransfer extends Transfer { + /** + * USB request block. + * + * @see usbdevfs_urb + */ + MemorySegment urb; +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxUSBDevice.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxUSBDevice.java deleted file mode 100644 index fcae2330..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxUSBDevice.java +++ /dev/null @@ -1,229 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// - -package net.codecrete.usb.linux; - -import net.codecrete.usb.USBControlTransfer; -import net.codecrete.usb.USBDirection; -import net.codecrete.usb.USBException; -import net.codecrete.usb.USBTransferType; -import net.codecrete.usb.common.DescriptorParser; -import net.codecrete.usb.common.USBDescriptors; -import net.codecrete.usb.common.USBDeviceImpl; -import net.codecrete.usb.common.USBInterfaceImpl; -import net.codecrete.usb.linux.gen.fcntl.fcntl; -import net.codecrete.usb.linux.gen.ioctl.ioctl; -import net.codecrete.usb.linux.gen.unistd.unistd; -import net.codecrete.usb.linux.gen.usbdevice_fs.usbdevfs_bulktransfer; -import net.codecrete.usb.linux.gen.usbdevice_fs.usbdevfs_ctrltransfer; - -import java.io.IOException; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; -import java.nio.file.Files; -import java.nio.file.Path; - -import static java.lang.foreign.ValueLayout.JAVA_BYTE; -import static java.lang.foreign.ValueLayout.JAVA_INT; - -public class LinuxUSBDevice extends USBDeviceImpl { - - private int fd = -1; - - LinuxUSBDevice(Object id, int vendorId, int productId, String manufacturer, String product, String serial) { - super(id, vendorId, productId, manufacturer, product, serial); - loadDescription((String) id); - } - - private void loadDescription(String path) { - byte[] descriptors; - try { - descriptors = Files.readAllBytes(Path.of(path)); - } catch (IOException e) { - throw new USBException("Cannot read configuration descriptor", e); - } - - // `descriptors` contains the device descriptor followed by the configuration descriptor - // (including the interface descriptors, endpoint descriptors etc.) - - try (var session = MemorySession.openConfined()) { - var descriptorsSegment = MemorySegment.ofArray(descriptors); - - // separate device descriptor (and copy it to fix alignment issues) - var deviceDesc = session.allocate(USBDescriptors.Device$Struct); - deviceDesc.copyFrom(descriptorsSegment.asSlice(0, USBDescriptors.Device$Struct.byteSize())); - - int classCode = 255 & (byte) USBDescriptors.Device_bDeviceClass.get(deviceDesc); - int subclassCode = 255 & (byte) USBDescriptors.Device_bDeviceSubClass.get(deviceDesc); - int protocolCode = 255 & (byte) USBDescriptors.Device_bDeviceProtocol.get(deviceDesc); - setClassCodes(classCode, subclassCode, protocolCode); - - var usbVersion = (short) USBDescriptors.Device_bcdUSB.get(deviceDesc); - var deviceVersion = (short) USBDescriptors.Device_bcdDevice.get(deviceDesc); - setVersions(usbVersion, deviceVersion); - - // skip device descriptor - var configDesc = session.allocateArray(JAVA_BYTE, descriptors.length - 18); - configDesc.copyFrom(descriptorsSegment.asSlice(USBDescriptors.Device$Struct.byteSize())); - var configuration = DescriptorParser.parseConfigurationDescriptor(configDesc, vendorId(), productId()); - setInterfaces(configuration.interfaces); - } - } - - @Override - public boolean isOpen() { - return fd != -1; - } - - @Override - public void open() { - if (isOpen()) - throw new USBException("the device is already open"); - - try (var session = MemorySession.openConfined()) { - var pathUtf8 = session.allocateUtf8String(id_.toString()); - fd = fcntl.open(pathUtf8, fcntl.O_RDWR() | fcntl.O_CLOEXEC()); - if (fd == -1) - throw new USBException("Cannot open USB device", IO.getErrno()); - } - } - - @Override - public void close() { - if (!isOpen()) - return; - - for (var intf : interfaces_) - ((USBInterfaceImpl) intf).setClaimed(false); - - unistd.close(fd); - fd = -1; - } - - public void claimInterface(int interfaceNumber) { - checkIsOpen(); - - var intf = getInterface(interfaceNumber); - if (intf == null) - throw new USBException(String.format("Invalid interface number: %d", interfaceNumber)); - if (intf.isClaimed()) - throw new USBException(String.format("Interface %d has already been claimed", interfaceNumber)); - - try (var session = MemorySession.openConfined()) { - var intfNumSegment = session.allocate(JAVA_INT, interfaceNumber); - int ret = ioctl.ioctl(fd, USBDevFS.CLAIMINTERFACE, intfNumSegment.address()); - if (ret != 0) - throw new USBException("Cannot claim USB interface", IO.getErrno()); - setClaimed(interfaceNumber, true); - } - } - - public void releaseInterface(int interfaceNumber) { - checkIsOpen(); - - var intf = getInterface(interfaceNumber); - if (intf == null) - throw new USBException(String.format("Invalid interface number: %d", interfaceNumber)); - if (!intf.isClaimed()) - throw new USBException(String.format("Interface %d has not been claimed", interfaceNumber)); - - try (var session = MemorySession.openConfined()) { - var intfNumSegment = session.allocate(JAVA_INT, interfaceNumber); - int ret = ioctl.ioctl(fd, USBDevFS.RELEASEINTERFACE, intfNumSegment.address()); - if (ret != 0) - throw new USBException("Cannot release USB interface", IO.getErrno()); - setClaimed(interfaceNumber, false); - } - } - - private MemorySegment createCtrlTransfer(MemorySession session, USBDirection direction, USBControlTransfer setup, - MemorySegment data) { - var ctrlTransfer = session.allocate(usbdevfs_ctrltransfer.$LAYOUT()); - var bmRequest = - (direction == USBDirection.IN ? 0x80 : 0) | (setup.requestType().ordinal() << 5) | setup.recipient().ordinal(); - usbdevfs_ctrltransfer.bRequestType$set(ctrlTransfer, (byte) bmRequest); - usbdevfs_ctrltransfer.bRequest$set(ctrlTransfer, setup.request()); - usbdevfs_ctrltransfer.wValue$set(ctrlTransfer, setup.value()); - usbdevfs_ctrltransfer.wIndex$set(ctrlTransfer, setup.index()); - usbdevfs_ctrltransfer.wLength$set(ctrlTransfer, (short) data.byteSize()); - usbdevfs_ctrltransfer.data$set(ctrlTransfer, data.address()); - return ctrlTransfer; - } - - @Override - public byte[] controlTransferIn(USBControlTransfer setup, int length) { - try (var session = MemorySession.openConfined()) { - var data = session.allocate(length); - var ctrlTransfer = createCtrlTransfer(session, USBDirection.IN, setup, data); - - int res = ioctl.ioctl(fd, USBDevFS.CONTROL, ctrlTransfer.address()); - if (res < 0) - throw new USBException("Control IN transfer failed", IO.getErrno()); - - return data.asSlice(0, res).toArray(JAVA_BYTE); - } - } - - @Override - public void controlTransferOut(USBControlTransfer setup, byte[] data) { - try (var session = MemorySession.openConfined()) { - int dataLength = data != null ? data.length : 0; - var buffer = session.allocate(dataLength); - if (dataLength != 0) - buffer.copyFrom(MemorySegment.ofArray(data)); - var ctrlTransfer = createCtrlTransfer(session, USBDirection.OUT, setup, buffer); - - int res = ioctl.ioctl(fd, USBDevFS.CONTROL, ctrlTransfer.address()); - if (res < 0) - throw new USBException("Control OUT transfer failed", IO.getErrno()); - } - } - - private MemorySegment createBulkTransfer(MemorySession session, byte endpointAddress, MemorySegment data) { - var transfer = session.allocate(usbdevfs_bulktransfer.$LAYOUT()); - usbdevfs_bulktransfer.ep$set(transfer, 255 & endpointAddress); - usbdevfs_bulktransfer.len$set(transfer, (int) data.byteSize()); - usbdevfs_bulktransfer.data$set(transfer, data.address()); - return transfer; - } - - @Override - public void transferOut(int endpointNumber, byte[] data) { - var endpointAddress = getEndpointAddress(endpointNumber, USBDirection.OUT, - USBTransferType.BULK, USBTransferType.INTERRUPT); - - try (var session = MemorySession.openConfined()) { - var buffer = session.allocate(data.length); - buffer.copyFrom(MemorySegment.ofArray(data)); - var transfer = createBulkTransfer(session, endpointAddress, buffer); - - int res = ioctl.ioctl(fd, USBDevFS.BULK, transfer.address()); - if (res < 0) - throw new USBException(String.format("USB OUT transfer on endpoint %d failed", endpointNumber), - IO.getErrno()); - } - } - - @Override - public byte[] transferIn(int endpointNumber, int maxLength) { - var endpointAddress = getEndpointAddress(endpointNumber, USBDirection.IN, - USBTransferType.BULK, USBTransferType.INTERRUPT); - - try (var session = MemorySession.openConfined()) { - var buffer = session.allocate(maxLength); - - var transfer = createBulkTransfer(session, endpointAddress, buffer); - - int res = ioctl.ioctl(fd, USBDevFS.BULK, transfer.address()); - if (res < 0) - throw new USBException(String.format("USB IN transfer on endpoint %d failed", endpointNumber), - IO.getErrno()); - - return buffer.asSlice(0, res).toArray(JAVA_BYTE); - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxUSBDeviceRegistry.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxUSBDeviceRegistry.java deleted file mode 100644 index 2a793579..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxUSBDeviceRegistry.java +++ /dev/null @@ -1,240 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// - -package net.codecrete.usb.linux; - -import net.codecrete.usb.USBDevice; -import net.codecrete.usb.USBException; -import net.codecrete.usb.common.USBDeviceRegistry; -import net.codecrete.usb.linux.gen.select.fd_set; -import net.codecrete.usb.linux.gen.select.select; -import net.codecrete.usb.linux.gen.udev.udev; - -import java.lang.foreign.Addressable; -import java.lang.foreign.MemoryAddress; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; -import java.util.ArrayList; -import java.util.List; - -import static java.lang.foreign.MemoryAddress.NULL; -import static java.lang.foreign.ValueLayout.JAVA_LONG; - -/** - * Linux implementation of USB device registry. - */ -public class LinuxUSBDeviceRegistry extends USBDeviceRegistry { - - private static final MemorySegment SUBSYSTEM_USB = MemorySession.global().allocateUtf8String("usb"); - private static final MemorySegment MONITOR_NAME = MemorySession.global().allocateUtf8String("udev"); - private static final MemorySegment DEVTYPE_USB_DEVICE = MemorySession.global().allocateUtf8String("usb_device"); - - @Override - protected void monitorDevices() { - - int fd; - MemoryAddress monitor; - - try { - // setup udev monitor - var udevInstance = udev.udev_new(); - if (udevInstance == NULL) - throw new USBException("internal error (udev_new)"); - - monitor = udev.udev_monitor_new_from_netlink(udevInstance, MONITOR_NAME); - if (monitor == NULL) - throw new USBException("internal error (udev_monitor_new_from_netlink)"); - - if (udev.udev_monitor_filter_add_match_subsystem_devtype(monitor, SUBSYSTEM_USB, DEVTYPE_USB_DEVICE) < 0) - throw new USBException("internal error (udev_monitor_filter_add_match_subsystem_devtype)"); - - if (udev.udev_monitor_enable_receiving(monitor) < 0) - throw new USBException("internal error (udev_monitor_enable_receiving)"); - - fd = udev.udev_monitor_get_fd(monitor); - if (fd < 0) - throw new USBException("internal error (udev_monitor_get_fd)"); - - // create initial list of devices - var deviceList = enumeratePresentDevices(udevInstance); - setInitialDeviceList(deviceList); - - } catch (Throwable e) { - enumerationFailed(e); - return; - } - - // monitor device changes - //noinspection InfiniteLoopStatement - while (true) { - try (var session = MemorySession.openConfined()) { - - // wait for next change - waitForFileDescriptor(fd, session); - - // retrieve change - var udevDevice = udev.udev_monitor_receive_device(monitor); - if (udevDevice == null) - continue; // shouldn't happen - - session.addCloseAction(() -> udev.udev_device_unref(udevDevice)); - - // get details - var action = getDeviceAction(udevDevice); - - if ("add".equals(action)) { - onDeviceConnected(udevDevice); - } else if ("remove".equals(action)) { - onDeviceDisconnected(udevDevice); - } - } - } - } - - private List enumeratePresentDevices(Addressable udevInstance) { - List result = new ArrayList<>(); - try (var outerSession = MemorySession.openConfined()) { - - // create device enumerator - var enumerate = udev.udev_enumerate_new(udevInstance); - if (enumerate == NULL) - throw new USBException("internal error (udev_enumerate_new)"); - - outerSession.addCloseAction(() -> udev.udev_enumerate_unref(enumerate)); - - if (udev.udev_enumerate_add_match_subsystem(enumerate, SUBSYSTEM_USB) < 0) - throw new USBException("internal error (udev_enumerate_add_match_subsystem)"); - - if (udev.udev_enumerate_scan_devices(enumerate) < 0) - throw new USBException("internal error (udev_enumerate_scan_devices)"); - - // enumerate devices - for (var entry = udev.udev_enumerate_get_list_entry(enumerate); entry != NULL; entry = - udev.udev_list_entry_get_next(entry)) { - - try (var session = MemorySession.openConfined()) { - - var path = udev.udev_list_entry_get_name(entry); - if (path == NULL) - continue; - - // get device handle - var dev = udev.udev_device_new_from_syspath(udevInstance, path); - if (dev == NULL) - continue; - - // ensure the device is released - session.addCloseAction(() -> udev.udev_device_unref(dev)); - - // get device details - var device = getDeviceDetails(dev); - if (device != null) - result.add(device); - } - } - } - - return result; - } - - private void onDeviceConnected(MemoryAddress udevDevice) { - - var device = getDeviceDetails(udevDevice); - if (device != null) - addDevice(device); - } - - private void onDeviceDisconnected(MemoryAddress udevDevice) { - - var devPath = getDeviceName(udevDevice); - if (devPath == null) - return; - - closeAndRemoveDevice(devPath); - } - - /** - * Retrieves the device details and returns a {@code USBDevice} instance. - *

- * If the device is missing one of vendor ID, product ID or device path, - * {@code null} is returned. - *

- * - * @param udevDevice the device (udev_device*) - * @return the device instance - */ - private static USBDevice getDeviceDetails(MemoryAddress udevDevice) { - - int vendorId = 0; - int productId = 0; - - try { - // retrieve device attributes - String idVendor = getDeviceAttribute(udevDevice, "idVendor"); - if (idVendor == null) - return null; - - String idProduct = getDeviceAttribute(udevDevice, "idProduct"); - if (idProduct == null) - return null; - - // get device path - var devPath = getDeviceName(udevDevice); - if (devPath == null) - return null; - - vendorId = Integer.parseInt(idVendor, 16); - productId = Integer.parseInt(idProduct, 16); - - // create device instance - return new LinuxUSBDevice(devPath, vendorId, productId, getDeviceAttribute(udevDevice, "manufacturer"), - getDeviceAttribute(udevDevice, "product"), getDeviceAttribute(udevDevice, "serial")); - - } catch (Throwable e) { - System.err.printf("Info: [JavaDoesUSB] failed to retrieve information about device 0x%04x/0x%04x - " + - "ignoring device%n", vendorId, productId); - e.printStackTrace(System.err); - return null; - } - } - - private static String getDeviceAttribute(Addressable udevDevice, String attribute) { - try (var session = MemorySession.openConfined()) { - var sysattr = session.allocateUtf8String(attribute); - var valueAddr = udev.udev_device_get_sysattr_value(udevDevice, sysattr); - if (valueAddr == NULL) - return null; - - var value = MemorySegment.ofAddress(valueAddr, 2000, session); - return value.getUtf8String(0); - } - } - - private static String getDeviceName(Addressable udevDevice) { - return Linux.createStringFromAddress(udev.udev_device_get_devnode(udevDevice)); - } - - private static String getDeviceAction(Addressable udevDevice) { - return Linux.createStringFromAddress(udev.udev_device_get_action(udevDevice)); - } - - /** - * Waits until the specified file descriptor becomes ready for reading. - * - * @param fd the file descriptor - * @param session a memory session for allocating memory - */ - private static void waitForFileDescriptor(int fd, MemorySession session) { - // fd_set is a bit array (constructed from 64-bit integers) - var fds = session.allocate(fd_set.$LAYOUT()); - fds.set(JAVA_LONG, fd / JAVA_LONG.bitSize(), 1L << (fd % JAVA_LONG.bitSize())); - - int res = select.select(fd + 1, fds, NULL, NULL, NULL); - if (res <= 0) - throw new USBException("internal error (select)"); - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxUsbDevice.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxUsbDevice.java new file mode 100644 index 00000000..d0c87f09 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxUsbDevice.java @@ -0,0 +1,358 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.linux; + +import net.codecrete.usb.UsbControlTransfer; +import net.codecrete.usb.UsbDirection; +import net.codecrete.usb.UsbException; +import net.codecrete.usb.UsbTransferType; +import net.codecrete.usb.common.Transfer; +import net.codecrete.usb.common.UsbDeviceImpl; +import net.codecrete.usb.common.UsbInterfaceImpl; +import net.codecrete.usb.linux.gen.fcntl.fcntl; +import net.codecrete.usb.linux.gen.unistd.unistd; +import net.codecrete.usb.linux.gen.usbdevice_fs.usbdevfs_disconnect_claim; +import net.codecrete.usb.linux.gen.usbdevice_fs.usbdevfs_ioctl; +import net.codecrete.usb.linux.gen.usbdevice_fs.usbdevfs_setinterface; +import net.codecrete.usb.linux.gen.usbdevice_fs.usbdevice_fs; +import net.codecrete.usb.usbstandard.DeviceDescriptor; +import net.codecrete.usb.usbstandard.SetupPacket; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.nio.file.Files; +import java.nio.file.Path; + +import static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static java.lang.foreign.ValueLayout.JAVA_INT; +import static net.codecrete.usb.linux.Linux.allocateErrorState; +import static net.codecrete.usb.linux.LinuxUsbException.throwException; +import static net.codecrete.usb.linux.LinuxUsbException.throwLastError; + +@SuppressWarnings("java:S2160") +public class LinuxUsbDevice extends UsbDeviceImpl { + + private static final MemorySegment DRIVER_NAME_USBFS = Arena.global().allocateFrom("usbfs"); + + // volatile: written under the device monitor, read unlocked via isOpened() + private volatile int fd = -1; + + private final LinuxAsyncTask asyncTask; + + private boolean detachDrivers = false; + + LinuxUsbDevice(Object id, int vendorId, int productId) { + super(id, vendorId, productId); + asyncTask = LinuxAsyncTask.INSTANCE; + loadDescription((String) id); + } + + private void loadDescription(String path) { + byte[] descriptors; + try { + descriptors = Files.readAllBytes(Path.of(path)); + } catch (IOException e) { + throw new UsbException("reading configuration descriptor failed", e); + } + + // `descriptors` contains the device descriptor followed by the configuration descriptor + // (including the interface descriptors, endpoint descriptors etc.) + var descriptorsSegment = MemorySegment.ofArray(descriptors); + setFromDeviceDescriptor(descriptorsSegment.asSlice(0, DeviceDescriptor.LAYOUT)); + setConfigurationDescriptor(descriptorsSegment.asSlice(DeviceDescriptor.LAYOUT.byteSize())); + } + + @Override + public synchronized void detachStandardDrivers() { + checkIsClosed("detachStandardDrivers() must not be called while the device is open"); + detachDrivers = true; + } + + @Override + public synchronized void attachStandardDrivers() { + checkIsClosed("attachStandardDrivers() must not be called while the device is open"); + detachDrivers = false; + } + + @Override + public boolean isOpened() { + return fd != -1; + } + + @Override + public synchronized void open() { + checkIsClosed("device is already open"); + + try (var arena = Arena.ofConfined()) { + var pathUtf8 = arena.allocateFrom(uniqueDeviceId.toString()); + var errorState = allocateErrorState(arena); + fd = IO.open(pathUtf8, fcntl.O_RDWR() | fcntl.O_CLOEXEC(), errorState); + if (fd == -1) + throwLastError(errorState, "opening USB device failed"); + asyncTask.addForAsyncIOCompletion(this); + } + } + + @Override + public synchronized void close() { + if (!isOpened()) + return; + + asyncTask.removeFromAsyncIOCompletion(this); + + for (var intf : interfaceList) + ((UsbInterfaceImpl) intf).setClaimed(false); + + unistd.close(fd); + fd = -1; + } + + int fileDescriptor() { + return fd; + } + + public synchronized void claimInterface(int interfaceNumber) { + checkIsOpen(); + + getInterfaceWithCheck(interfaceNumber, false); + + try (var arena = Arena.ofConfined()) { + var errorState = allocateErrorState(arena); + int ret; + + if (detachDrivers) { + // claim interface (detaching kernel driver) + var disconnectClaim = usbdevfs_disconnect_claim.allocate(arena); + usbdevfs_disconnect_claim.interface_(disconnectClaim, interfaceNumber); + usbdevfs_disconnect_claim.flags(disconnectClaim, usbdevice_fs.USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER()); + usbdevfs_disconnect_claim.driver(disconnectClaim).copyFrom(DRIVER_NAME_USBFS); + ret = IO.ioctl(fd, UsbDevFS.DISCONNECT_CLAIM, disconnectClaim, errorState); + + } else { + // claim interface (without detaching kernel driver) + var intfNumSegment = arena.allocate(JAVA_INT); + intfNumSegment.setAtIndex(JAVA_INT, 0, interfaceNumber); + ret = IO.ioctl(fd, UsbDevFS.CLAIMINTERFACE, intfNumSegment, errorState); + } + + if (ret != 0) + throwLastError(errorState, "claiming USB interface failed"); + + setClaimed(interfaceNumber, true); + } + } + + @Override + public synchronized void selectAlternateSetting(int interfaceNumber, int alternateNumber) { + checkIsOpen(); + + var intf = getInterfaceWithCheck(interfaceNumber, true); + + // check alternate setting + var altSetting = intf.getAlternate(alternateNumber); + + try (var arena = Arena.ofConfined()) { + var setIntfSegment = usbdevfs_setinterface.allocate(arena); + usbdevfs_setinterface.interface_(setIntfSegment, interfaceNumber); + usbdevfs_setinterface.altsetting(setIntfSegment, alternateNumber); + var errorState = allocateErrorState(arena); + var ret = IO.ioctl(fd, UsbDevFS.SETINTERFACE, setIntfSegment, errorState); + if (ret != 0) + throwLastError(errorState, "setting alternate interface failed"); + } + + intf.setAlternate(altSetting); + } + + public synchronized void releaseInterface(int interfaceNumber) { + checkIsOpen(); + + getInterfaceWithCheck(interfaceNumber, true); + + try (var arena = Arena.ofConfined()) { + var intfNumSegment = arena.allocate(JAVA_INT); + intfNumSegment.setAtIndex(JAVA_INT, 0, interfaceNumber); + var errorState = allocateErrorState(arena); + var ret = IO.ioctl(fd, UsbDevFS.RELEASEINTERFACE, intfNumSegment, errorState); + if (ret != 0) + throwLastError(errorState, "releasing USB interface failed"); + + setClaimed(interfaceNumber, false); + + if (detachDrivers) { + // reattach kernel driver + var request = usbdevfs_ioctl.allocate(arena); + usbdevfs_ioctl.ifno(request, interfaceNumber); + usbdevfs_ioctl.ioctl_code(request, UsbDevFS.CONNECT); + usbdevfs_ioctl.data(request, MemorySegment.NULL); + IO.ioctl(fd, UsbDevFS.IOCTL, request, errorState); + } + } + } + + @Override + public void controlTransferOut(@NotNull UsbControlTransfer setup, byte[] data) { + try (var arena = Arena.ofConfined()) { + var dataLength = data != null ? data.length : 0; + var transfer = createSyncCtrlTransfer(arena, UsbDirection.OUT, setup, dataLength); + if (dataLength != 0) + transfer.data().asSlice(8).copyFrom(MemorySegment.ofArray(data)); + + synchronized (transfer) { + submitTransfer(UsbDirection.OUT, 0, transfer); + waitForTransfer(transfer, 0, UsbDirection.OUT, 0); + } + } + } + + @Override + public byte @NotNull [] controlTransferIn(@NotNull UsbControlTransfer setup, int length) { + try (var arena = Arena.ofConfined()) { + var transfer = createSyncCtrlTransfer(arena, UsbDirection.IN, setup, length); + + synchronized (transfer) { + submitTransfer(UsbDirection.IN, 0, transfer); + waitForTransfer(transfer, 0, UsbDirection.IN, 0); + } + + return transfer.data().asSlice(8, transfer.resultSize()).toArray(JAVA_BYTE); + } + } + + /** + * Create transfer object for synchronous control request. + * + * @param arena arena for allocating memory + * @param direction direction + * @param setup setup data + * @param dataLength data length (in addition to setup data) + * @return transfer object + */ + private LinuxTransfer createSyncCtrlTransfer(Arena arena, UsbDirection direction, UsbControlTransfer setup, + int dataLength) { + var bmRequest = + (direction == UsbDirection.IN ? 0x80 : 0) | (setup.requestType().ordinal() << 5) | setup.recipient().ordinal(); + var buffer = arena.allocate(8L + dataLength, 8); + var setupPacket = new SetupPacket(buffer); + setupPacket.setRequestType(bmRequest); + setupPacket.setRequest(setup.request()); + setupPacket.setValue(setup.value()); + setupPacket.setIndex(setup.index()); + setupPacket.setLength(dataLength); + + var transfer = new LinuxTransfer(); + transfer.setData(buffer); + transfer.setDataSize((int) buffer.byteSize()); + transfer.setResultSize(-1); + transfer.setCompletion(UsbDeviceImpl::onSyncTransferCompleted); + + return transfer; + } + + @Override + public void transferOut(int endpointNumber, byte @NotNull [] data, int offset, int length, int timeout) { + // Auto arena: a transfer that times out may be abandoned (see UsbDeviceImpl.waitForTransfer), + // so the buffer must outlive a possible late completion instead of being freed deterministically. + var arena = Arena.ofAuto(); + var buffer = arena.allocate(length); + buffer.copyFrom(MemorySegment.ofArray(data).asSlice(offset, length)); + var transfer = createSyncTransfer(buffer); + + synchronized (transfer) { + submitTransfer(UsbDirection.OUT, endpointNumber, transfer); + waitForTransfer(transfer, timeout, UsbDirection.OUT, endpointNumber); + } + } + + @Override + public byte @NotNull [] transferIn(int endpointNumber, int timeout) { + var endpoint = getEndpoint(UsbDirection.IN, endpointNumber, UsbTransferType.BULK, UsbTransferType.INTERRUPT); + + // Auto arena: a transfer that times out may be abandoned (see UsbDeviceImpl.waitForTransfer), + // so the buffer must outlive a possible late completion instead of being freed deterministically. + var arena = Arena.ofAuto(); + var buffer = arena.allocate(endpoint.packetSize()); + var transfer = createSyncTransfer(buffer); + + synchronized (transfer) { + submitTransfer(UsbDirection.IN, endpointNumber, transfer); + waitForTransfer(transfer, timeout, UsbDirection.IN, endpointNumber); + } + + return buffer.asSlice(0, transfer.resultSize()).toArray(JAVA_BYTE); + } + + private LinuxTransfer createSyncTransfer(MemorySegment data) { + var transfer = new LinuxTransfer(); + transfer.setData(data); + transfer.setDataSize((int) data.byteSize()); + transfer.setResultSize(-1); + transfer.setCompletion(UsbDeviceImpl::onSyncTransferCompleted); + return transfer; + } + + synchronized void submitTransfer(UsbDirection direction, int endpointNumber, LinuxTransfer transfer) { + if (endpointNumber != 0) { + var endpoint = getEndpoint(direction, endpointNumber, UsbTransferType.BULK, UsbTransferType.INTERRUPT); + asyncTask.submitTransfer(this, endpoint.endpointAddress(), endpoint.transferType(), transfer); + } else { + asyncTask.submitTransfer(this, 0, UsbTransferType.CONTROL, transfer); + } + } + + @Override + protected Transfer createTransfer() { + return new LinuxTransfer(); + } + + @Override + protected void throwOSException(int errorCode, String message, Object... args) { + throwException(errorCode, message, args); + } + + @Override + public void clearHalt(UsbDirection direction, int endpointNumber) { + var endpoint = getEndpoint(direction, endpointNumber, UsbTransferType.BULK, UsbTransferType.INTERRUPT); + + try (var arena = Arena.ofConfined()) { + var endpointAddrSegment = arena.allocate(JAVA_INT); + endpointAddrSegment.setAtIndex(JAVA_INT, 0, endpoint.endpointAddress() & 0xff); + var errorState = allocateErrorState(arena); + var res = IO.ioctl(fd, UsbDevFS.CLEAR_HALT, endpointAddrSegment, errorState); + if (res < 0) + throwLastError(errorState, "clearing halt failed"); + } + } + + @Override + public synchronized void abortTransfers(UsbDirection direction, int endpointNumber) { + var endpoint = getEndpoint(direction, endpointNumber, UsbTransferType.BULK, UsbTransferType.INTERRUPT); + + asyncTask.abortTransfers(this, endpoint.endpointAddress()); + } + + @Override + public synchronized @NotNull InputStream openInputStream(int endpointNumber, int bufferSize) { + // check that endpoint number is valid + getEndpoint(UsbDirection.IN, endpointNumber, UsbTransferType.BULK, null); + + return new LinuxEndpointInputStream(this, endpointNumber, bufferSize); + } + + @Override + public synchronized @NotNull OutputStream openOutputStream(int endpointNumber, int bufferSize) { + // check that endpoint number is valid + getEndpoint(UsbDirection.OUT, endpointNumber, UsbTransferType.BULK, null); + + return new LinuxEndpointOutputStream(this, endpointNumber, bufferSize); + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxUsbDeviceRegistry.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxUsbDeviceRegistry.java new file mode 100644 index 00000000..6c39abe9 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxUsbDeviceRegistry.java @@ -0,0 +1,274 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.linux; + +import net.codecrete.usb.UsbDevice; +import net.codecrete.usb.common.ScopeCleanup; +import net.codecrete.usb.common.UsbDeviceRegistry; +import net.codecrete.usb.linux.gen.udev.udev; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.util.ArrayList; +import java.util.List; + +import static java.lang.System.Logger.Level.INFO; +import static java.lang.foreign.MemorySegment.NULL; +import static net.codecrete.usb.linux.EPoll.epoll_create1; +import static net.codecrete.usb.linux.EPoll.epoll_wait; +import static net.codecrete.usb.linux.Linux.allocateErrorState; +import static net.codecrete.usb.linux.LinuxUsbException.throwException; +import static net.codecrete.usb.linux.LinuxUsbException.throwLastError; +import static net.codecrete.usb.linux.gen.epoll.epoll.EPOLLIN; +import static net.codecrete.usb.linux.gen.errno.errno.EINTR; +import static net.codecrete.usb.linux.gen.fcntl.fcntl.FD_CLOEXEC; + +/** + * Linux implementation of USB device registry. + */ +public class LinuxUsbDeviceRegistry extends UsbDeviceRegistry { + + private static final System.Logger LOG = System.getLogger(LinuxUsbDeviceRegistry.class.getName()); + + private static final MemorySegment SUBSYSTEM_USB; + private static final MemorySegment MONITOR_NAME; + private static final MemorySegment DEVTYPE_USB_DEVICE; + + private static final MemorySegment ATTR_ID_VENDOR; + private static final MemorySegment ATTR_ID_PRODUCT; + private static final MemorySegment ATTR_MANUFACTURER; + private static final MemorySegment ATTR_PRODUCT; + private static final MemorySegment ATTR_SERIAL; + + private MemorySegment monitor; + private int monitorFd; + + static { + var global = Arena.global(); + + SUBSYSTEM_USB = global.allocateFrom("usb"); + MONITOR_NAME = global.allocateFrom("udev"); + DEVTYPE_USB_DEVICE = global.allocateFrom("usb_device"); + + ATTR_ID_VENDOR = global.allocateFrom("idVendor"); + ATTR_ID_PRODUCT = global.allocateFrom("idProduct"); + ATTR_MANUFACTURER = global.allocateFrom("manufacturer"); + ATTR_PRODUCT = global.allocateFrom("product"); + ATTR_SERIAL = global.allocateFrom("serial"); + } + + @SuppressWarnings("java:S1181") + private boolean setupMonitor() { + try { + // setup udev monitor + var udevInstance = udev.udev_new(); + if (udevInstance.address() == 0) + throwException("internal error (udev_new)"); + + monitor = udev.udev_monitor_new_from_netlink(udevInstance, MONITOR_NAME); + if (monitor.address() == 0) + throwException("internal error (udev_monitor_new_from_netlink)"); + + if (udev.udev_monitor_filter_add_match_subsystem_devtype(monitor, SUBSYSTEM_USB, DEVTYPE_USB_DEVICE) < 0) + throwException("internal error (udev_monitor_filter_add_match_subsystem_devtype)"); + + if (udev.udev_monitor_enable_receiving(monitor) < 0) + throwException("internal error (udev_monitor_enable_receiving)"); + + monitorFd = udev.udev_monitor_get_fd(monitor); + if (monitorFd < 0) + throwException("internal error (udev_monitor_get_fd)"); + + // create initial list of devices + var deviceList = enumeratePresentDevices(udevInstance); + setInitialDeviceList(deviceList); + return true; + + } catch (Throwable e) { + enumerationFailed(e); + return false; + } + } + + @SuppressWarnings("java:S2189") + @Override + protected void monitorDevices() { + if (!setupMonitor()) + return; + + try (var arena = Arena.ofConfined()) { + // create epoll + var errorState = allocateErrorState(arena); + var epfd = epoll_create1(FD_CLOEXEC(), errorState); + if (epfd < 0) + throwLastError(errorState, "internal error (epoll_create)"); + EPoll.addFileDescriptor(epfd, EPOLLIN(), monitorFd); + + // allocate event (as output for epoll_wait) + var event = arena.allocate(EPoll.EVENT$LAYOUT); + + // monitor device changes + //noinspection InfiniteLoopStatement + while (true) { + try (var cleanup = new ScopeCleanup()) { + + // wait for next change + int res = epoll_wait(epfd, event, 1, -1, errorState); + if (res < 0) { + var err = Linux.getErrno(errorState); + if (err == EINTR()) + continue; // continue on interrupt + throwException(err, "internal error (epoll_wait)"); + } + + // retrieve change + var udevDevice = udev.udev_monitor_receive_device(monitor); + if (udevDevice != NULL) { + cleanup.add(() -> udev.udev_device_unref(udevDevice)); + + // get details + var action = getDeviceAction(udevDevice); + + if ("add".equals(action)) { + onDeviceConnected(udevDevice); + } else if ("remove".equals(action)) { + onDeviceDisconnected(udevDevice); + } + } + } + } + } + } + + @SuppressWarnings("java:S135") + private List enumeratePresentDevices(MemorySegment udevInstance) { + List result = new ArrayList<>(); + try (var outerCleanup = new ScopeCleanup()) { + + // create device enumerator + var enumerate = udev.udev_enumerate_new(udevInstance); + if (enumerate.address() == 0) + throwException("internal error (udev_enumerate_new)"); + + outerCleanup.add(() -> udev.udev_enumerate_unref(enumerate)); + + if (udev.udev_enumerate_add_match_subsystem(enumerate, SUBSYSTEM_USB) < 0) + throwException("internal error (udev_enumerate_add_match_subsystem)"); + + if (udev.udev_enumerate_scan_devices(enumerate) < 0) + throwException("internal error (udev_enumerate_scan_devices)"); + + // enumerate devices + for (var entry = udev.udev_enumerate_get_list_entry(enumerate); entry.address() != 0; entry = + udev.udev_list_entry_get_next(entry)) { + + try (var cleanup = new ScopeCleanup()) { + + var path = udev.udev_list_entry_get_name(entry); + if (path.address() == 0) + continue; + + // get device handle + var dev = udev.udev_device_new_from_syspath(udevInstance, path); + if (dev.address() == 0) + continue; + + // ensure the device is released + cleanup.add(() -> udev.udev_device_unref(dev)); + + // get device details + var device = getDeviceDetails(dev); + if (device != null) + result.add(device); + } + } + } + + return result; + } + + private void onDeviceConnected(MemorySegment udevDevice) { + + var device = getDeviceDetails(udevDevice); + if (device != null) + addDevice(device); + } + + private void onDeviceDisconnected(MemorySegment udevDevice) { + + var devPath = getDeviceName(udevDevice); + if (devPath == null) + return; + + closeAndRemoveDevice(devPath); + } + + /** + * Retrieves the device details and returns a {@code UsbDevice} instance. + *

+ * If the device is missing one of vendor ID, product ID or device path, + * {@code null} is returned. + *

+ * + * @param udevDevice the device (udev_device*) + * @return the device instance + */ + @SuppressWarnings("java:S106") + private UsbDevice getDeviceDetails(MemorySegment udevDevice) { + + int vendorId = 0; + int productId = 0; + + try { + // retrieve device attributes + String idVendor = getDeviceAttribute(udevDevice, ATTR_ID_VENDOR); + if (idVendor == null) + return null; + + String idProduct = getDeviceAttribute(udevDevice, ATTR_ID_PRODUCT); + if (idProduct == null) + return null; + + // get device path + var devPath = getDeviceName(udevDevice); + if (devPath == null) + return null; + + vendorId = Integer.parseInt(idVendor, 16); + productId = Integer.parseInt(idProduct, 16); + + // create device instance + var device = new LinuxUsbDevice(devPath, vendorId, productId); + + device.setProductStrings(getDeviceAttribute(udevDevice, ATTR_MANUFACTURER), getDeviceAttribute(udevDevice + , ATTR_PRODUCT), getDeviceAttribute(udevDevice, ATTR_SERIAL)); + + return device; + + } catch (Exception e) { + LOG.log(INFO, String.format("failed to retrieve information about device 0x%04x/0x%04x - ignoring device", vendorId, productId), e); + return null; + } + } + + private static String getDeviceAttribute(MemorySegment udevDevice, MemorySegment attribute) { + var value = udev.udev_device_get_sysattr_value(udevDevice, attribute); + if (value.address() == 0) + return null; + + return value.getString(0); + } + + private static String getDeviceName(MemorySegment udevDevice) { + return udev.udev_device_get_devnode(udevDevice).getString(0); + } + + private static String getDeviceAction(MemorySegment udevDevice) { + return udev.udev_device_get_action(udevDevice).getString(0); + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxUsbException.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxUsbException.java new file mode 100644 index 00000000..4f0d95c5 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxUsbException.java @@ -0,0 +1,77 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +package net.codecrete.usb.linux; + +import net.codecrete.usb.UsbException; +import net.codecrete.usb.UsbStallException; +import net.codecrete.usb.linux.gen.errno.errno; + +import java.lang.foreign.MemorySegment; + +/** + * Exception thrown if a Linux specific error occurs. + */ +public class LinuxUsbException extends UsbException { + + /** + * Creates a new instance. + *

+ * The message for the Linux error code is looked up and appended to the message. + *

+ * + * @param message exception message + * @param errorCode Linux error code (returned by {@code errno}) + */ + public LinuxUsbException(String message, int errorCode) { + super(String.format("%s: %s", message, Linux.getErrorMessage(errorCode)), errorCode); + } + + /** + * Throws an exception for the specified Linux error code. + *

+ * The message for the Linux error code is looked up and appended to the message. + *

+ * + * @param errorCode Linux error code (returned by {@code errno}) + * @param message exception message format ({@link String#format(String, Object...)} style) + * @param args arguments for exception message + */ + static void throwException(int errorCode, String message, Object... args) { + var formattedMessage = String.format(message, args); + if (errorCode == errno.EPIPE()) { + throw new UsbStallException(formattedMessage); + } else { + throw new LinuxUsbException(formattedMessage, errorCode); + } + } + + /** + * Throws a USB exception. + * + * @param message exception message format ({@link String#format(String, Object...)} style) + * @param args arguments for exception message + */ + static void throwException(String message, Object... args) { + throw new UsbException(String.format(message, args)); + } + + /** + * Throws an exception for the last error. + *

+ * The message of the last Linux error code is provided in a memory segment with the layout + * {@link Linux#ERRNO_STATE}. + *

+ * + * @param errorState segment with error state + * @param message exception message format ({@link String#format(String, Object...)} style) + * @param args arguments for exception message + */ + static void throwLastError(MemorySegment errorState, String message, Object... args) { + throwException(Linux.getErrno(errorState), message, args); + } + +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/USBDevFS.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/USBDevFS.java deleted file mode 100644 index 96b6c011..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/USBDevFS.java +++ /dev/null @@ -1,19 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// - -package net.codecrete.usb.linux; - -/** - * Data structures and constants related to the USB device file system. - */ -public class USBDevFS { - - public static final long CONTROL = 0xc0185500; - public static final long BULK = 0xc0185502; - public static final long CLAIMINTERFACE = 0x8004550F; - public static final long RELEASEINTERFACE = 0x80045510; -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/UsbDevFS.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/UsbDevFS.java new file mode 100644 index 00000000..15f1d270 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/UsbDevFS.java @@ -0,0 +1,33 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.linux; + +/** + * Data structures and constants related to the USB device file system. + *

+ * The usbdev_fs header files compute these constants using function like macros. + * Thus, they cannot be generated using jextract. + *

+ */ +class UsbDevFS { + + private UsbDevFS() { + } + + // constants that jextract cannot generate as they are built from function-like macros + static final long CLAIMINTERFACE = 0x8004550FL; + static final long RELEASEINTERFACE = 0x80045510L; + static final long SETINTERFACE = 0x80085504L; + static final long CLEAR_HALT = 0x80045515L; + static final long SUBMITURB = 0x8038550AL; + static final long DISCARDURB = 0x550BL; + static final long REAPURBNDELAY = 0x4008550DL; + static final long DISCONNECT_CLAIM = 0x8108551BL; + static final int CONNECT = 0x5517; + static final long IOCTL = 0xC0105512L; +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/epoll/epoll$shared.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/epoll/epoll$shared.java new file mode 100644 index 00000000..5cb27567 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/epoll/epoll$shared.java @@ -0,0 +1,63 @@ +// Generated by jextract + +package net.codecrete.usb.linux.gen.epoll; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +public class epoll$shared { + + epoll$shared() { + // Should not be called directly + } + + public static final ValueLayout.OfBoolean C_BOOL = (ValueLayout.OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool"); + public static final ValueLayout.OfByte C_CHAR =(ValueLayout.OfByte)Linker.nativeLinker().canonicalLayouts().get("char"); + public static final ValueLayout.OfShort C_SHORT = (ValueLayout.OfShort) Linker.nativeLinker().canonicalLayouts().get("short"); + public static final ValueLayout.OfInt C_INT = (ValueLayout.OfInt) Linker.nativeLinker().canonicalLayouts().get("int"); + public static final ValueLayout.OfLong C_LONG_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long long"); + public static final ValueLayout.OfFloat C_FLOAT = (ValueLayout.OfFloat) Linker.nativeLinker().canonicalLayouts().get("float"); + public static final ValueLayout.OfDouble C_DOUBLE = (ValueLayout.OfDouble) Linker.nativeLinker().canonicalLayouts().get("double"); + public static final AddressLayout C_POINTER = ((AddressLayout) Linker.nativeLinker().canonicalLayouts().get("void*")) + .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, C_CHAR)); + public static final ValueLayout.OfLong C_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long"); + + static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + + static void traceDowncall(String name, Object... args) { + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("%s(%s)\n", name, traceArgs); + } + + static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { + try { + return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } + + static MemoryLayout align(MemoryLayout layout, long align) { + return switch (layout) { + case PaddingLayout p -> p; + case ValueLayout v -> v.withByteAlignment(align); + case GroupLayout g -> { + MemoryLayout[] alignedMembers = g.memberLayouts().stream() + .map(m -> align(m, align)).toArray(MemoryLayout[]::new); + yield g instanceof StructLayout ? + MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); + } + case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); + }; + } +} + diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/epoll/epoll.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/epoll/epoll.java new file mode 100644 index 00000000..22e0cb02 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/epoll/epoll.java @@ -0,0 +1,72 @@ +// Generated by jextract + +package net.codecrete.usb.linux.gen.epoll; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +public class epoll extends epoll$shared { + + epoll() { + // Should not be called directly + } + + static final Arena LIBRARY_ARENA = Arena.ofAuto(); + + static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup() + .or(Linker.nativeLinker().defaultLookup()); + + private static final int EPOLL_CTL_ADD = (int)1L; + /** + * {@snippet lang=c : + * #define EPOLL_CTL_ADD 1 + * } + */ + public static int EPOLL_CTL_ADD() { + return EPOLL_CTL_ADD; + } + private static final int EPOLL_CTL_DEL = (int)2L; + /** + * {@snippet lang=c : + * #define EPOLL_CTL_DEL 2 + * } + */ + public static int EPOLL_CTL_DEL() { + return EPOLL_CTL_DEL; + } + private static final int EPOLLIN = (int)1L; + /** + * {@snippet lang=c : + * enum EPOLL_EVENTS.EPOLLIN = 1 + * } + */ + public static int EPOLLIN() { + return EPOLLIN; + } + private static final int EPOLLOUT = (int)4L; + /** + * {@snippet lang=c : + * enum EPOLL_EVENTS.EPOLLOUT = 4 + * } + */ + public static int EPOLLOUT() { + return EPOLLOUT; + } + private static final int EPOLLWAKEUP = (int)536870912L; + /** + * {@snippet lang=c : + * enum EPOLL_EVENTS.EPOLLWAKEUP = 536870912 + * } + */ + public static int EPOLLWAKEUP() { + return EPOLLWAKEUP; + } +} + diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/errno/Constants$root.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/errno/Constants$root.java deleted file mode 100644 index 73adc572..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/errno/Constants$root.java +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.linux.gen.errno; - -import static java.lang.foreign.ValueLayout.*; -public class Constants$root { - - static final OfBoolean C_BOOL$LAYOUT = JAVA_BOOLEAN; - static final OfByte C_CHAR$LAYOUT = JAVA_BYTE; - static final OfShort C_SHORT$LAYOUT = JAVA_SHORT.withBitAlignment(16); - static final OfInt C_INT$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfLong C_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfLong C_LONG_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfFloat C_FLOAT$LAYOUT = JAVA_FLOAT.withBitAlignment(32); - static final OfDouble C_DOUBLE$LAYOUT = JAVA_DOUBLE.withBitAlignment(64); - static final OfAddress C_POINTER$LAYOUT = ADDRESS.withBitAlignment(64); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/errno/RuntimeHelper.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/errno/RuntimeHelper.java deleted file mode 100644 index 282b5dfb..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/errno/RuntimeHelper.java +++ /dev/null @@ -1,216 +0,0 @@ -package net.codecrete.usb.linux.gen.errno; -// Generated by jextract - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import static java.lang.foreign.ValueLayout.*; - -final class RuntimeHelper { - - private RuntimeHelper() {} - private final static Linker LINKER = Linker.nativeLinker(); - private final static ClassLoader LOADER = RuntimeHelper.class.getClassLoader(); - private final static MethodHandles.Lookup MH_LOOKUP = MethodHandles.lookup(); - private final static SymbolLookup SYMBOL_LOOKUP; - - final static SegmentAllocator CONSTANT_ALLOCATOR = - (size, align) -> MemorySegment.allocateNative(size, align, MemorySession.openImplicit()); - - static { - - SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); - SYMBOL_LOOKUP = name -> loaderLookup.lookup(name).or(() -> LINKER.defaultLookup().lookup(name)); - } - - static T requireNonNull(T obj, String symbolName) { - if (obj == null) { - throw new UnsatisfiedLinkError("unresolved symbol: " + symbolName); - } - return obj; - } - - private final static SegmentAllocator THROWING_ALLOCATOR = (x, y) -> { throw new AssertionError("should not reach here"); }; - - static final MemorySegment lookupGlobalVariable(String name, MemoryLayout layout) { - return SYMBOL_LOOKUP.lookup(name).map(symbol -> MemorySegment.ofAddress(symbol.address(), layout.byteSize(), MemorySession.openShared())).orElse(null); - } - - static final MethodHandle downcallHandle(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> LINKER.downcallHandle(addr, fdesc)). - orElse(null); - } - - static final MethodHandle downcallHandle(FunctionDescriptor fdesc) { - return LINKER.downcallHandle(fdesc); - } - - static final MethodHandle downcallHandleVariadic(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> VarargsInvoker.make(addr, fdesc)). - orElse(null); - } - - static final MemorySegment upcallStub(Class fi, Z z, FunctionDescriptor fdesc, MemorySession session) { - try { - MethodHandle handle = MH_LOOKUP.findVirtual(fi, "apply", Linker.upcallType(fdesc)); - handle = handle.bindTo(z); - return LINKER.upcallStub(handle, fdesc, session); - } catch (Throwable ex) { - throw new AssertionError(ex); - } - } - - static MemorySegment asArray(MemoryAddress addr, MemoryLayout layout, int numElements, MemorySession session) { - return MemorySegment.ofAddress(addr, numElements * layout.byteSize(), session); - } - - // Internals only below this point - - private static class VarargsInvoker { - private static final MethodHandle INVOKE_MH; - private final MemorySegment symbol; - private final FunctionDescriptor function; - - private VarargsInvoker(MemorySegment symbol, FunctionDescriptor function) { - this.symbol = symbol; - this.function = function; - } - - static { - try { - INVOKE_MH = MethodHandles.lookup().findVirtual(VarargsInvoker.class, "invoke", MethodType.methodType(Object.class, SegmentAllocator.class, Object[].class)); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - static MethodHandle make(MemorySegment symbol, FunctionDescriptor function) { - VarargsInvoker invoker = new VarargsInvoker(symbol, function); - MethodHandle handle = INVOKE_MH.bindTo(invoker).asCollector(Object[].class, function.argumentLayouts().size() + 1); - MethodType mtype = MethodType.methodType(function.returnLayout().isPresent() ? carrier(function.returnLayout().get(), true) : void.class); - for (MemoryLayout layout : function.argumentLayouts()) { - mtype = mtype.appendParameterTypes(carrier(layout, false)); - } - mtype = mtype.appendParameterTypes(Object[].class); - if (mtype.returnType().equals(MemorySegment.class)) { - mtype = mtype.insertParameterTypes(0, SegmentAllocator.class); - } else { - handle = MethodHandles.insertArguments(handle, 0, THROWING_ALLOCATOR); - } - return handle.asType(mtype); - } - - static Class carrier(MemoryLayout layout, boolean ret) { - if (layout instanceof ValueLayout valueLayout) { - return (ret || valueLayout.carrier() != MemoryAddress.class) ? - valueLayout.carrier() : Addressable.class; - } else if (layout instanceof GroupLayout) { - return MemorySegment.class; - } else { - throw new AssertionError("Cannot get here!"); - } - } - - private Object invoke(SegmentAllocator allocator, Object[] args) throws Throwable { - // one trailing Object[] - int nNamedArgs = function.argumentLayouts().size(); - assert(args.length == nNamedArgs + 1); - // The last argument is the array of vararg collector - Object[] unnamedArgs = (Object[]) args[args.length - 1]; - - int argsCount = nNamedArgs + unnamedArgs.length; - Class[] argTypes = new Class[argsCount]; - MemoryLayout[] argLayouts = new MemoryLayout[nNamedArgs + unnamedArgs.length]; - - int pos = 0; - for (pos = 0; pos < nNamedArgs; pos++) { - argLayouts[pos] = function.argumentLayouts().get(pos); - } - - assert pos == nNamedArgs; - for (Object o: unnamedArgs) { - argLayouts[pos] = variadicLayout(normalize(o.getClass())); - pos++; - } - assert pos == argsCount; - - FunctionDescriptor f = (function.returnLayout().isEmpty()) ? - FunctionDescriptor.ofVoid(argLayouts) : - FunctionDescriptor.of(function.returnLayout().get(), argLayouts); - MethodHandle mh = LINKER.downcallHandle(symbol, f); - if (mh.type().returnType() == MemorySegment.class) { - mh = mh.bindTo(allocator); - } - // flatten argument list so that it can be passed to an asSpreader MH - Object[] allArgs = new Object[nNamedArgs + unnamedArgs.length]; - System.arraycopy(args, 0, allArgs, 0, nNamedArgs); - System.arraycopy(unnamedArgs, 0, allArgs, nNamedArgs, unnamedArgs.length); - - return mh.asSpreader(Object[].class, argsCount).invoke(allArgs); - } - - private static Class unboxIfNeeded(Class clazz) { - if (clazz == Boolean.class) { - return boolean.class; - } else if (clazz == Void.class) { - return void.class; - } else if (clazz == Byte.class) { - return byte.class; - } else if (clazz == Character.class) { - return char.class; - } else if (clazz == Short.class) { - return short.class; - } else if (clazz == Integer.class) { - return int.class; - } else if (clazz == Long.class) { - return long.class; - } else if (clazz == Float.class) { - return float.class; - } else if (clazz == Double.class) { - return double.class; - } else { - return clazz; - } - } - - private Class promote(Class c) { - if (c == byte.class || c == char.class || c == short.class || c == int.class) { - return long.class; - } else if (c == float.class) { - return double.class; - } else { - return c; - } - } - - private Class normalize(Class c) { - c = unboxIfNeeded(c); - if (c.isPrimitive()) { - return promote(c); - } - if (MemoryAddress.class.isAssignableFrom(c)) { - return MemoryAddress.class; - } - if (MemorySegment.class.isAssignableFrom(c)) { - return MemorySegment.class; - } - throw new IllegalArgumentException("Invalid type for ABI: " + c.getTypeName()); - } - - private MemoryLayout variadicLayout(Class c) { - if (c == long.class) { - return JAVA_LONG; - } else if (c == double.class) { - return JAVA_DOUBLE; - } else if (MemoryAddress.class.isAssignableFrom(c)) { - return ADDRESS; - } else { - throw new IllegalArgumentException("Unhandled variadic argument class: " + c); - } - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/errno/constants$0.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/errno/constants$0.java deleted file mode 100644 index cb3211ae..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/errno/constants$0.java +++ /dev/null @@ -1,16 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.linux.gen.errno; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.invoke.MethodHandle; -class constants$0 { - - static final FunctionDescriptor __errno_location$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT); - static final MethodHandle __errno_location$MH = RuntimeHelper.downcallHandle( - "__errno_location", - constants$0.__errno_location$FUNC - ); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/errno/errno$shared.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/errno/errno$shared.java new file mode 100644 index 00000000..499be5f7 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/errno/errno$shared.java @@ -0,0 +1,63 @@ +// Generated by jextract + +package net.codecrete.usb.linux.gen.errno; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +public class errno$shared { + + errno$shared() { + // Should not be called directly + } + + public static final ValueLayout.OfBoolean C_BOOL = (ValueLayout.OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool"); + public static final ValueLayout.OfByte C_CHAR =(ValueLayout.OfByte)Linker.nativeLinker().canonicalLayouts().get("char"); + public static final ValueLayout.OfShort C_SHORT = (ValueLayout.OfShort) Linker.nativeLinker().canonicalLayouts().get("short"); + public static final ValueLayout.OfInt C_INT = (ValueLayout.OfInt) Linker.nativeLinker().canonicalLayouts().get("int"); + public static final ValueLayout.OfLong C_LONG_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long long"); + public static final ValueLayout.OfFloat C_FLOAT = (ValueLayout.OfFloat) Linker.nativeLinker().canonicalLayouts().get("float"); + public static final ValueLayout.OfDouble C_DOUBLE = (ValueLayout.OfDouble) Linker.nativeLinker().canonicalLayouts().get("double"); + public static final AddressLayout C_POINTER = ((AddressLayout) Linker.nativeLinker().canonicalLayouts().get("void*")) + .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, C_CHAR)); + public static final ValueLayout.OfLong C_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long"); + + static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + + static void traceDowncall(String name, Object... args) { + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("%s(%s)\n", name, traceArgs); + } + + static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { + try { + return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } + + static MemoryLayout align(MemoryLayout layout, long align) { + return switch (layout) { + case PaddingLayout p -> p; + case ValueLayout v -> v.withByteAlignment(align); + case GroupLayout g -> { + MemoryLayout[] alignedMembers = g.memberLayouts().stream() + .map(m -> align(m, align)).toArray(MemoryLayout[]::new); + yield g instanceof StructLayout ? + MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); + } + case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); + }; + } +} + diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/errno/errno.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/errno/errno.java index ddc5d6e8..d4397f45 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/errno/errno.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/errno/errno.java @@ -2,32 +2,98 @@ package net.codecrete.usb.linux.gen.errno; -import java.lang.foreign.MemoryAddress; -import java.lang.invoke.MethodHandle; +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; import static java.lang.foreign.ValueLayout.*; -public class errno { - - /* package-private */ errno() {} - public static OfByte C_CHAR = Constants$root.C_CHAR$LAYOUT; - public static OfShort C_SHORT = Constants$root.C_SHORT$LAYOUT; - public static OfInt C_INT = Constants$root.C_INT$LAYOUT; - public static OfLong C_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfLong C_LONG_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfFloat C_FLOAT = Constants$root.C_FLOAT$LAYOUT; - public static OfDouble C_DOUBLE = Constants$root.C_DOUBLE$LAYOUT; - public static OfAddress C_POINTER = Constants$root.C_POINTER$LAYOUT; - public static MethodHandle __errno_location$MH() { - return RuntimeHelper.requireNonNull(constants$0.__errno_location$MH,"__errno_location"); - } - public static MemoryAddress __errno_location () { - var mh$ = __errno_location$MH(); - try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } +import static java.lang.foreign.MemoryLayout.PathElement.*; + +public class errno extends errno$shared { + + errno() { + // Should not be called directly } -} + static final Arena LIBRARY_ARENA = Arena.ofAuto(); + + static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup() + .or(Linker.nativeLinker().defaultLookup()); + + private static final int ENOENT = (int)2L; + /** + * {@snippet lang=c : + * #define ENOENT 2 + * } + */ + public static int ENOENT() { + return ENOENT; + } + private static final int EINTR = (int)4L; + /** + * {@snippet lang=c : + * #define EINTR 4 + * } + */ + public static int EINTR() { + return EINTR; + } + private static final int EBADF = (int)9L; + /** + * {@snippet lang=c : + * #define EBADF 9 + * } + */ + public static int EBADF() { + return EBADF; + } + private static final int EAGAIN = (int)11L; + /** + * {@snippet lang=c : + * #define EAGAIN 11 + * } + */ + public static int EAGAIN() { + return EAGAIN; + } + private static final int ENODEV = (int)19L; + /** + * {@snippet lang=c : + * #define ENODEV 19 + * } + */ + public static int ENODEV() { + return ENODEV; + } + private static final int EINVAL = (int)22L; + /** + * {@snippet lang=c : + * #define EINVAL 22 + * } + */ + public static int EINVAL() { + return EINVAL; + } + private static final int EPIPE = (int)32L; + /** + * {@snippet lang=c : + * #define EPIPE 32 + * } + */ + public static int EPIPE() { + return EPIPE; + } + private static final int ECANCELED = (int)125L; + /** + * {@snippet lang=c : + * #define ECANCELED 125 + * } + */ + public static int ECANCELED() { + return ECANCELED; + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/fcntl/Constants$root.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/fcntl/Constants$root.java deleted file mode 100644 index c938e854..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/fcntl/Constants$root.java +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.linux.gen.fcntl; - -import static java.lang.foreign.ValueLayout.*; -public class Constants$root { - - static final OfBoolean C_BOOL$LAYOUT = JAVA_BOOLEAN; - static final OfByte C_CHAR$LAYOUT = JAVA_BYTE; - static final OfShort C_SHORT$LAYOUT = JAVA_SHORT.withBitAlignment(16); - static final OfInt C_INT$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfLong C_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfLong C_LONG_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfFloat C_FLOAT$LAYOUT = JAVA_FLOAT.withBitAlignment(32); - static final OfDouble C_DOUBLE$LAYOUT = JAVA_DOUBLE.withBitAlignment(64); - static final OfAddress C_POINTER$LAYOUT = ADDRESS.withBitAlignment(64); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/fcntl/RuntimeHelper.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/fcntl/RuntimeHelper.java deleted file mode 100644 index 48c09b33..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/fcntl/RuntimeHelper.java +++ /dev/null @@ -1,216 +0,0 @@ -package net.codecrete.usb.linux.gen.fcntl; -// Generated by jextract - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import static java.lang.foreign.ValueLayout.*; - -final class RuntimeHelper { - - private RuntimeHelper() {} - private final static Linker LINKER = Linker.nativeLinker(); - private final static ClassLoader LOADER = RuntimeHelper.class.getClassLoader(); - private final static MethodHandles.Lookup MH_LOOKUP = MethodHandles.lookup(); - private final static SymbolLookup SYMBOL_LOOKUP; - - final static SegmentAllocator CONSTANT_ALLOCATOR = - (size, align) -> MemorySegment.allocateNative(size, align, MemorySession.openImplicit()); - - static { - - SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); - SYMBOL_LOOKUP = name -> loaderLookup.lookup(name).or(() -> LINKER.defaultLookup().lookup(name)); - } - - static T requireNonNull(T obj, String symbolName) { - if (obj == null) { - throw new UnsatisfiedLinkError("unresolved symbol: " + symbolName); - } - return obj; - } - - private final static SegmentAllocator THROWING_ALLOCATOR = (x, y) -> { throw new AssertionError("should not reach here"); }; - - static final MemorySegment lookupGlobalVariable(String name, MemoryLayout layout) { - return SYMBOL_LOOKUP.lookup(name).map(symbol -> MemorySegment.ofAddress(symbol.address(), layout.byteSize(), MemorySession.openShared())).orElse(null); - } - - static final MethodHandle downcallHandle(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> LINKER.downcallHandle(addr, fdesc)). - orElse(null); - } - - static final MethodHandle downcallHandle(FunctionDescriptor fdesc) { - return LINKER.downcallHandle(fdesc); - } - - static final MethodHandle downcallHandleVariadic(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> VarargsInvoker.make(addr, fdesc)). - orElse(null); - } - - static final MemorySegment upcallStub(Class fi, Z z, FunctionDescriptor fdesc, MemorySession session) { - try { - MethodHandle handle = MH_LOOKUP.findVirtual(fi, "apply", Linker.upcallType(fdesc)); - handle = handle.bindTo(z); - return LINKER.upcallStub(handle, fdesc, session); - } catch (Throwable ex) { - throw new AssertionError(ex); - } - } - - static MemorySegment asArray(MemoryAddress addr, MemoryLayout layout, int numElements, MemorySession session) { - return MemorySegment.ofAddress(addr, numElements * layout.byteSize(), session); - } - - // Internals only below this point - - private static class VarargsInvoker { - private static final MethodHandle INVOKE_MH; - private final MemorySegment symbol; - private final FunctionDescriptor function; - - private VarargsInvoker(MemorySegment symbol, FunctionDescriptor function) { - this.symbol = symbol; - this.function = function; - } - - static { - try { - INVOKE_MH = MethodHandles.lookup().findVirtual(VarargsInvoker.class, "invoke", MethodType.methodType(Object.class, SegmentAllocator.class, Object[].class)); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - static MethodHandle make(MemorySegment symbol, FunctionDescriptor function) { - VarargsInvoker invoker = new VarargsInvoker(symbol, function); - MethodHandle handle = INVOKE_MH.bindTo(invoker).asCollector(Object[].class, function.argumentLayouts().size() + 1); - MethodType mtype = MethodType.methodType(function.returnLayout().isPresent() ? carrier(function.returnLayout().get(), true) : void.class); - for (MemoryLayout layout : function.argumentLayouts()) { - mtype = mtype.appendParameterTypes(carrier(layout, false)); - } - mtype = mtype.appendParameterTypes(Object[].class); - if (mtype.returnType().equals(MemorySegment.class)) { - mtype = mtype.insertParameterTypes(0, SegmentAllocator.class); - } else { - handle = MethodHandles.insertArguments(handle, 0, THROWING_ALLOCATOR); - } - return handle.asType(mtype); - } - - static Class carrier(MemoryLayout layout, boolean ret) { - if (layout instanceof ValueLayout valueLayout) { - return (ret || valueLayout.carrier() != MemoryAddress.class) ? - valueLayout.carrier() : Addressable.class; - } else if (layout instanceof GroupLayout) { - return MemorySegment.class; - } else { - throw new AssertionError("Cannot get here!"); - } - } - - private Object invoke(SegmentAllocator allocator, Object[] args) throws Throwable { - // one trailing Object[] - int nNamedArgs = function.argumentLayouts().size(); - assert(args.length == nNamedArgs + 1); - // The last argument is the array of vararg collector - Object[] unnamedArgs = (Object[]) args[args.length - 1]; - - int argsCount = nNamedArgs + unnamedArgs.length; - Class[] argTypes = new Class[argsCount]; - MemoryLayout[] argLayouts = new MemoryLayout[nNamedArgs + unnamedArgs.length]; - - int pos = 0; - for (pos = 0; pos < nNamedArgs; pos++) { - argLayouts[pos] = function.argumentLayouts().get(pos); - } - - assert pos == nNamedArgs; - for (Object o: unnamedArgs) { - argLayouts[pos] = variadicLayout(normalize(o.getClass())); - pos++; - } - assert pos == argsCount; - - FunctionDescriptor f = (function.returnLayout().isEmpty()) ? - FunctionDescriptor.ofVoid(argLayouts) : - FunctionDescriptor.of(function.returnLayout().get(), argLayouts); - MethodHandle mh = LINKER.downcallHandle(symbol, f); - if (mh.type().returnType() == MemorySegment.class) { - mh = mh.bindTo(allocator); - } - // flatten argument list so that it can be passed to an asSpreader MH - Object[] allArgs = new Object[nNamedArgs + unnamedArgs.length]; - System.arraycopy(args, 0, allArgs, 0, nNamedArgs); - System.arraycopy(unnamedArgs, 0, allArgs, nNamedArgs, unnamedArgs.length); - - return mh.asSpreader(Object[].class, argsCount).invoke(allArgs); - } - - private static Class unboxIfNeeded(Class clazz) { - if (clazz == Boolean.class) { - return boolean.class; - } else if (clazz == Void.class) { - return void.class; - } else if (clazz == Byte.class) { - return byte.class; - } else if (clazz == Character.class) { - return char.class; - } else if (clazz == Short.class) { - return short.class; - } else if (clazz == Integer.class) { - return int.class; - } else if (clazz == Long.class) { - return long.class; - } else if (clazz == Float.class) { - return float.class; - } else if (clazz == Double.class) { - return double.class; - } else { - return clazz; - } - } - - private Class promote(Class c) { - if (c == byte.class || c == char.class || c == short.class || c == int.class) { - return long.class; - } else if (c == float.class) { - return double.class; - } else { - return c; - } - } - - private Class normalize(Class c) { - c = unboxIfNeeded(c); - if (c.isPrimitive()) { - return promote(c); - } - if (MemoryAddress.class.isAssignableFrom(c)) { - return MemoryAddress.class; - } - if (MemorySegment.class.isAssignableFrom(c)) { - return MemorySegment.class; - } - throw new IllegalArgumentException("Invalid type for ABI: " + c.getTypeName()); - } - - private MemoryLayout variadicLayout(Class c) { - if (c == long.class) { - return JAVA_LONG; - } else if (c == double.class) { - return JAVA_DOUBLE; - } else if (MemoryAddress.class.isAssignableFrom(c)) { - return ADDRESS; - } else { - throw new IllegalArgumentException("Unhandled variadic argument class: " + c); - } - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/fcntl/constants$0.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/fcntl/constants$0.java deleted file mode 100644 index 517dcb8f..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/fcntl/constants$0.java +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.linux.gen.fcntl; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.invoke.MethodHandle; -class constants$0 { - - static final FunctionDescriptor open$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT - ); - static final MethodHandle open$MH = RuntimeHelper.downcallHandleVariadic( - "open", - constants$0.open$FUNC - ); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/fcntl/fcntl$shared.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/fcntl/fcntl$shared.java new file mode 100644 index 00000000..0a6ca8e4 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/fcntl/fcntl$shared.java @@ -0,0 +1,63 @@ +// Generated by jextract + +package net.codecrete.usb.linux.gen.fcntl; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +public class fcntl$shared { + + fcntl$shared() { + // Should not be called directly + } + + public static final ValueLayout.OfBoolean C_BOOL = (ValueLayout.OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool"); + public static final ValueLayout.OfByte C_CHAR =(ValueLayout.OfByte)Linker.nativeLinker().canonicalLayouts().get("char"); + public static final ValueLayout.OfShort C_SHORT = (ValueLayout.OfShort) Linker.nativeLinker().canonicalLayouts().get("short"); + public static final ValueLayout.OfInt C_INT = (ValueLayout.OfInt) Linker.nativeLinker().canonicalLayouts().get("int"); + public static final ValueLayout.OfLong C_LONG_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long long"); + public static final ValueLayout.OfFloat C_FLOAT = (ValueLayout.OfFloat) Linker.nativeLinker().canonicalLayouts().get("float"); + public static final ValueLayout.OfDouble C_DOUBLE = (ValueLayout.OfDouble) Linker.nativeLinker().canonicalLayouts().get("double"); + public static final AddressLayout C_POINTER = ((AddressLayout) Linker.nativeLinker().canonicalLayouts().get("void*")) + .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, C_CHAR)); + public static final ValueLayout.OfLong C_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long"); + + static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + + static void traceDowncall(String name, Object... args) { + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("%s(%s)\n", name, traceArgs); + } + + static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { + try { + return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } + + static MemoryLayout align(MemoryLayout layout, long align) { + return switch (layout) { + case PaddingLayout p -> p; + case ValueLayout v -> v.withByteAlignment(align); + case GroupLayout g -> { + MemoryLayout[] alignedMembers = g.memberLayouts().stream() + .map(m -> align(m, align)).toArray(MemoryLayout[]::new); + yield g instanceof StructLayout ? + MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); + } + case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); + }; + } +} + diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/fcntl/fcntl.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/fcntl/fcntl.java index 41eb1e0c..72cbfb53 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/fcntl/fcntl.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/fcntl/fcntl.java @@ -2,38 +2,53 @@ package net.codecrete.usb.linux.gen.fcntl; -import java.lang.foreign.Addressable; -import java.lang.invoke.MethodHandle; +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; import static java.lang.foreign.ValueLayout.*; -public class fcntl { - - /* package-private */ fcntl() {} - public static OfByte C_CHAR = Constants$root.C_CHAR$LAYOUT; - public static OfShort C_SHORT = Constants$root.C_SHORT$LAYOUT; - public static OfInt C_INT = Constants$root.C_INT$LAYOUT; - public static OfLong C_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfLong C_LONG_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfFloat C_FLOAT = Constants$root.C_FLOAT$LAYOUT; - public static OfDouble C_DOUBLE = Constants$root.C_DOUBLE$LAYOUT; - public static OfAddress C_POINTER = Constants$root.C_POINTER$LAYOUT; - public static int O_RDWR() { - return (int)2L; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +public class fcntl extends fcntl$shared { + + fcntl() { + // Should not be called directly } - public static MethodHandle open$MH() { - return RuntimeHelper.requireNonNull(constants$0.open$MH,"open"); + + static final Arena LIBRARY_ARENA = Arena.ofAuto(); + + static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup() + .or(Linker.nativeLinker().defaultLookup()); + + private static final int O_RDWR = (int)2L; + /** + * {@snippet lang=c : + * #define O_RDWR 2 + * } + */ + public static int O_RDWR() { + return O_RDWR; } - public static int open ( Addressable __file, int __oflag, Object... x2) { - var mh$ = open$MH(); - try { - return (int)mh$.invokeExact(__file, __oflag, x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } + private static final int FD_CLOEXEC = (int)1L; + /** + * {@snippet lang=c : + * #define FD_CLOEXEC 1 + * } + */ + public static int FD_CLOEXEC() { + return FD_CLOEXEC; } + private static final int O_CLOEXEC = (int)524288L; + /** + * {@snippet lang=c : + * #define O_CLOEXEC 524288 + * } + */ public static int O_CLOEXEC() { - return (int)524288L; + return O_CLOEXEC; } } - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/ioctl/Constants$root.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/ioctl/Constants$root.java deleted file mode 100644 index c9e48345..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/ioctl/Constants$root.java +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.linux.gen.ioctl; - -import static java.lang.foreign.ValueLayout.*; -public class Constants$root { - - static final OfBoolean C_BOOL$LAYOUT = JAVA_BOOLEAN; - static final OfByte C_CHAR$LAYOUT = JAVA_BYTE; - static final OfShort C_SHORT$LAYOUT = JAVA_SHORT.withBitAlignment(16); - static final OfInt C_INT$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfLong C_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfLong C_LONG_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfFloat C_FLOAT$LAYOUT = JAVA_FLOAT.withBitAlignment(32); - static final OfDouble C_DOUBLE$LAYOUT = JAVA_DOUBLE.withBitAlignment(64); - static final OfAddress C_POINTER$LAYOUT = ADDRESS.withBitAlignment(64); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/ioctl/RuntimeHelper.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/ioctl/RuntimeHelper.java deleted file mode 100644 index e7740016..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/ioctl/RuntimeHelper.java +++ /dev/null @@ -1,216 +0,0 @@ -package net.codecrete.usb.linux.gen.ioctl; -// Generated by jextract - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import static java.lang.foreign.ValueLayout.*; - -final class RuntimeHelper { - - private RuntimeHelper() {} - private final static Linker LINKER = Linker.nativeLinker(); - private final static ClassLoader LOADER = RuntimeHelper.class.getClassLoader(); - private final static MethodHandles.Lookup MH_LOOKUP = MethodHandles.lookup(); - private final static SymbolLookup SYMBOL_LOOKUP; - - final static SegmentAllocator CONSTANT_ALLOCATOR = - (size, align) -> MemorySegment.allocateNative(size, align, MemorySession.openImplicit()); - - static { - - SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); - SYMBOL_LOOKUP = name -> loaderLookup.lookup(name).or(() -> LINKER.defaultLookup().lookup(name)); - } - - static T requireNonNull(T obj, String symbolName) { - if (obj == null) { - throw new UnsatisfiedLinkError("unresolved symbol: " + symbolName); - } - return obj; - } - - private final static SegmentAllocator THROWING_ALLOCATOR = (x, y) -> { throw new AssertionError("should not reach here"); }; - - static final MemorySegment lookupGlobalVariable(String name, MemoryLayout layout) { - return SYMBOL_LOOKUP.lookup(name).map(symbol -> MemorySegment.ofAddress(symbol.address(), layout.byteSize(), MemorySession.openShared())).orElse(null); - } - - static final MethodHandle downcallHandle(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> LINKER.downcallHandle(addr, fdesc)). - orElse(null); - } - - static final MethodHandle downcallHandle(FunctionDescriptor fdesc) { - return LINKER.downcallHandle(fdesc); - } - - static final MethodHandle downcallHandleVariadic(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> VarargsInvoker.make(addr, fdesc)). - orElse(null); - } - - static final MemorySegment upcallStub(Class fi, Z z, FunctionDescriptor fdesc, MemorySession session) { - try { - MethodHandle handle = MH_LOOKUP.findVirtual(fi, "apply", Linker.upcallType(fdesc)); - handle = handle.bindTo(z); - return LINKER.upcallStub(handle, fdesc, session); - } catch (Throwable ex) { - throw new AssertionError(ex); - } - } - - static MemorySegment asArray(MemoryAddress addr, MemoryLayout layout, int numElements, MemorySession session) { - return MemorySegment.ofAddress(addr, numElements * layout.byteSize(), session); - } - - // Internals only below this point - - private static class VarargsInvoker { - private static final MethodHandle INVOKE_MH; - private final MemorySegment symbol; - private final FunctionDescriptor function; - - private VarargsInvoker(MemorySegment symbol, FunctionDescriptor function) { - this.symbol = symbol; - this.function = function; - } - - static { - try { - INVOKE_MH = MethodHandles.lookup().findVirtual(VarargsInvoker.class, "invoke", MethodType.methodType(Object.class, SegmentAllocator.class, Object[].class)); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - static MethodHandle make(MemorySegment symbol, FunctionDescriptor function) { - VarargsInvoker invoker = new VarargsInvoker(symbol, function); - MethodHandle handle = INVOKE_MH.bindTo(invoker).asCollector(Object[].class, function.argumentLayouts().size() + 1); - MethodType mtype = MethodType.methodType(function.returnLayout().isPresent() ? carrier(function.returnLayout().get(), true) : void.class); - for (MemoryLayout layout : function.argumentLayouts()) { - mtype = mtype.appendParameterTypes(carrier(layout, false)); - } - mtype = mtype.appendParameterTypes(Object[].class); - if (mtype.returnType().equals(MemorySegment.class)) { - mtype = mtype.insertParameterTypes(0, SegmentAllocator.class); - } else { - handle = MethodHandles.insertArguments(handle, 0, THROWING_ALLOCATOR); - } - return handle.asType(mtype); - } - - static Class carrier(MemoryLayout layout, boolean ret) { - if (layout instanceof ValueLayout valueLayout) { - return (ret || valueLayout.carrier() != MemoryAddress.class) ? - valueLayout.carrier() : Addressable.class; - } else if (layout instanceof GroupLayout) { - return MemorySegment.class; - } else { - throw new AssertionError("Cannot get here!"); - } - } - - private Object invoke(SegmentAllocator allocator, Object[] args) throws Throwable { - // one trailing Object[] - int nNamedArgs = function.argumentLayouts().size(); - assert(args.length == nNamedArgs + 1); - // The last argument is the array of vararg collector - Object[] unnamedArgs = (Object[]) args[args.length - 1]; - - int argsCount = nNamedArgs + unnamedArgs.length; - Class[] argTypes = new Class[argsCount]; - MemoryLayout[] argLayouts = new MemoryLayout[nNamedArgs + unnamedArgs.length]; - - int pos = 0; - for (pos = 0; pos < nNamedArgs; pos++) { - argLayouts[pos] = function.argumentLayouts().get(pos); - } - - assert pos == nNamedArgs; - for (Object o: unnamedArgs) { - argLayouts[pos] = variadicLayout(normalize(o.getClass())); - pos++; - } - assert pos == argsCount; - - FunctionDescriptor f = (function.returnLayout().isEmpty()) ? - FunctionDescriptor.ofVoid(argLayouts) : - FunctionDescriptor.of(function.returnLayout().get(), argLayouts); - MethodHandle mh = LINKER.downcallHandle(symbol, f); - if (mh.type().returnType() == MemorySegment.class) { - mh = mh.bindTo(allocator); - } - // flatten argument list so that it can be passed to an asSpreader MH - Object[] allArgs = new Object[nNamedArgs + unnamedArgs.length]; - System.arraycopy(args, 0, allArgs, 0, nNamedArgs); - System.arraycopy(unnamedArgs, 0, allArgs, nNamedArgs, unnamedArgs.length); - - return mh.asSpreader(Object[].class, argsCount).invoke(allArgs); - } - - private static Class unboxIfNeeded(Class clazz) { - if (clazz == Boolean.class) { - return boolean.class; - } else if (clazz == Void.class) { - return void.class; - } else if (clazz == Byte.class) { - return byte.class; - } else if (clazz == Character.class) { - return char.class; - } else if (clazz == Short.class) { - return short.class; - } else if (clazz == Integer.class) { - return int.class; - } else if (clazz == Long.class) { - return long.class; - } else if (clazz == Float.class) { - return float.class; - } else if (clazz == Double.class) { - return double.class; - } else { - return clazz; - } - } - - private Class promote(Class c) { - if (c == byte.class || c == char.class || c == short.class || c == int.class) { - return long.class; - } else if (c == float.class) { - return double.class; - } else { - return c; - } - } - - private Class normalize(Class c) { - c = unboxIfNeeded(c); - if (c.isPrimitive()) { - return promote(c); - } - if (MemoryAddress.class.isAssignableFrom(c)) { - return MemoryAddress.class; - } - if (MemorySegment.class.isAssignableFrom(c)) { - return MemorySegment.class; - } - throw new IllegalArgumentException("Invalid type for ABI: " + c.getTypeName()); - } - - private MemoryLayout variadicLayout(Class c) { - if (c == long.class) { - return JAVA_LONG; - } else if (c == double.class) { - return JAVA_DOUBLE; - } else if (MemoryAddress.class.isAssignableFrom(c)) { - return ADDRESS; - } else { - throw new IllegalArgumentException("Unhandled variadic argument class: " + c); - } - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/ioctl/constants$0.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/ioctl/constants$0.java deleted file mode 100644 index e68a7892..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/ioctl/constants$0.java +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.linux.gen.ioctl; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.invoke.MethodHandle; -class constants$0 { - - static final FunctionDescriptor ioctl$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_LONG_LONG$LAYOUT - ); - static final MethodHandle ioctl$MH = RuntimeHelper.downcallHandleVariadic( - "ioctl", - constants$0.ioctl$FUNC - ); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/ioctl/ioctl.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/ioctl/ioctl.java deleted file mode 100644 index cbe51a0b..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/ioctl/ioctl.java +++ /dev/null @@ -1,32 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.linux.gen.ioctl; - -import java.lang.invoke.MethodHandle; - -import static java.lang.foreign.ValueLayout.*; -public class ioctl { - - /* package-private */ ioctl() {} - public static OfByte C_CHAR = Constants$root.C_CHAR$LAYOUT; - public static OfShort C_SHORT = Constants$root.C_SHORT$LAYOUT; - public static OfInt C_INT = Constants$root.C_INT$LAYOUT; - public static OfLong C_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfLong C_LONG_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfFloat C_FLOAT = Constants$root.C_FLOAT$LAYOUT; - public static OfDouble C_DOUBLE = Constants$root.C_DOUBLE$LAYOUT; - public static OfAddress C_POINTER = Constants$root.C_POINTER$LAYOUT; - public static MethodHandle ioctl$MH() { - return RuntimeHelper.requireNonNull(constants$0.ioctl$MH,"ioctl"); - } - public static int ioctl ( int __fd, long __request, Object... x2) { - var mh$ = ioctl$MH(); - try { - return (int)mh$.invokeExact(__fd, __request, x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/select/Constants$root.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/select/Constants$root.java deleted file mode 100644 index bd53659c..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/select/Constants$root.java +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.linux.gen.select; - -import static java.lang.foreign.ValueLayout.*; -public class Constants$root { - - static final OfBoolean C_BOOL$LAYOUT = JAVA_BOOLEAN; - static final OfByte C_CHAR$LAYOUT = JAVA_BYTE; - static final OfShort C_SHORT$LAYOUT = JAVA_SHORT.withBitAlignment(16); - static final OfInt C_INT$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfLong C_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfLong C_LONG_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfFloat C_FLOAT$LAYOUT = JAVA_FLOAT.withBitAlignment(32); - static final OfDouble C_DOUBLE$LAYOUT = JAVA_DOUBLE.withBitAlignment(64); - static final OfAddress C_POINTER$LAYOUT = ADDRESS.withBitAlignment(64); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/select/RuntimeHelper.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/select/RuntimeHelper.java deleted file mode 100644 index a634735c..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/select/RuntimeHelper.java +++ /dev/null @@ -1,216 +0,0 @@ -package net.codecrete.usb.linux.gen.select; -// Generated by jextract - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import static java.lang.foreign.ValueLayout.*; - -final class RuntimeHelper { - - private RuntimeHelper() {} - private final static Linker LINKER = Linker.nativeLinker(); - private final static ClassLoader LOADER = RuntimeHelper.class.getClassLoader(); - private final static MethodHandles.Lookup MH_LOOKUP = MethodHandles.lookup(); - private final static SymbolLookup SYMBOL_LOOKUP; - - final static SegmentAllocator CONSTANT_ALLOCATOR = - (size, align) -> MemorySegment.allocateNative(size, align, MemorySession.openImplicit()); - - static { - - SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); - SYMBOL_LOOKUP = name -> loaderLookup.lookup(name).or(() -> LINKER.defaultLookup().lookup(name)); - } - - static T requireNonNull(T obj, String symbolName) { - if (obj == null) { - throw new UnsatisfiedLinkError("unresolved symbol: " + symbolName); - } - return obj; - } - - private final static SegmentAllocator THROWING_ALLOCATOR = (x, y) -> { throw new AssertionError("should not reach here"); }; - - static final MemorySegment lookupGlobalVariable(String name, MemoryLayout layout) { - return SYMBOL_LOOKUP.lookup(name).map(symbol -> MemorySegment.ofAddress(symbol.address(), layout.byteSize(), MemorySession.openShared())).orElse(null); - } - - static final MethodHandle downcallHandle(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> LINKER.downcallHandle(addr, fdesc)). - orElse(null); - } - - static final MethodHandle downcallHandle(FunctionDescriptor fdesc) { - return LINKER.downcallHandle(fdesc); - } - - static final MethodHandle downcallHandleVariadic(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> VarargsInvoker.make(addr, fdesc)). - orElse(null); - } - - static final MemorySegment upcallStub(Class fi, Z z, FunctionDescriptor fdesc, MemorySession session) { - try { - MethodHandle handle = MH_LOOKUP.findVirtual(fi, "apply", Linker.upcallType(fdesc)); - handle = handle.bindTo(z); - return LINKER.upcallStub(handle, fdesc, session); - } catch (Throwable ex) { - throw new AssertionError(ex); - } - } - - static MemorySegment asArray(MemoryAddress addr, MemoryLayout layout, int numElements, MemorySession session) { - return MemorySegment.ofAddress(addr, numElements * layout.byteSize(), session); - } - - // Internals only below this point - - private static class VarargsInvoker { - private static final MethodHandle INVOKE_MH; - private final MemorySegment symbol; - private final FunctionDescriptor function; - - private VarargsInvoker(MemorySegment symbol, FunctionDescriptor function) { - this.symbol = symbol; - this.function = function; - } - - static { - try { - INVOKE_MH = MethodHandles.lookup().findVirtual(VarargsInvoker.class, "invoke", MethodType.methodType(Object.class, SegmentAllocator.class, Object[].class)); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - static MethodHandle make(MemorySegment symbol, FunctionDescriptor function) { - VarargsInvoker invoker = new VarargsInvoker(symbol, function); - MethodHandle handle = INVOKE_MH.bindTo(invoker).asCollector(Object[].class, function.argumentLayouts().size() + 1); - MethodType mtype = MethodType.methodType(function.returnLayout().isPresent() ? carrier(function.returnLayout().get(), true) : void.class); - for (MemoryLayout layout : function.argumentLayouts()) { - mtype = mtype.appendParameterTypes(carrier(layout, false)); - } - mtype = mtype.appendParameterTypes(Object[].class); - if (mtype.returnType().equals(MemorySegment.class)) { - mtype = mtype.insertParameterTypes(0, SegmentAllocator.class); - } else { - handle = MethodHandles.insertArguments(handle, 0, THROWING_ALLOCATOR); - } - return handle.asType(mtype); - } - - static Class carrier(MemoryLayout layout, boolean ret) { - if (layout instanceof ValueLayout valueLayout) { - return (ret || valueLayout.carrier() != MemoryAddress.class) ? - valueLayout.carrier() : Addressable.class; - } else if (layout instanceof GroupLayout) { - return MemorySegment.class; - } else { - throw new AssertionError("Cannot get here!"); - } - } - - private Object invoke(SegmentAllocator allocator, Object[] args) throws Throwable { - // one trailing Object[] - int nNamedArgs = function.argumentLayouts().size(); - assert(args.length == nNamedArgs + 1); - // The last argument is the array of vararg collector - Object[] unnamedArgs = (Object[]) args[args.length - 1]; - - int argsCount = nNamedArgs + unnamedArgs.length; - Class[] argTypes = new Class[argsCount]; - MemoryLayout[] argLayouts = new MemoryLayout[nNamedArgs + unnamedArgs.length]; - - int pos = 0; - for (pos = 0; pos < nNamedArgs; pos++) { - argLayouts[pos] = function.argumentLayouts().get(pos); - } - - assert pos == nNamedArgs; - for (Object o: unnamedArgs) { - argLayouts[pos] = variadicLayout(normalize(o.getClass())); - pos++; - } - assert pos == argsCount; - - FunctionDescriptor f = (function.returnLayout().isEmpty()) ? - FunctionDescriptor.ofVoid(argLayouts) : - FunctionDescriptor.of(function.returnLayout().get(), argLayouts); - MethodHandle mh = LINKER.downcallHandle(symbol, f); - if (mh.type().returnType() == MemorySegment.class) { - mh = mh.bindTo(allocator); - } - // flatten argument list so that it can be passed to an asSpreader MH - Object[] allArgs = new Object[nNamedArgs + unnamedArgs.length]; - System.arraycopy(args, 0, allArgs, 0, nNamedArgs); - System.arraycopy(unnamedArgs, 0, allArgs, nNamedArgs, unnamedArgs.length); - - return mh.asSpreader(Object[].class, argsCount).invoke(allArgs); - } - - private static Class unboxIfNeeded(Class clazz) { - if (clazz == Boolean.class) { - return boolean.class; - } else if (clazz == Void.class) { - return void.class; - } else if (clazz == Byte.class) { - return byte.class; - } else if (clazz == Character.class) { - return char.class; - } else if (clazz == Short.class) { - return short.class; - } else if (clazz == Integer.class) { - return int.class; - } else if (clazz == Long.class) { - return long.class; - } else if (clazz == Float.class) { - return float.class; - } else if (clazz == Double.class) { - return double.class; - } else { - return clazz; - } - } - - private Class promote(Class c) { - if (c == byte.class || c == char.class || c == short.class || c == int.class) { - return long.class; - } else if (c == float.class) { - return double.class; - } else { - return c; - } - } - - private Class normalize(Class c) { - c = unboxIfNeeded(c); - if (c.isPrimitive()) { - return promote(c); - } - if (MemoryAddress.class.isAssignableFrom(c)) { - return MemoryAddress.class; - } - if (MemorySegment.class.isAssignableFrom(c)) { - return MemorySegment.class; - } - throw new IllegalArgumentException("Invalid type for ABI: " + c.getTypeName()); - } - - private MemoryLayout variadicLayout(Class c) { - if (c == long.class) { - return JAVA_LONG; - } else if (c == double.class) { - return JAVA_DOUBLE; - } else if (MemoryAddress.class.isAssignableFrom(c)) { - return ADDRESS; - } else { - throw new IllegalArgumentException("Unhandled variadic argument class: " + c); - } - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/select/constants$0.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/select/constants$0.java deleted file mode 100644 index 1e095f2b..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/select/constants$0.java +++ /dev/null @@ -1,22 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.linux.gen.select; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.invoke.MethodHandle; -class constants$0 { - - static final FunctionDescriptor select$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle select$MH = RuntimeHelper.downcallHandle( - "select", - constants$0.select$FUNC - ); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/select/fd_set.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/select/fd_set.java deleted file mode 100644 index 2a321b89..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/select/fd_set.java +++ /dev/null @@ -1,25 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.linux.gen.select; - -import java.lang.foreign.*; -public class fd_set { - - static final GroupLayout $struct$LAYOUT = MemoryLayout.structLayout( - MemoryLayout.sequenceLayout(16, Constants$root.C_LONG_LONG$LAYOUT).withName("__fds_bits") - ); - public static MemoryLayout $LAYOUT() { - return fd_set.$struct$LAYOUT; - } - public static MemorySegment __fds_bits$slice(MemorySegment seg) { - return seg.asSlice(0, 128); - } - public static long sizeof() { return $LAYOUT().byteSize(); } - public static MemorySegment allocate(SegmentAllocator allocator) { return allocator.allocate($LAYOUT()); } - public static MemorySegment allocateArray(int len, SegmentAllocator allocator) { - return allocator.allocate(MemoryLayout.sequenceLayout(len, $LAYOUT())); - } - public static MemorySegment ofAddress(MemoryAddress addr, MemorySession session) { return RuntimeHelper.asArray(addr, $LAYOUT(), 1, session); } -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/select/select.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/select/select.java deleted file mode 100644 index 85ed94b2..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/select/select.java +++ /dev/null @@ -1,33 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.linux.gen.select; - -import java.lang.foreign.Addressable; -import java.lang.invoke.MethodHandle; - -import static java.lang.foreign.ValueLayout.*; -public class select { - - /* package-private */ select() {} - public static OfByte C_CHAR = Constants$root.C_CHAR$LAYOUT; - public static OfShort C_SHORT = Constants$root.C_SHORT$LAYOUT; - public static OfInt C_INT = Constants$root.C_INT$LAYOUT; - public static OfLong C_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfLong C_LONG_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfFloat C_FLOAT = Constants$root.C_FLOAT$LAYOUT; - public static OfDouble C_DOUBLE = Constants$root.C_DOUBLE$LAYOUT; - public static OfAddress C_POINTER = Constants$root.C_POINTER$LAYOUT; - public static MethodHandle select$MH() { - return RuntimeHelper.requireNonNull(constants$0.select$MH,"select"); - } - public static int select ( int __nfds, Addressable __readfds, Addressable __writefds, Addressable __exceptfds, Addressable __timeout) { - var mh$ = select$MH(); - try { - return (int)mh$.invokeExact(__nfds, __readfds, __writefds, __exceptfds, __timeout); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/string/string$shared.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/string/string$shared.java new file mode 100644 index 00000000..824bb277 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/string/string$shared.java @@ -0,0 +1,63 @@ +// Generated by jextract + +package net.codecrete.usb.linux.gen.string; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +public class string$shared { + + string$shared() { + // Should not be called directly + } + + public static final ValueLayout.OfBoolean C_BOOL = (ValueLayout.OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool"); + public static final ValueLayout.OfByte C_CHAR =(ValueLayout.OfByte)Linker.nativeLinker().canonicalLayouts().get("char"); + public static final ValueLayout.OfShort C_SHORT = (ValueLayout.OfShort) Linker.nativeLinker().canonicalLayouts().get("short"); + public static final ValueLayout.OfInt C_INT = (ValueLayout.OfInt) Linker.nativeLinker().canonicalLayouts().get("int"); + public static final ValueLayout.OfLong C_LONG_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long long"); + public static final ValueLayout.OfFloat C_FLOAT = (ValueLayout.OfFloat) Linker.nativeLinker().canonicalLayouts().get("float"); + public static final ValueLayout.OfDouble C_DOUBLE = (ValueLayout.OfDouble) Linker.nativeLinker().canonicalLayouts().get("double"); + public static final AddressLayout C_POINTER = ((AddressLayout) Linker.nativeLinker().canonicalLayouts().get("void*")) + .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, C_CHAR)); + public static final ValueLayout.OfLong C_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long"); + + static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + + static void traceDowncall(String name, Object... args) { + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("%s(%s)\n", name, traceArgs); + } + + static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { + try { + return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } + + static MemoryLayout align(MemoryLayout layout, long align) { + return switch (layout) { + case PaddingLayout p -> p; + case ValueLayout v -> v.withByteAlignment(align); + case GroupLayout g -> { + MemoryLayout[] alignedMembers = g.memberLayouts().stream() + .map(m -> align(m, align)).toArray(MemoryLayout[]::new); + yield g instanceof StructLayout ? + MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); + } + case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); + }; + } +} + diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/string/string.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/string/string.java new file mode 100644 index 00000000..de0e4452 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/string/string.java @@ -0,0 +1,87 @@ +// Generated by jextract + +package net.codecrete.usb.linux.gen.string; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +public class string extends string$shared { + + string() { + // Should not be called directly + } + + static final Arena LIBRARY_ARENA = Arena.ofAuto(); + + static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup() + .or(Linker.nativeLinker().defaultLookup()); + + + private static class strerror { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + string.C_POINTER, + string.C_INT + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("strerror"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * extern char *strerror(int __errnum) + * } + */ + public static FunctionDescriptor strerror$descriptor() { + return strerror.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * extern char *strerror(int __errnum) + * } + */ + public static MethodHandle strerror$handle() { + return strerror.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * extern char *strerror(int __errnum) + * } + */ + public static MemorySegment strerror$address() { + return strerror.ADDR; + } + + /** + * {@snippet lang=c : + * extern char *strerror(int __errnum) + * } + */ + public static MemorySegment strerror(int __errnum) { + var mh$ = strerror.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("strerror", __errnum); + } + return (MemorySegment)mh$.invokeExact(__errnum); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } +} + diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/Constants$root.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/Constants$root.java deleted file mode 100644 index 2a06330e..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/Constants$root.java +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.linux.gen.udev; - -import static java.lang.foreign.ValueLayout.*; -public class Constants$root { - - static final OfBoolean C_BOOL$LAYOUT = JAVA_BOOLEAN; - static final OfByte C_CHAR$LAYOUT = JAVA_BYTE; - static final OfShort C_SHORT$LAYOUT = JAVA_SHORT.withBitAlignment(16); - static final OfInt C_INT$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfLong C_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfLong C_LONG_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfFloat C_FLOAT$LAYOUT = JAVA_FLOAT.withBitAlignment(32); - static final OfDouble C_DOUBLE$LAYOUT = JAVA_DOUBLE.withBitAlignment(64); - static final OfAddress C_POINTER$LAYOUT = ADDRESS.withBitAlignment(64); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/RuntimeHelper.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/RuntimeHelper.java deleted file mode 100644 index 775c0b6c..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/RuntimeHelper.java +++ /dev/null @@ -1,217 +0,0 @@ -package net.codecrete.usb.linux.gen.udev; -// Generated by jextract - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import static java.lang.foreign.ValueLayout.*; - -final class RuntimeHelper { - - private RuntimeHelper() {} - private final static Linker LINKER = Linker.nativeLinker(); - private final static ClassLoader LOADER = RuntimeHelper.class.getClassLoader(); - private final static MethodHandles.Lookup MH_LOOKUP = MethodHandles.lookup(); - private final static SymbolLookup SYMBOL_LOOKUP; - - final static SegmentAllocator CONSTANT_ALLOCATOR = - (size, align) -> MemorySegment.allocateNative(size, align, MemorySession.openImplicit()); - - static { -// System.loadLibrary("udev"); -// SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); - SymbolLookup loaderLookup = SymbolLookup.libraryLookup("libudev.so", MemorySession.openImplicit()); - SYMBOL_LOOKUP = name -> loaderLookup.lookup(name).or(() -> LINKER.defaultLookup().lookup(name)); - } - - static T requireNonNull(T obj, String symbolName) { - if (obj == null) { - throw new UnsatisfiedLinkError("unresolved symbol: " + symbolName); - } - return obj; - } - - private final static SegmentAllocator THROWING_ALLOCATOR = (x, y) -> { throw new AssertionError("should not reach here"); }; - - static final MemorySegment lookupGlobalVariable(String name, MemoryLayout layout) { - return SYMBOL_LOOKUP.lookup(name).map(symbol -> MemorySegment.ofAddress(symbol.address(), layout.byteSize(), MemorySession.openShared())).orElse(null); - } - - static final MethodHandle downcallHandle(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> LINKER.downcallHandle(addr, fdesc)). - orElse(null); - } - - static final MethodHandle downcallHandle(FunctionDescriptor fdesc) { - return LINKER.downcallHandle(fdesc); - } - - static final MethodHandle downcallHandleVariadic(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> VarargsInvoker.make(addr, fdesc)). - orElse(null); - } - - static final MemorySegment upcallStub(Class fi, Z z, FunctionDescriptor fdesc, MemorySession session) { - try { - MethodHandle handle = MH_LOOKUP.findVirtual(fi, "apply", Linker.upcallType(fdesc)); - handle = handle.bindTo(z); - return LINKER.upcallStub(handle, fdesc, session); - } catch (Throwable ex) { - throw new AssertionError(ex); - } - } - - static MemorySegment asArray(MemoryAddress addr, MemoryLayout layout, int numElements, MemorySession session) { - return MemorySegment.ofAddress(addr, numElements * layout.byteSize(), session); - } - - // Internals only below this point - - private static class VarargsInvoker { - private static final MethodHandle INVOKE_MH; - private final MemorySegment symbol; - private final FunctionDescriptor function; - - private VarargsInvoker(MemorySegment symbol, FunctionDescriptor function) { - this.symbol = symbol; - this.function = function; - } - - static { - try { - INVOKE_MH = MethodHandles.lookup().findVirtual(VarargsInvoker.class, "invoke", MethodType.methodType(Object.class, SegmentAllocator.class, Object[].class)); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - static MethodHandle make(MemorySegment symbol, FunctionDescriptor function) { - VarargsInvoker invoker = new VarargsInvoker(symbol, function); - MethodHandle handle = INVOKE_MH.bindTo(invoker).asCollector(Object[].class, function.argumentLayouts().size() + 1); - MethodType mtype = MethodType.methodType(function.returnLayout().isPresent() ? carrier(function.returnLayout().get(), true) : void.class); - for (MemoryLayout layout : function.argumentLayouts()) { - mtype = mtype.appendParameterTypes(carrier(layout, false)); - } - mtype = mtype.appendParameterTypes(Object[].class); - if (mtype.returnType().equals(MemorySegment.class)) { - mtype = mtype.insertParameterTypes(0, SegmentAllocator.class); - } else { - handle = MethodHandles.insertArguments(handle, 0, THROWING_ALLOCATOR); - } - return handle.asType(mtype); - } - - static Class carrier(MemoryLayout layout, boolean ret) { - if (layout instanceof ValueLayout valueLayout) { - return (ret || valueLayout.carrier() != MemoryAddress.class) ? - valueLayout.carrier() : Addressable.class; - } else if (layout instanceof GroupLayout) { - return MemorySegment.class; - } else { - throw new AssertionError("Cannot get here!"); - } - } - - private Object invoke(SegmentAllocator allocator, Object[] args) throws Throwable { - // one trailing Object[] - int nNamedArgs = function.argumentLayouts().size(); - assert(args.length == nNamedArgs + 1); - // The last argument is the array of vararg collector - Object[] unnamedArgs = (Object[]) args[args.length - 1]; - - int argsCount = nNamedArgs + unnamedArgs.length; - Class[] argTypes = new Class[argsCount]; - MemoryLayout[] argLayouts = new MemoryLayout[nNamedArgs + unnamedArgs.length]; - - int pos = 0; - for (pos = 0; pos < nNamedArgs; pos++) { - argLayouts[pos] = function.argumentLayouts().get(pos); - } - - assert pos == nNamedArgs; - for (Object o: unnamedArgs) { - argLayouts[pos] = variadicLayout(normalize(o.getClass())); - pos++; - } - assert pos == argsCount; - - FunctionDescriptor f = (function.returnLayout().isEmpty()) ? - FunctionDescriptor.ofVoid(argLayouts) : - FunctionDescriptor.of(function.returnLayout().get(), argLayouts); - MethodHandle mh = LINKER.downcallHandle(symbol, f); - if (mh.type().returnType() == MemorySegment.class) { - mh = mh.bindTo(allocator); - } - // flatten argument list so that it can be passed to an asSpreader MH - Object[] allArgs = new Object[nNamedArgs + unnamedArgs.length]; - System.arraycopy(args, 0, allArgs, 0, nNamedArgs); - System.arraycopy(unnamedArgs, 0, allArgs, nNamedArgs, unnamedArgs.length); - - return mh.asSpreader(Object[].class, argsCount).invoke(allArgs); - } - - private static Class unboxIfNeeded(Class clazz) { - if (clazz == Boolean.class) { - return boolean.class; - } else if (clazz == Void.class) { - return void.class; - } else if (clazz == Byte.class) { - return byte.class; - } else if (clazz == Character.class) { - return char.class; - } else if (clazz == Short.class) { - return short.class; - } else if (clazz == Integer.class) { - return int.class; - } else if (clazz == Long.class) { - return long.class; - } else if (clazz == Float.class) { - return float.class; - } else if (clazz == Double.class) { - return double.class; - } else { - return clazz; - } - } - - private Class promote(Class c) { - if (c == byte.class || c == char.class || c == short.class || c == int.class) { - return long.class; - } else if (c == float.class) { - return double.class; - } else { - return c; - } - } - - private Class normalize(Class c) { - c = unboxIfNeeded(c); - if (c.isPrimitive()) { - return promote(c); - } - if (MemoryAddress.class.isAssignableFrom(c)) { - return MemoryAddress.class; - } - if (MemorySegment.class.isAssignableFrom(c)) { - return MemorySegment.class; - } - throw new IllegalArgumentException("Invalid type for ABI: " + c.getTypeName()); - } - - private MemoryLayout variadicLayout(Class c) { - if (c == long.class) { - return JAVA_LONG; - } else if (c == double.class) { - return JAVA_DOUBLE; - } else if (MemoryAddress.class.isAssignableFrom(c)) { - return ADDRESS; - } else { - throw new IllegalArgumentException("Unhandled variadic argument class: " + c); - } - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/constants$0.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/constants$0.java deleted file mode 100644 index 962ea187..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/constants$0.java +++ /dev/null @@ -1,52 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.linux.gen.udev; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.invoke.MethodHandle; -class constants$0 { - - static final FunctionDescriptor udev_new$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT); - static final MethodHandle udev_new$MH = RuntimeHelper.downcallHandle( - "udev_new", - constants$0.udev_new$FUNC - ); - static final FunctionDescriptor udev_list_entry_get_next$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle udev_list_entry_get_next$MH = RuntimeHelper.downcallHandle( - "udev_list_entry_get_next", - constants$0.udev_list_entry_get_next$FUNC - ); - static final FunctionDescriptor udev_list_entry_get_name$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle udev_list_entry_get_name$MH = RuntimeHelper.downcallHandle( - "udev_list_entry_get_name", - constants$0.udev_list_entry_get_name$FUNC - ); - static final FunctionDescriptor udev_device_unref$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle udev_device_unref$MH = RuntimeHelper.downcallHandle( - "udev_device_unref", - constants$0.udev_device_unref$FUNC - ); - static final FunctionDescriptor udev_device_new_from_syspath$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle udev_device_new_from_syspath$MH = RuntimeHelper.downcallHandle( - "udev_device_new_from_syspath", - constants$0.udev_device_new_from_syspath$FUNC - ); - static final FunctionDescriptor udev_device_get_devtype$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle udev_device_get_devtype$MH = RuntimeHelper.downcallHandle( - "udev_device_get_devtype", - constants$0.udev_device_get_devtype$FUNC - ); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/constants$1.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/constants$1.java deleted file mode 100644 index 986a90d3..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/constants$1.java +++ /dev/null @@ -1,55 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.linux.gen.udev; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.invoke.MethodHandle; -class constants$1 { - - static final FunctionDescriptor udev_device_get_devnode$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle udev_device_get_devnode$MH = RuntimeHelper.downcallHandle( - "udev_device_get_devnode", - constants$1.udev_device_get_devnode$FUNC - ); - static final FunctionDescriptor udev_device_get_action$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle udev_device_get_action$MH = RuntimeHelper.downcallHandle( - "udev_device_get_action", - constants$1.udev_device_get_action$FUNC - ); - static final FunctionDescriptor udev_device_get_sysattr_value$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle udev_device_get_sysattr_value$MH = RuntimeHelper.downcallHandle( - "udev_device_get_sysattr_value", - constants$1.udev_device_get_sysattr_value$FUNC - ); - static final FunctionDescriptor udev_monitor_new_from_netlink$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle udev_monitor_new_from_netlink$MH = RuntimeHelper.downcallHandle( - "udev_monitor_new_from_netlink", - constants$1.udev_monitor_new_from_netlink$FUNC - ); - static final FunctionDescriptor udev_monitor_enable_receiving$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle udev_monitor_enable_receiving$MH = RuntimeHelper.downcallHandle( - "udev_monitor_enable_receiving", - constants$1.udev_monitor_enable_receiving$FUNC - ); - static final FunctionDescriptor udev_monitor_get_fd$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle udev_monitor_get_fd$MH = RuntimeHelper.downcallHandle( - "udev_monitor_get_fd", - constants$1.udev_monitor_get_fd$FUNC - ); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/constants$2.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/constants$2.java deleted file mode 100644 index 47b95ed6..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/constants$2.java +++ /dev/null @@ -1,56 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.linux.gen.udev; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.invoke.MethodHandle; -class constants$2 { - - static final FunctionDescriptor udev_monitor_receive_device$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle udev_monitor_receive_device$MH = RuntimeHelper.downcallHandle( - "udev_monitor_receive_device", - constants$2.udev_monitor_receive_device$FUNC - ); - static final FunctionDescriptor udev_monitor_filter_add_match_subsystem_devtype$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle udev_monitor_filter_add_match_subsystem_devtype$MH = RuntimeHelper.downcallHandle( - "udev_monitor_filter_add_match_subsystem_devtype", - constants$2.udev_monitor_filter_add_match_subsystem_devtype$FUNC - ); - static final FunctionDescriptor udev_enumerate_unref$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle udev_enumerate_unref$MH = RuntimeHelper.downcallHandle( - "udev_enumerate_unref", - constants$2.udev_enumerate_unref$FUNC - ); - static final FunctionDescriptor udev_enumerate_new$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle udev_enumerate_new$MH = RuntimeHelper.downcallHandle( - "udev_enumerate_new", - constants$2.udev_enumerate_new$FUNC - ); - static final FunctionDescriptor udev_enumerate_add_match_subsystem$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle udev_enumerate_add_match_subsystem$MH = RuntimeHelper.downcallHandle( - "udev_enumerate_add_match_subsystem", - constants$2.udev_enumerate_add_match_subsystem$FUNC - ); - static final FunctionDescriptor udev_enumerate_scan_devices$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle udev_enumerate_scan_devices$MH = RuntimeHelper.downcallHandle( - "udev_enumerate_scan_devices", - constants$2.udev_enumerate_scan_devices$FUNC - ); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/constants$3.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/constants$3.java deleted file mode 100644 index 862a7270..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/constants$3.java +++ /dev/null @@ -1,18 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.linux.gen.udev; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.invoke.MethodHandle; -class constants$3 { - - static final FunctionDescriptor udev_enumerate_get_list_entry$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle udev_enumerate_get_list_entry$MH = RuntimeHelper.downcallHandle( - "udev_enumerate_get_list_entry", - constants$3.udev_enumerate_get_list_entry$FUNC - ); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/udev$shared.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/udev$shared.java new file mode 100644 index 00000000..b7958526 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/udev$shared.java @@ -0,0 +1,63 @@ +// Generated by jextract + +package net.codecrete.usb.linux.gen.udev; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +public class udev$shared { + + udev$shared() { + // Should not be called directly + } + + public static final ValueLayout.OfBoolean C_BOOL = (ValueLayout.OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool"); + public static final ValueLayout.OfByte C_CHAR =(ValueLayout.OfByte)Linker.nativeLinker().canonicalLayouts().get("char"); + public static final ValueLayout.OfShort C_SHORT = (ValueLayout.OfShort) Linker.nativeLinker().canonicalLayouts().get("short"); + public static final ValueLayout.OfInt C_INT = (ValueLayout.OfInt) Linker.nativeLinker().canonicalLayouts().get("int"); + public static final ValueLayout.OfLong C_LONG_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long long"); + public static final ValueLayout.OfFloat C_FLOAT = (ValueLayout.OfFloat) Linker.nativeLinker().canonicalLayouts().get("float"); + public static final ValueLayout.OfDouble C_DOUBLE = (ValueLayout.OfDouble) Linker.nativeLinker().canonicalLayouts().get("double"); + public static final AddressLayout C_POINTER = ((AddressLayout) Linker.nativeLinker().canonicalLayouts().get("void*")) + .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, C_CHAR)); + public static final ValueLayout.OfLong C_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long"); + + static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + + static void traceDowncall(String name, Object... args) { + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("%s(%s)\n", name, traceArgs); + } + + static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { + try { + return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } + + static MemoryLayout align(MemoryLayout layout, long align) { + return switch (layout) { + case PaddingLayout p -> p; + case ValueLayout v -> v.withByteAlignment(align); + case GroupLayout g -> { + MemoryLayout[] alignedMembers = g.memberLayouts().stream() + .map(m -> align(m, align)).toArray(MemoryLayout[]::new); + yield g instanceof StructLayout ? + MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); + } + case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); + }; + } +} + diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/udev.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/udev.java index ea5160ae..f43a699d 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/udev.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/udev/udev.java @@ -2,231 +2,1171 @@ package net.codecrete.usb.linux.gen.udev; -import java.lang.foreign.Addressable; -import java.lang.foreign.MemoryAddress; -import java.lang.invoke.MethodHandle; +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; import static java.lang.foreign.ValueLayout.*; -public class udev { +import static java.lang.foreign.MemoryLayout.PathElement.*; - /* package-private */ udev() {} - public static OfByte C_CHAR = Constants$root.C_CHAR$LAYOUT; - public static OfShort C_SHORT = Constants$root.C_SHORT$LAYOUT; - public static OfInt C_INT = Constants$root.C_INT$LAYOUT; - public static OfLong C_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfLong C_LONG_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfFloat C_FLOAT = Constants$root.C_FLOAT$LAYOUT; - public static OfDouble C_DOUBLE = Constants$root.C_DOUBLE$LAYOUT; - public static OfAddress C_POINTER = Constants$root.C_POINTER$LAYOUT; - public static MethodHandle udev_new$MH() { - return RuntimeHelper.requireNonNull(constants$0.udev_new$MH,"udev_new"); +public class udev extends udev$shared { + + udev() { + // Should not be called directly } - public static MemoryAddress udev_new () { - var mh$ = udev_new$MH(); + + static final Arena LIBRARY_ARENA = Arena.ofAuto(); + + static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.libraryLookup("libudev.so.1", LIBRARY_ARENA) + .or(SymbolLookup.loaderLookup()) + .or(Linker.nativeLinker().defaultLookup()); + + + private static class udev_new { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + udev.C_POINTER ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("udev_new"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * struct udev *udev_new(void) + * } + */ + public static FunctionDescriptor udev_new$descriptor() { + return udev_new.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * struct udev *udev_new(void) + * } + */ + public static MethodHandle udev_new$handle() { + return udev_new.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * struct udev *udev_new(void) + * } + */ + public static MemorySegment udev_new$address() { + return udev_new.ADDR; + } + + /** + * {@snippet lang=c : + * struct udev *udev_new(void) + * } + */ + public static MemorySegment udev_new() { + var mh$ = udev_new.HANDLE; try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(); + if (TRACE_DOWNCALLS) { + traceDowncall("udev_new"); + } + return (MemorySegment)mh$.invokeExact(); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle udev_list_entry_get_next$MH() { - return RuntimeHelper.requireNonNull(constants$0.udev_list_entry_get_next$MH,"udev_list_entry_get_next"); + + private static class udev_list_entry_get_next { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + udev.C_POINTER, + udev.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("udev_list_entry_get_next"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry) + * } + */ + public static FunctionDescriptor udev_list_entry_get_next$descriptor() { + return udev_list_entry_get_next.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry) + * } + */ + public static MethodHandle udev_list_entry_get_next$handle() { + return udev_list_entry_get_next.HANDLE; } - public static MemoryAddress udev_list_entry_get_next ( Addressable list_entry) { - var mh$ = udev_list_entry_get_next$MH(); + + /** + * Address for: + * {@snippet lang=c : + * struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry) + * } + */ + public static MemorySegment udev_list_entry_get_next$address() { + return udev_list_entry_get_next.ADDR; + } + + /** + * {@snippet lang=c : + * struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry) + * } + */ + public static MemorySegment udev_list_entry_get_next(MemorySegment list_entry) { + var mh$ = udev_list_entry_get_next.HANDLE; try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(list_entry); + if (TRACE_DOWNCALLS) { + traceDowncall("udev_list_entry_get_next", list_entry); + } + return (MemorySegment)mh$.invokeExact(list_entry); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle udev_list_entry_get_name$MH() { - return RuntimeHelper.requireNonNull(constants$0.udev_list_entry_get_name$MH,"udev_list_entry_get_name"); + + private static class udev_list_entry_get_name { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + udev.C_POINTER, + udev.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("udev_list_entry_get_name"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * const char *udev_list_entry_get_name(struct udev_list_entry *list_entry) + * } + */ + public static FunctionDescriptor udev_list_entry_get_name$descriptor() { + return udev_list_entry_get_name.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * const char *udev_list_entry_get_name(struct udev_list_entry *list_entry) + * } + */ + public static MethodHandle udev_list_entry_get_name$handle() { + return udev_list_entry_get_name.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * const char *udev_list_entry_get_name(struct udev_list_entry *list_entry) + * } + */ + public static MemorySegment udev_list_entry_get_name$address() { + return udev_list_entry_get_name.ADDR; } - public static MemoryAddress udev_list_entry_get_name ( Addressable list_entry) { - var mh$ = udev_list_entry_get_name$MH(); + + /** + * {@snippet lang=c : + * const char *udev_list_entry_get_name(struct udev_list_entry *list_entry) + * } + */ + public static MemorySegment udev_list_entry_get_name(MemorySegment list_entry) { + var mh$ = udev_list_entry_get_name.HANDLE; try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(list_entry); + if (TRACE_DOWNCALLS) { + traceDowncall("udev_list_entry_get_name", list_entry); + } + return (MemorySegment)mh$.invokeExact(list_entry); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle udev_device_unref$MH() { - return RuntimeHelper.requireNonNull(constants$0.udev_device_unref$MH,"udev_device_unref"); + + private static class udev_device_unref { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + udev.C_POINTER, + udev.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("udev_device_unref"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * struct udev_device *udev_device_unref(struct udev_device *udev_device) + * } + */ + public static FunctionDescriptor udev_device_unref$descriptor() { + return udev_device_unref.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * struct udev_device *udev_device_unref(struct udev_device *udev_device) + * } + */ + public static MethodHandle udev_device_unref$handle() { + return udev_device_unref.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * struct udev_device *udev_device_unref(struct udev_device *udev_device) + * } + */ + public static MemorySegment udev_device_unref$address() { + return udev_device_unref.ADDR; } - public static MemoryAddress udev_device_unref ( Addressable udev_device) { - var mh$ = udev_device_unref$MH(); + + /** + * {@snippet lang=c : + * struct udev_device *udev_device_unref(struct udev_device *udev_device) + * } + */ + public static MemorySegment udev_device_unref(MemorySegment udev_device) { + var mh$ = udev_device_unref.HANDLE; try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(udev_device); + if (TRACE_DOWNCALLS) { + traceDowncall("udev_device_unref", udev_device); + } + return (MemorySegment)mh$.invokeExact(udev_device); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle udev_device_new_from_syspath$MH() { - return RuntimeHelper.requireNonNull(constants$0.udev_device_new_from_syspath$MH,"udev_device_new_from_syspath"); + + private static class udev_device_new_from_syspath { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + udev.C_POINTER, + udev.C_POINTER, + udev.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("udev_device_new_from_syspath"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath) + * } + */ + public static FunctionDescriptor udev_device_new_from_syspath$descriptor() { + return udev_device_new_from_syspath.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath) + * } + */ + public static MethodHandle udev_device_new_from_syspath$handle() { + return udev_device_new_from_syspath.HANDLE; } - public static MemoryAddress udev_device_new_from_syspath ( Addressable udev, Addressable syspath) { - var mh$ = udev_device_new_from_syspath$MH(); + + /** + * Address for: + * {@snippet lang=c : + * struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath) + * } + */ + public static MemorySegment udev_device_new_from_syspath$address() { + return udev_device_new_from_syspath.ADDR; + } + + /** + * {@snippet lang=c : + * struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath) + * } + */ + public static MemorySegment udev_device_new_from_syspath(MemorySegment udev, MemorySegment syspath) { + var mh$ = udev_device_new_from_syspath.HANDLE; try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(udev, syspath); + if (TRACE_DOWNCALLS) { + traceDowncall("udev_device_new_from_syspath", udev, syspath); + } + return (MemorySegment)mh$.invokeExact(udev, syspath); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle udev_device_get_devtype$MH() { - return RuntimeHelper.requireNonNull(constants$0.udev_device_get_devtype$MH,"udev_device_get_devtype"); + + private static class udev_device_get_devtype { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + udev.C_POINTER, + udev.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("udev_device_get_devtype"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * const char *udev_device_get_devtype(struct udev_device *udev_device) + * } + */ + public static FunctionDescriptor udev_device_get_devtype$descriptor() { + return udev_device_get_devtype.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * const char *udev_device_get_devtype(struct udev_device *udev_device) + * } + */ + public static MethodHandle udev_device_get_devtype$handle() { + return udev_device_get_devtype.HANDLE; } - public static MemoryAddress udev_device_get_devtype ( Addressable udev_device) { - var mh$ = udev_device_get_devtype$MH(); + + /** + * Address for: + * {@snippet lang=c : + * const char *udev_device_get_devtype(struct udev_device *udev_device) + * } + */ + public static MemorySegment udev_device_get_devtype$address() { + return udev_device_get_devtype.ADDR; + } + + /** + * {@snippet lang=c : + * const char *udev_device_get_devtype(struct udev_device *udev_device) + * } + */ + public static MemorySegment udev_device_get_devtype(MemorySegment udev_device) { + var mh$ = udev_device_get_devtype.HANDLE; try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(udev_device); + if (TRACE_DOWNCALLS) { + traceDowncall("udev_device_get_devtype", udev_device); + } + return (MemorySegment)mh$.invokeExact(udev_device); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle udev_device_get_devnode$MH() { - return RuntimeHelper.requireNonNull(constants$1.udev_device_get_devnode$MH,"udev_device_get_devnode"); + + private static class udev_device_get_devnode { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + udev.C_POINTER, + udev.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("udev_device_get_devnode"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * const char *udev_device_get_devnode(struct udev_device *udev_device) + * } + */ + public static FunctionDescriptor udev_device_get_devnode$descriptor() { + return udev_device_get_devnode.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * const char *udev_device_get_devnode(struct udev_device *udev_device) + * } + */ + public static MethodHandle udev_device_get_devnode$handle() { + return udev_device_get_devnode.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * const char *udev_device_get_devnode(struct udev_device *udev_device) + * } + */ + public static MemorySegment udev_device_get_devnode$address() { + return udev_device_get_devnode.ADDR; } - public static MemoryAddress udev_device_get_devnode ( Addressable udev_device) { - var mh$ = udev_device_get_devnode$MH(); + + /** + * {@snippet lang=c : + * const char *udev_device_get_devnode(struct udev_device *udev_device) + * } + */ + public static MemorySegment udev_device_get_devnode(MemorySegment udev_device) { + var mh$ = udev_device_get_devnode.HANDLE; try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(udev_device); + if (TRACE_DOWNCALLS) { + traceDowncall("udev_device_get_devnode", udev_device); + } + return (MemorySegment)mh$.invokeExact(udev_device); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle udev_device_get_action$MH() { - return RuntimeHelper.requireNonNull(constants$1.udev_device_get_action$MH,"udev_device_get_action"); + + private static class udev_device_get_action { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + udev.C_POINTER, + udev.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("udev_device_get_action"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * const char *udev_device_get_action(struct udev_device *udev_device) + * } + */ + public static FunctionDescriptor udev_device_get_action$descriptor() { + return udev_device_get_action.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * const char *udev_device_get_action(struct udev_device *udev_device) + * } + */ + public static MethodHandle udev_device_get_action$handle() { + return udev_device_get_action.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * const char *udev_device_get_action(struct udev_device *udev_device) + * } + */ + public static MemorySegment udev_device_get_action$address() { + return udev_device_get_action.ADDR; } - public static MemoryAddress udev_device_get_action ( Addressable udev_device) { - var mh$ = udev_device_get_action$MH(); + + /** + * {@snippet lang=c : + * const char *udev_device_get_action(struct udev_device *udev_device) + * } + */ + public static MemorySegment udev_device_get_action(MemorySegment udev_device) { + var mh$ = udev_device_get_action.HANDLE; try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(udev_device); + if (TRACE_DOWNCALLS) { + traceDowncall("udev_device_get_action", udev_device); + } + return (MemorySegment)mh$.invokeExact(udev_device); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle udev_device_get_sysattr_value$MH() { - return RuntimeHelper.requireNonNull(constants$1.udev_device_get_sysattr_value$MH,"udev_device_get_sysattr_value"); + + private static class udev_device_get_sysattr_value { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + udev.C_POINTER, + udev.C_POINTER, + udev.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("udev_device_get_sysattr_value"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr) + * } + */ + public static FunctionDescriptor udev_device_get_sysattr_value$descriptor() { + return udev_device_get_sysattr_value.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr) + * } + */ + public static MethodHandle udev_device_get_sysattr_value$handle() { + return udev_device_get_sysattr_value.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr) + * } + */ + public static MemorySegment udev_device_get_sysattr_value$address() { + return udev_device_get_sysattr_value.ADDR; } - public static MemoryAddress udev_device_get_sysattr_value ( Addressable udev_device, Addressable sysattr) { - var mh$ = udev_device_get_sysattr_value$MH(); + + /** + * {@snippet lang=c : + * const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr) + * } + */ + public static MemorySegment udev_device_get_sysattr_value(MemorySegment udev_device, MemorySegment sysattr) { + var mh$ = udev_device_get_sysattr_value.HANDLE; try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(udev_device, sysattr); + if (TRACE_DOWNCALLS) { + traceDowncall("udev_device_get_sysattr_value", udev_device, sysattr); + } + return (MemorySegment)mh$.invokeExact(udev_device, sysattr); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle udev_monitor_new_from_netlink$MH() { - return RuntimeHelper.requireNonNull(constants$1.udev_monitor_new_from_netlink$MH,"udev_monitor_new_from_netlink"); + + private static class udev_monitor_new_from_netlink { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + udev.C_POINTER, + udev.C_POINTER, + udev.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("udev_monitor_new_from_netlink"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name) + * } + */ + public static FunctionDescriptor udev_monitor_new_from_netlink$descriptor() { + return udev_monitor_new_from_netlink.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name) + * } + */ + public static MethodHandle udev_monitor_new_from_netlink$handle() { + return udev_monitor_new_from_netlink.HANDLE; } - public static MemoryAddress udev_monitor_new_from_netlink ( Addressable udev, Addressable name) { - var mh$ = udev_monitor_new_from_netlink$MH(); + + /** + * Address for: + * {@snippet lang=c : + * struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name) + * } + */ + public static MemorySegment udev_monitor_new_from_netlink$address() { + return udev_monitor_new_from_netlink.ADDR; + } + + /** + * {@snippet lang=c : + * struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name) + * } + */ + public static MemorySegment udev_monitor_new_from_netlink(MemorySegment udev, MemorySegment name) { + var mh$ = udev_monitor_new_from_netlink.HANDLE; try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(udev, name); + if (TRACE_DOWNCALLS) { + traceDowncall("udev_monitor_new_from_netlink", udev, name); + } + return (MemorySegment)mh$.invokeExact(udev, name); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle udev_monitor_enable_receiving$MH() { - return RuntimeHelper.requireNonNull(constants$1.udev_monitor_enable_receiving$MH,"udev_monitor_enable_receiving"); + + private static class udev_monitor_enable_receiving { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + udev.C_INT, + udev.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("udev_monitor_enable_receiving"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } - public static int udev_monitor_enable_receiving ( Addressable udev_monitor) { - var mh$ = udev_monitor_enable_receiving$MH(); + + /** + * Function descriptor for: + * {@snippet lang=c : + * int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) + * } + */ + public static FunctionDescriptor udev_monitor_enable_receiving$descriptor() { + return udev_monitor_enable_receiving.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) + * } + */ + public static MethodHandle udev_monitor_enable_receiving$handle() { + return udev_monitor_enable_receiving.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) + * } + */ + public static MemorySegment udev_monitor_enable_receiving$address() { + return udev_monitor_enable_receiving.ADDR; + } + + /** + * {@snippet lang=c : + * int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) + * } + */ + public static int udev_monitor_enable_receiving(MemorySegment udev_monitor) { + var mh$ = udev_monitor_enable_receiving.HANDLE; try { + if (TRACE_DOWNCALLS) { + traceDowncall("udev_monitor_enable_receiving", udev_monitor); + } return (int)mh$.invokeExact(udev_monitor); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle udev_monitor_get_fd$MH() { - return RuntimeHelper.requireNonNull(constants$1.udev_monitor_get_fd$MH,"udev_monitor_get_fd"); + + private static class udev_monitor_get_fd { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + udev.C_INT, + udev.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("udev_monitor_get_fd"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * int udev_monitor_get_fd(struct udev_monitor *udev_monitor) + * } + */ + public static FunctionDescriptor udev_monitor_get_fd$descriptor() { + return udev_monitor_get_fd.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * int udev_monitor_get_fd(struct udev_monitor *udev_monitor) + * } + */ + public static MethodHandle udev_monitor_get_fd$handle() { + return udev_monitor_get_fd.HANDLE; } - public static int udev_monitor_get_fd ( Addressable udev_monitor) { - var mh$ = udev_monitor_get_fd$MH(); + + /** + * Address for: + * {@snippet lang=c : + * int udev_monitor_get_fd(struct udev_monitor *udev_monitor) + * } + */ + public static MemorySegment udev_monitor_get_fd$address() { + return udev_monitor_get_fd.ADDR; + } + + /** + * {@snippet lang=c : + * int udev_monitor_get_fd(struct udev_monitor *udev_monitor) + * } + */ + public static int udev_monitor_get_fd(MemorySegment udev_monitor) { + var mh$ = udev_monitor_get_fd.HANDLE; try { + if (TRACE_DOWNCALLS) { + traceDowncall("udev_monitor_get_fd", udev_monitor); + } return (int)mh$.invokeExact(udev_monitor); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle udev_monitor_receive_device$MH() { - return RuntimeHelper.requireNonNull(constants$2.udev_monitor_receive_device$MH,"udev_monitor_receive_device"); + + private static class udev_monitor_receive_device { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + udev.C_POINTER, + udev.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("udev_monitor_receive_device"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor) + * } + */ + public static FunctionDescriptor udev_monitor_receive_device$descriptor() { + return udev_monitor_receive_device.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor) + * } + */ + public static MethodHandle udev_monitor_receive_device$handle() { + return udev_monitor_receive_device.HANDLE; } - public static MemoryAddress udev_monitor_receive_device ( Addressable udev_monitor) { - var mh$ = udev_monitor_receive_device$MH(); + + /** + * Address for: + * {@snippet lang=c : + * struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor) + * } + */ + public static MemorySegment udev_monitor_receive_device$address() { + return udev_monitor_receive_device.ADDR; + } + + /** + * {@snippet lang=c : + * struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor) + * } + */ + public static MemorySegment udev_monitor_receive_device(MemorySegment udev_monitor) { + var mh$ = udev_monitor_receive_device.HANDLE; try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(udev_monitor); + if (TRACE_DOWNCALLS) { + traceDowncall("udev_monitor_receive_device", udev_monitor); + } + return (MemorySegment)mh$.invokeExact(udev_monitor); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle udev_monitor_filter_add_match_subsystem_devtype$MH() { - return RuntimeHelper.requireNonNull(constants$2.udev_monitor_filter_add_match_subsystem_devtype$MH,"udev_monitor_filter_add_match_subsystem_devtype"); + + private static class udev_monitor_filter_add_match_subsystem_devtype { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + udev.C_INT, + udev.C_POINTER, + udev.C_POINTER, + udev.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("udev_monitor_filter_add_match_subsystem_devtype"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype) + * } + */ + public static FunctionDescriptor udev_monitor_filter_add_match_subsystem_devtype$descriptor() { + return udev_monitor_filter_add_match_subsystem_devtype.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype) + * } + */ + public static MethodHandle udev_monitor_filter_add_match_subsystem_devtype$handle() { + return udev_monitor_filter_add_match_subsystem_devtype.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype) + * } + */ + public static MemorySegment udev_monitor_filter_add_match_subsystem_devtype$address() { + return udev_monitor_filter_add_match_subsystem_devtype.ADDR; } - public static int udev_monitor_filter_add_match_subsystem_devtype ( Addressable udev_monitor, Addressable subsystem, Addressable devtype) { - var mh$ = udev_monitor_filter_add_match_subsystem_devtype$MH(); + + /** + * {@snippet lang=c : + * int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype) + * } + */ + public static int udev_monitor_filter_add_match_subsystem_devtype(MemorySegment udev_monitor, MemorySegment subsystem, MemorySegment devtype) { + var mh$ = udev_monitor_filter_add_match_subsystem_devtype.HANDLE; try { + if (TRACE_DOWNCALLS) { + traceDowncall("udev_monitor_filter_add_match_subsystem_devtype", udev_monitor, subsystem, devtype); + } return (int)mh$.invokeExact(udev_monitor, subsystem, devtype); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle udev_enumerate_unref$MH() { - return RuntimeHelper.requireNonNull(constants$2.udev_enumerate_unref$MH,"udev_enumerate_unref"); + + private static class udev_enumerate_unref { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + udev.C_POINTER, + udev.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("udev_enumerate_unref"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * struct udev_enumerate *udev_enumerate_unref(struct udev_enumerate *udev_enumerate) + * } + */ + public static FunctionDescriptor udev_enumerate_unref$descriptor() { + return udev_enumerate_unref.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * struct udev_enumerate *udev_enumerate_unref(struct udev_enumerate *udev_enumerate) + * } + */ + public static MethodHandle udev_enumerate_unref$handle() { + return udev_enumerate_unref.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * struct udev_enumerate *udev_enumerate_unref(struct udev_enumerate *udev_enumerate) + * } + */ + public static MemorySegment udev_enumerate_unref$address() { + return udev_enumerate_unref.ADDR; } - public static MemoryAddress udev_enumerate_unref ( Addressable udev_enumerate) { - var mh$ = udev_enumerate_unref$MH(); + + /** + * {@snippet lang=c : + * struct udev_enumerate *udev_enumerate_unref(struct udev_enumerate *udev_enumerate) + * } + */ + public static MemorySegment udev_enumerate_unref(MemorySegment udev_enumerate) { + var mh$ = udev_enumerate_unref.HANDLE; try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(udev_enumerate); + if (TRACE_DOWNCALLS) { + traceDowncall("udev_enumerate_unref", udev_enumerate); + } + return (MemorySegment)mh$.invokeExact(udev_enumerate); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle udev_enumerate_new$MH() { - return RuntimeHelper.requireNonNull(constants$2.udev_enumerate_new$MH,"udev_enumerate_new"); + + private static class udev_enumerate_new { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + udev.C_POINTER, + udev.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("udev_enumerate_new"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * struct udev_enumerate *udev_enumerate_new(struct udev *udev) + * } + */ + public static FunctionDescriptor udev_enumerate_new$descriptor() { + return udev_enumerate_new.DESC; } - public static MemoryAddress udev_enumerate_new ( Addressable udev) { - var mh$ = udev_enumerate_new$MH(); + + /** + * Downcall method handle for: + * {@snippet lang=c : + * struct udev_enumerate *udev_enumerate_new(struct udev *udev) + * } + */ + public static MethodHandle udev_enumerate_new$handle() { + return udev_enumerate_new.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * struct udev_enumerate *udev_enumerate_new(struct udev *udev) + * } + */ + public static MemorySegment udev_enumerate_new$address() { + return udev_enumerate_new.ADDR; + } + + /** + * {@snippet lang=c : + * struct udev_enumerate *udev_enumerate_new(struct udev *udev) + * } + */ + public static MemorySegment udev_enumerate_new(MemorySegment udev) { + var mh$ = udev_enumerate_new.HANDLE; try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(udev); + if (TRACE_DOWNCALLS) { + traceDowncall("udev_enumerate_new", udev); + } + return (MemorySegment)mh$.invokeExact(udev); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle udev_enumerate_add_match_subsystem$MH() { - return RuntimeHelper.requireNonNull(constants$2.udev_enumerate_add_match_subsystem$MH,"udev_enumerate_add_match_subsystem"); + + private static class udev_enumerate_add_match_subsystem { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + udev.C_INT, + udev.C_POINTER, + udev.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("udev_enumerate_add_match_subsystem"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) + * } + */ + public static FunctionDescriptor udev_enumerate_add_match_subsystem$descriptor() { + return udev_enumerate_add_match_subsystem.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) + * } + */ + public static MethodHandle udev_enumerate_add_match_subsystem$handle() { + return udev_enumerate_add_match_subsystem.HANDLE; } - public static int udev_enumerate_add_match_subsystem ( Addressable udev_enumerate, Addressable subsystem) { - var mh$ = udev_enumerate_add_match_subsystem$MH(); + + /** + * Address for: + * {@snippet lang=c : + * int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) + * } + */ + public static MemorySegment udev_enumerate_add_match_subsystem$address() { + return udev_enumerate_add_match_subsystem.ADDR; + } + + /** + * {@snippet lang=c : + * int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) + * } + */ + public static int udev_enumerate_add_match_subsystem(MemorySegment udev_enumerate, MemorySegment subsystem) { + var mh$ = udev_enumerate_add_match_subsystem.HANDLE; try { + if (TRACE_DOWNCALLS) { + traceDowncall("udev_enumerate_add_match_subsystem", udev_enumerate, subsystem); + } return (int)mh$.invokeExact(udev_enumerate, subsystem); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle udev_enumerate_scan_devices$MH() { - return RuntimeHelper.requireNonNull(constants$2.udev_enumerate_scan_devices$MH,"udev_enumerate_scan_devices"); + + private static class udev_enumerate_scan_devices { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + udev.C_INT, + udev.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("udev_enumerate_scan_devices"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate) + * } + */ + public static FunctionDescriptor udev_enumerate_scan_devices$descriptor() { + return udev_enumerate_scan_devices.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate) + * } + */ + public static MethodHandle udev_enumerate_scan_devices$handle() { + return udev_enumerate_scan_devices.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate) + * } + */ + public static MemorySegment udev_enumerate_scan_devices$address() { + return udev_enumerate_scan_devices.ADDR; } - public static int udev_enumerate_scan_devices ( Addressable udev_enumerate) { - var mh$ = udev_enumerate_scan_devices$MH(); + + /** + * {@snippet lang=c : + * int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate) + * } + */ + public static int udev_enumerate_scan_devices(MemorySegment udev_enumerate) { + var mh$ = udev_enumerate_scan_devices.HANDLE; try { + if (TRACE_DOWNCALLS) { + traceDowncall("udev_enumerate_scan_devices", udev_enumerate); + } return (int)mh$.invokeExact(udev_enumerate); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle udev_enumerate_get_list_entry$MH() { - return RuntimeHelper.requireNonNull(constants$3.udev_enumerate_get_list_entry$MH,"udev_enumerate_get_list_entry"); + + private static class udev_enumerate_get_list_entry { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + udev.C_POINTER, + udev.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("udev_enumerate_get_list_entry"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate) + * } + */ + public static FunctionDescriptor udev_enumerate_get_list_entry$descriptor() { + return udev_enumerate_get_list_entry.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate) + * } + */ + public static MethodHandle udev_enumerate_get_list_entry$handle() { + return udev_enumerate_get_list_entry.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate) + * } + */ + public static MemorySegment udev_enumerate_get_list_entry$address() { + return udev_enumerate_get_list_entry.ADDR; } - public static MemoryAddress udev_enumerate_get_list_entry ( Addressable udev_enumerate) { - var mh$ = udev_enumerate_get_list_entry$MH(); + + /** + * {@snippet lang=c : + * struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate) + * } + */ + public static MemorySegment udev_enumerate_get_list_entry(MemorySegment udev_enumerate) { + var mh$ = udev_enumerate_get_list_entry.HANDLE; try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(udev_enumerate); + if (TRACE_DOWNCALLS) { + traceDowncall("udev_enumerate_get_list_entry", udev_enumerate); + } + return (MemorySegment)mh$.invokeExact(udev_enumerate); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } } - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/unistd/Constants$root.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/unistd/Constants$root.java deleted file mode 100644 index 1fe4e3c0..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/unistd/Constants$root.java +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.linux.gen.unistd; - -import static java.lang.foreign.ValueLayout.*; -public class Constants$root { - - static final OfBoolean C_BOOL$LAYOUT = JAVA_BOOLEAN; - static final OfByte C_CHAR$LAYOUT = JAVA_BYTE; - static final OfShort C_SHORT$LAYOUT = JAVA_SHORT.withBitAlignment(16); - static final OfInt C_INT$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfLong C_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfLong C_LONG_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfFloat C_FLOAT$LAYOUT = JAVA_FLOAT.withBitAlignment(32); - static final OfDouble C_DOUBLE$LAYOUT = JAVA_DOUBLE.withBitAlignment(64); - static final OfAddress C_POINTER$LAYOUT = ADDRESS.withBitAlignment(64); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/unistd/RuntimeHelper.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/unistd/RuntimeHelper.java deleted file mode 100644 index b771271b..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/unistd/RuntimeHelper.java +++ /dev/null @@ -1,216 +0,0 @@ -package net.codecrete.usb.linux.gen.unistd; -// Generated by jextract - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import static java.lang.foreign.ValueLayout.*; - -final class RuntimeHelper { - - private RuntimeHelper() {} - private final static Linker LINKER = Linker.nativeLinker(); - private final static ClassLoader LOADER = RuntimeHelper.class.getClassLoader(); - private final static MethodHandles.Lookup MH_LOOKUP = MethodHandles.lookup(); - private final static SymbolLookup SYMBOL_LOOKUP; - - final static SegmentAllocator CONSTANT_ALLOCATOR = - (size, align) -> MemorySegment.allocateNative(size, align, MemorySession.openImplicit()); - - static { - - SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); - SYMBOL_LOOKUP = name -> loaderLookup.lookup(name).or(() -> LINKER.defaultLookup().lookup(name)); - } - - static T requireNonNull(T obj, String symbolName) { - if (obj == null) { - throw new UnsatisfiedLinkError("unresolved symbol: " + symbolName); - } - return obj; - } - - private final static SegmentAllocator THROWING_ALLOCATOR = (x, y) -> { throw new AssertionError("should not reach here"); }; - - static final MemorySegment lookupGlobalVariable(String name, MemoryLayout layout) { - return SYMBOL_LOOKUP.lookup(name).map(symbol -> MemorySegment.ofAddress(symbol.address(), layout.byteSize(), MemorySession.openShared())).orElse(null); - } - - static final MethodHandle downcallHandle(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> LINKER.downcallHandle(addr, fdesc)). - orElse(null); - } - - static final MethodHandle downcallHandle(FunctionDescriptor fdesc) { - return LINKER.downcallHandle(fdesc); - } - - static final MethodHandle downcallHandleVariadic(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> VarargsInvoker.make(addr, fdesc)). - orElse(null); - } - - static final MemorySegment upcallStub(Class fi, Z z, FunctionDescriptor fdesc, MemorySession session) { - try { - MethodHandle handle = MH_LOOKUP.findVirtual(fi, "apply", Linker.upcallType(fdesc)); - handle = handle.bindTo(z); - return LINKER.upcallStub(handle, fdesc, session); - } catch (Throwable ex) { - throw new AssertionError(ex); - } - } - - static MemorySegment asArray(MemoryAddress addr, MemoryLayout layout, int numElements, MemorySession session) { - return MemorySegment.ofAddress(addr, numElements * layout.byteSize(), session); - } - - // Internals only below this point - - private static class VarargsInvoker { - private static final MethodHandle INVOKE_MH; - private final MemorySegment symbol; - private final FunctionDescriptor function; - - private VarargsInvoker(MemorySegment symbol, FunctionDescriptor function) { - this.symbol = symbol; - this.function = function; - } - - static { - try { - INVOKE_MH = MethodHandles.lookup().findVirtual(VarargsInvoker.class, "invoke", MethodType.methodType(Object.class, SegmentAllocator.class, Object[].class)); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - static MethodHandle make(MemorySegment symbol, FunctionDescriptor function) { - VarargsInvoker invoker = new VarargsInvoker(symbol, function); - MethodHandle handle = INVOKE_MH.bindTo(invoker).asCollector(Object[].class, function.argumentLayouts().size() + 1); - MethodType mtype = MethodType.methodType(function.returnLayout().isPresent() ? carrier(function.returnLayout().get(), true) : void.class); - for (MemoryLayout layout : function.argumentLayouts()) { - mtype = mtype.appendParameterTypes(carrier(layout, false)); - } - mtype = mtype.appendParameterTypes(Object[].class); - if (mtype.returnType().equals(MemorySegment.class)) { - mtype = mtype.insertParameterTypes(0, SegmentAllocator.class); - } else { - handle = MethodHandles.insertArguments(handle, 0, THROWING_ALLOCATOR); - } - return handle.asType(mtype); - } - - static Class carrier(MemoryLayout layout, boolean ret) { - if (layout instanceof ValueLayout valueLayout) { - return (ret || valueLayout.carrier() != MemoryAddress.class) ? - valueLayout.carrier() : Addressable.class; - } else if (layout instanceof GroupLayout) { - return MemorySegment.class; - } else { - throw new AssertionError("Cannot get here!"); - } - } - - private Object invoke(SegmentAllocator allocator, Object[] args) throws Throwable { - // one trailing Object[] - int nNamedArgs = function.argumentLayouts().size(); - assert(args.length == nNamedArgs + 1); - // The last argument is the array of vararg collector - Object[] unnamedArgs = (Object[]) args[args.length - 1]; - - int argsCount = nNamedArgs + unnamedArgs.length; - Class[] argTypes = new Class[argsCount]; - MemoryLayout[] argLayouts = new MemoryLayout[nNamedArgs + unnamedArgs.length]; - - int pos = 0; - for (pos = 0; pos < nNamedArgs; pos++) { - argLayouts[pos] = function.argumentLayouts().get(pos); - } - - assert pos == nNamedArgs; - for (Object o: unnamedArgs) { - argLayouts[pos] = variadicLayout(normalize(o.getClass())); - pos++; - } - assert pos == argsCount; - - FunctionDescriptor f = (function.returnLayout().isEmpty()) ? - FunctionDescriptor.ofVoid(argLayouts) : - FunctionDescriptor.of(function.returnLayout().get(), argLayouts); - MethodHandle mh = LINKER.downcallHandle(symbol, f); - if (mh.type().returnType() == MemorySegment.class) { - mh = mh.bindTo(allocator); - } - // flatten argument list so that it can be passed to an asSpreader MH - Object[] allArgs = new Object[nNamedArgs + unnamedArgs.length]; - System.arraycopy(args, 0, allArgs, 0, nNamedArgs); - System.arraycopy(unnamedArgs, 0, allArgs, nNamedArgs, unnamedArgs.length); - - return mh.asSpreader(Object[].class, argsCount).invoke(allArgs); - } - - private static Class unboxIfNeeded(Class clazz) { - if (clazz == Boolean.class) { - return boolean.class; - } else if (clazz == Void.class) { - return void.class; - } else if (clazz == Byte.class) { - return byte.class; - } else if (clazz == Character.class) { - return char.class; - } else if (clazz == Short.class) { - return short.class; - } else if (clazz == Integer.class) { - return int.class; - } else if (clazz == Long.class) { - return long.class; - } else if (clazz == Float.class) { - return float.class; - } else if (clazz == Double.class) { - return double.class; - } else { - return clazz; - } - } - - private Class promote(Class c) { - if (c == byte.class || c == char.class || c == short.class || c == int.class) { - return long.class; - } else if (c == float.class) { - return double.class; - } else { - return c; - } - } - - private Class normalize(Class c) { - c = unboxIfNeeded(c); - if (c.isPrimitive()) { - return promote(c); - } - if (MemoryAddress.class.isAssignableFrom(c)) { - return MemoryAddress.class; - } - if (MemorySegment.class.isAssignableFrom(c)) { - return MemorySegment.class; - } - throw new IllegalArgumentException("Invalid type for ABI: " + c.getTypeName()); - } - - private MemoryLayout variadicLayout(Class c) { - if (c == long.class) { - return JAVA_LONG; - } else if (c == double.class) { - return JAVA_DOUBLE; - } else if (MemoryAddress.class.isAssignableFrom(c)) { - return ADDRESS; - } else { - throw new IllegalArgumentException("Unhandled variadic argument class: " + c); - } - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/unistd/constants$0.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/unistd/constants$0.java deleted file mode 100644 index 26995ca4..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/unistd/constants$0.java +++ /dev/null @@ -1,18 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.linux.gen.unistd; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.invoke.MethodHandle; -class constants$0 { - - static final FunctionDescriptor close$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT - ); - static final MethodHandle close$MH = RuntimeHelper.downcallHandle( - "close", - constants$0.close$FUNC - ); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/unistd/unistd$shared.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/unistd/unistd$shared.java new file mode 100644 index 00000000..11064c54 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/unistd/unistd$shared.java @@ -0,0 +1,63 @@ +// Generated by jextract + +package net.codecrete.usb.linux.gen.unistd; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +public class unistd$shared { + + unistd$shared() { + // Should not be called directly + } + + public static final ValueLayout.OfBoolean C_BOOL = (ValueLayout.OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool"); + public static final ValueLayout.OfByte C_CHAR =(ValueLayout.OfByte)Linker.nativeLinker().canonicalLayouts().get("char"); + public static final ValueLayout.OfShort C_SHORT = (ValueLayout.OfShort) Linker.nativeLinker().canonicalLayouts().get("short"); + public static final ValueLayout.OfInt C_INT = (ValueLayout.OfInt) Linker.nativeLinker().canonicalLayouts().get("int"); + public static final ValueLayout.OfLong C_LONG_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long long"); + public static final ValueLayout.OfFloat C_FLOAT = (ValueLayout.OfFloat) Linker.nativeLinker().canonicalLayouts().get("float"); + public static final ValueLayout.OfDouble C_DOUBLE = (ValueLayout.OfDouble) Linker.nativeLinker().canonicalLayouts().get("double"); + public static final AddressLayout C_POINTER = ((AddressLayout) Linker.nativeLinker().canonicalLayouts().get("void*")) + .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, C_CHAR)); + public static final ValueLayout.OfLong C_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long"); + + static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + + static void traceDowncall(String name, Object... args) { + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("%s(%s)\n", name, traceArgs); + } + + static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { + try { + return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } + + static MemoryLayout align(MemoryLayout layout, long align) { + return switch (layout) { + case PaddingLayout p -> p; + case ValueLayout v -> v.withByteAlignment(align); + case GroupLayout g -> { + MemoryLayout[] alignedMembers = g.memberLayouts().stream() + .map(m -> align(m, align)).toArray(MemoryLayout[]::new); + yield g instanceof StructLayout ? + MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); + } + case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); + }; + } +} + diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/unistd/unistd.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/unistd/unistd.java index 74045e41..e0f89fcf 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/unistd/unistd.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/unistd/unistd.java @@ -2,31 +2,86 @@ package net.codecrete.usb.linux.gen.unistd; -import java.lang.invoke.MethodHandle; +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; import static java.lang.foreign.ValueLayout.*; -public class unistd { - - /* package-private */ unistd() {} - public static OfByte C_CHAR = Constants$root.C_CHAR$LAYOUT; - public static OfShort C_SHORT = Constants$root.C_SHORT$LAYOUT; - public static OfInt C_INT = Constants$root.C_INT$LAYOUT; - public static OfLong C_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfLong C_LONG_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfFloat C_FLOAT = Constants$root.C_FLOAT$LAYOUT; - public static OfDouble C_DOUBLE = Constants$root.C_DOUBLE$LAYOUT; - public static OfAddress C_POINTER = Constants$root.C_POINTER$LAYOUT; - public static MethodHandle close$MH() { - return RuntimeHelper.requireNonNull(constants$0.close$MH,"close"); +import static java.lang.foreign.MemoryLayout.PathElement.*; + +public class unistd extends unistd$shared { + + unistd() { + // Should not be called directly + } + + static final Arena LIBRARY_ARENA = Arena.ofAuto(); + + static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup() + .or(Linker.nativeLinker().defaultLookup()); + + + private static class close { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + unistd.C_INT, + unistd.C_INT + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("close"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } - public static int close ( int __fd) { - var mh$ = close$MH(); + + /** + * Function descriptor for: + * {@snippet lang=c : + * extern int close(int __fd) + * } + */ + public static FunctionDescriptor close$descriptor() { + return close.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * extern int close(int __fd) + * } + */ + public static MethodHandle close$handle() { + return close.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * extern int close(int __fd) + * } + */ + public static MemorySegment close$address() { + return close.ADDR; + } + + /** + * {@snippet lang=c : + * extern int close(int __fd) + * } + */ + public static int close(int __fd) { + var mh$ = close.HANDLE; try { + if (TRACE_DOWNCALLS) { + traceDowncall("close", __fd); + } return (int)mh$.invokeExact(__fd); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } } - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/Constants$root.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/Constants$root.java deleted file mode 100644 index e013f088..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/Constants$root.java +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.linux.gen.usbdevice_fs; - -import static java.lang.foreign.ValueLayout.*; -public class Constants$root { - - static final OfBoolean C_BOOL$LAYOUT = JAVA_BOOLEAN; - static final OfByte C_CHAR$LAYOUT = JAVA_BYTE; - static final OfShort C_SHORT$LAYOUT = JAVA_SHORT.withBitAlignment(16); - static final OfInt C_INT$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfLong C_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfLong C_LONG_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfFloat C_FLOAT$LAYOUT = JAVA_FLOAT.withBitAlignment(32); - static final OfDouble C_DOUBLE$LAYOUT = JAVA_DOUBLE.withBitAlignment(64); - static final OfAddress C_POINTER$LAYOUT = ADDRESS.withBitAlignment(64); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/RuntimeHelper.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/RuntimeHelper.java deleted file mode 100644 index 8d30b2af..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/RuntimeHelper.java +++ /dev/null @@ -1,216 +0,0 @@ -package net.codecrete.usb.linux.gen.usbdevice_fs; -// Generated by jextract - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import static java.lang.foreign.ValueLayout.*; - -final class RuntimeHelper { - - private RuntimeHelper() {} - private final static Linker LINKER = Linker.nativeLinker(); - private final static ClassLoader LOADER = RuntimeHelper.class.getClassLoader(); - private final static MethodHandles.Lookup MH_LOOKUP = MethodHandles.lookup(); - private final static SymbolLookup SYMBOL_LOOKUP; - - final static SegmentAllocator CONSTANT_ALLOCATOR = - (size, align) -> MemorySegment.allocateNative(size, align, MemorySession.openImplicit()); - - static { - - SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); - SYMBOL_LOOKUP = name -> loaderLookup.lookup(name).or(() -> LINKER.defaultLookup().lookup(name)); - } - - static T requireNonNull(T obj, String symbolName) { - if (obj == null) { - throw new UnsatisfiedLinkError("unresolved symbol: " + symbolName); - } - return obj; - } - - private final static SegmentAllocator THROWING_ALLOCATOR = (x, y) -> { throw new AssertionError("should not reach here"); }; - - static final MemorySegment lookupGlobalVariable(String name, MemoryLayout layout) { - return SYMBOL_LOOKUP.lookup(name).map(symbol -> MemorySegment.ofAddress(symbol.address(), layout.byteSize(), MemorySession.openShared())).orElse(null); - } - - static final MethodHandle downcallHandle(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> LINKER.downcallHandle(addr, fdesc)). - orElse(null); - } - - static final MethodHandle downcallHandle(FunctionDescriptor fdesc) { - return LINKER.downcallHandle(fdesc); - } - - static final MethodHandle downcallHandleVariadic(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> VarargsInvoker.make(addr, fdesc)). - orElse(null); - } - - static final MemorySegment upcallStub(Class fi, Z z, FunctionDescriptor fdesc, MemorySession session) { - try { - MethodHandle handle = MH_LOOKUP.findVirtual(fi, "apply", Linker.upcallType(fdesc)); - handle = handle.bindTo(z); - return LINKER.upcallStub(handle, fdesc, session); - } catch (Throwable ex) { - throw new AssertionError(ex); - } - } - - static MemorySegment asArray(MemoryAddress addr, MemoryLayout layout, int numElements, MemorySession session) { - return MemorySegment.ofAddress(addr, numElements * layout.byteSize(), session); - } - - // Internals only below this point - - private static class VarargsInvoker { - private static final MethodHandle INVOKE_MH; - private final MemorySegment symbol; - private final FunctionDescriptor function; - - private VarargsInvoker(MemorySegment symbol, FunctionDescriptor function) { - this.symbol = symbol; - this.function = function; - } - - static { - try { - INVOKE_MH = MethodHandles.lookup().findVirtual(VarargsInvoker.class, "invoke", MethodType.methodType(Object.class, SegmentAllocator.class, Object[].class)); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - static MethodHandle make(MemorySegment symbol, FunctionDescriptor function) { - VarargsInvoker invoker = new VarargsInvoker(symbol, function); - MethodHandle handle = INVOKE_MH.bindTo(invoker).asCollector(Object[].class, function.argumentLayouts().size() + 1); - MethodType mtype = MethodType.methodType(function.returnLayout().isPresent() ? carrier(function.returnLayout().get(), true) : void.class); - for (MemoryLayout layout : function.argumentLayouts()) { - mtype = mtype.appendParameterTypes(carrier(layout, false)); - } - mtype = mtype.appendParameterTypes(Object[].class); - if (mtype.returnType().equals(MemorySegment.class)) { - mtype = mtype.insertParameterTypes(0, SegmentAllocator.class); - } else { - handle = MethodHandles.insertArguments(handle, 0, THROWING_ALLOCATOR); - } - return handle.asType(mtype); - } - - static Class carrier(MemoryLayout layout, boolean ret) { - if (layout instanceof ValueLayout valueLayout) { - return (ret || valueLayout.carrier() != MemoryAddress.class) ? - valueLayout.carrier() : Addressable.class; - } else if (layout instanceof GroupLayout) { - return MemorySegment.class; - } else { - throw new AssertionError("Cannot get here!"); - } - } - - private Object invoke(SegmentAllocator allocator, Object[] args) throws Throwable { - // one trailing Object[] - int nNamedArgs = function.argumentLayouts().size(); - assert(args.length == nNamedArgs + 1); - // The last argument is the array of vararg collector - Object[] unnamedArgs = (Object[]) args[args.length - 1]; - - int argsCount = nNamedArgs + unnamedArgs.length; - Class[] argTypes = new Class[argsCount]; - MemoryLayout[] argLayouts = new MemoryLayout[nNamedArgs + unnamedArgs.length]; - - int pos = 0; - for (pos = 0; pos < nNamedArgs; pos++) { - argLayouts[pos] = function.argumentLayouts().get(pos); - } - - assert pos == nNamedArgs; - for (Object o: unnamedArgs) { - argLayouts[pos] = variadicLayout(normalize(o.getClass())); - pos++; - } - assert pos == argsCount; - - FunctionDescriptor f = (function.returnLayout().isEmpty()) ? - FunctionDescriptor.ofVoid(argLayouts) : - FunctionDescriptor.of(function.returnLayout().get(), argLayouts); - MethodHandle mh = LINKER.downcallHandle(symbol, f); - if (mh.type().returnType() == MemorySegment.class) { - mh = mh.bindTo(allocator); - } - // flatten argument list so that it can be passed to an asSpreader MH - Object[] allArgs = new Object[nNamedArgs + unnamedArgs.length]; - System.arraycopy(args, 0, allArgs, 0, nNamedArgs); - System.arraycopy(unnamedArgs, 0, allArgs, nNamedArgs, unnamedArgs.length); - - return mh.asSpreader(Object[].class, argsCount).invoke(allArgs); - } - - private static Class unboxIfNeeded(Class clazz) { - if (clazz == Boolean.class) { - return boolean.class; - } else if (clazz == Void.class) { - return void.class; - } else if (clazz == Byte.class) { - return byte.class; - } else if (clazz == Character.class) { - return char.class; - } else if (clazz == Short.class) { - return short.class; - } else if (clazz == Integer.class) { - return int.class; - } else if (clazz == Long.class) { - return long.class; - } else if (clazz == Float.class) { - return float.class; - } else if (clazz == Double.class) { - return double.class; - } else { - return clazz; - } - } - - private Class promote(Class c) { - if (c == byte.class || c == char.class || c == short.class || c == int.class) { - return long.class; - } else if (c == float.class) { - return double.class; - } else { - return c; - } - } - - private Class normalize(Class c) { - c = unboxIfNeeded(c); - if (c.isPrimitive()) { - return promote(c); - } - if (MemoryAddress.class.isAssignableFrom(c)) { - return MemoryAddress.class; - } - if (MemorySegment.class.isAssignableFrom(c)) { - return MemorySegment.class; - } - throw new IllegalArgumentException("Invalid type for ABI: " + c.getTypeName()); - } - - private MemoryLayout variadicLayout(Class c) { - if (c == long.class) { - return JAVA_LONG; - } else if (c == double.class) { - return JAVA_DOUBLE; - } else if (MemoryAddress.class.isAssignableFrom(c)) { - return ADDRESS; - } else { - throw new IllegalArgumentException("Unhandled variadic argument class: " + c); - } - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevfs_bulktransfer.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevfs_bulktransfer.java index 670dc414..5e4eba47 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevfs_bulktransfer.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevfs_bulktransfer.java @@ -2,90 +2,265 @@ package net.codecrete.usb.linux.gen.usbdevice_fs; +import java.lang.invoke.*; import java.lang.foreign.*; -import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +/** + * {@snippet lang=c : + * struct usbdevfs_bulktransfer { + * unsigned int ep; + * unsigned int len; + * unsigned int timeout; + * void *data; + * } + * } + */ public class usbdevfs_bulktransfer { - static final GroupLayout $struct$LAYOUT = MemoryLayout.structLayout( - Constants$root.C_INT$LAYOUT.withName("ep"), - Constants$root.C_INT$LAYOUT.withName("len"), - Constants$root.C_INT$LAYOUT.withName("timeout"), - MemoryLayout.paddingLayout(32), - Constants$root.C_POINTER$LAYOUT.withName("data") + usbdevfs_bulktransfer() { + // Should not be called directly + } + + private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( + usbdevice_fs.C_INT.withName("ep"), + usbdevice_fs.C_INT.withName("len"), + usbdevice_fs.C_INT.withName("timeout"), + MemoryLayout.paddingLayout(4), + usbdevice_fs.C_POINTER.withName("data") ).withName("usbdevfs_bulktransfer"); - public static MemoryLayout $LAYOUT() { - return usbdevfs_bulktransfer.$struct$LAYOUT; + + /** + * The layout of this struct + */ + public static final GroupLayout layout() { + return $LAYOUT; } - static final VarHandle ep$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("ep")); - public static VarHandle ep$VH() { - return usbdevfs_bulktransfer.ep$VH; + + private static final OfInt ep$LAYOUT = (OfInt)$LAYOUT.select(groupElement("ep")); + + /** + * Layout for field: + * {@snippet lang=c : + * unsigned int ep + * } + */ + public static final OfInt ep$layout() { + return ep$LAYOUT; } - public static int ep$get(MemorySegment seg) { - return (int)usbdevfs_bulktransfer.ep$VH.get(seg); + + private static final long ep$OFFSET = $LAYOUT.byteOffset(groupElement("ep")); + + /** + * Offset for field: + * {@snippet lang=c : + * unsigned int ep + * } + */ + public static final long ep$offset() { + return ep$OFFSET; } - public static void ep$set( MemorySegment seg, int x) { - usbdevfs_bulktransfer.ep$VH.set(seg, x); + + /** + * Getter for field: + * {@snippet lang=c : + * unsigned int ep + * } + */ + public static int ep(MemorySegment struct) { + return struct.get(ep$LAYOUT, ep$OFFSET); } - public static int ep$get(MemorySegment seg, long index) { - return (int)usbdevfs_bulktransfer.ep$VH.get(seg.asSlice(index*sizeof())); + + /** + * Setter for field: + * {@snippet lang=c : + * unsigned int ep + * } + */ + public static void ep(MemorySegment struct, int fieldValue) { + struct.set(ep$LAYOUT, ep$OFFSET, fieldValue); } - public static void ep$set(MemorySegment seg, long index, int x) { - usbdevfs_bulktransfer.ep$VH.set(seg.asSlice(index*sizeof()), x); + + private static final OfInt len$LAYOUT = (OfInt)$LAYOUT.select(groupElement("len")); + + /** + * Layout for field: + * {@snippet lang=c : + * unsigned int len + * } + */ + public static final OfInt len$layout() { + return len$LAYOUT; } - static final VarHandle len$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("len")); - public static VarHandle len$VH() { - return usbdevfs_bulktransfer.len$VH; + + private static final long len$OFFSET = $LAYOUT.byteOffset(groupElement("len")); + + /** + * Offset for field: + * {@snippet lang=c : + * unsigned int len + * } + */ + public static final long len$offset() { + return len$OFFSET; } - public static int len$get(MemorySegment seg) { - return (int)usbdevfs_bulktransfer.len$VH.get(seg); + + /** + * Getter for field: + * {@snippet lang=c : + * unsigned int len + * } + */ + public static int len(MemorySegment struct) { + return struct.get(len$LAYOUT, len$OFFSET); } - public static void len$set( MemorySegment seg, int x) { - usbdevfs_bulktransfer.len$VH.set(seg, x); + + /** + * Setter for field: + * {@snippet lang=c : + * unsigned int len + * } + */ + public static void len(MemorySegment struct, int fieldValue) { + struct.set(len$LAYOUT, len$OFFSET, fieldValue); } - public static int len$get(MemorySegment seg, long index) { - return (int)usbdevfs_bulktransfer.len$VH.get(seg.asSlice(index*sizeof())); + + private static final OfInt timeout$LAYOUT = (OfInt)$LAYOUT.select(groupElement("timeout")); + + /** + * Layout for field: + * {@snippet lang=c : + * unsigned int timeout + * } + */ + public static final OfInt timeout$layout() { + return timeout$LAYOUT; } - public static void len$set(MemorySegment seg, long index, int x) { - usbdevfs_bulktransfer.len$VH.set(seg.asSlice(index*sizeof()), x); + + private static final long timeout$OFFSET = $LAYOUT.byteOffset(groupElement("timeout")); + + /** + * Offset for field: + * {@snippet lang=c : + * unsigned int timeout + * } + */ + public static final long timeout$offset() { + return timeout$OFFSET; } - static final VarHandle timeout$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("timeout")); - public static VarHandle timeout$VH() { - return usbdevfs_bulktransfer.timeout$VH; + + /** + * Getter for field: + * {@snippet lang=c : + * unsigned int timeout + * } + */ + public static int timeout(MemorySegment struct) { + return struct.get(timeout$LAYOUT, timeout$OFFSET); } - public static int timeout$get(MemorySegment seg) { - return (int)usbdevfs_bulktransfer.timeout$VH.get(seg); + + /** + * Setter for field: + * {@snippet lang=c : + * unsigned int timeout + * } + */ + public static void timeout(MemorySegment struct, int fieldValue) { + struct.set(timeout$LAYOUT, timeout$OFFSET, fieldValue); } - public static void timeout$set( MemorySegment seg, int x) { - usbdevfs_bulktransfer.timeout$VH.set(seg, x); + + private static final AddressLayout data$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("data")); + + /** + * Layout for field: + * {@snippet lang=c : + * void *data + * } + */ + public static final AddressLayout data$layout() { + return data$LAYOUT; } - public static int timeout$get(MemorySegment seg, long index) { - return (int)usbdevfs_bulktransfer.timeout$VH.get(seg.asSlice(index*sizeof())); + + private static final long data$OFFSET = $LAYOUT.byteOffset(groupElement("data")); + + /** + * Offset for field: + * {@snippet lang=c : + * void *data + * } + */ + public static final long data$offset() { + return data$OFFSET; } - public static void timeout$set(MemorySegment seg, long index, int x) { - usbdevfs_bulktransfer.timeout$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Getter for field: + * {@snippet lang=c : + * void *data + * } + */ + public static MemorySegment data(MemorySegment struct) { + return struct.get(data$LAYOUT, data$OFFSET); } - static final VarHandle data$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("data")); - public static VarHandle data$VH() { - return usbdevfs_bulktransfer.data$VH; + + /** + * Setter for field: + * {@snippet lang=c : + * void *data + * } + */ + public static void data(MemorySegment struct, MemorySegment fieldValue) { + struct.set(data$LAYOUT, data$OFFSET, fieldValue); } - public static MemoryAddress data$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)usbdevfs_bulktransfer.data$VH.get(seg); + + /** + * Obtains a slice of {@code arrayParam} which selects the array element at {@code index}. + * The returned segment has address {@code arrayParam.address() + index * layout().byteSize()} + */ + public static MemorySegment asSlice(MemorySegment array, long index) { + return array.asSlice(layout().byteSize() * index); } - public static void data$set( MemorySegment seg, MemoryAddress x) { - usbdevfs_bulktransfer.data$VH.set(seg, x); + + /** + * The size (in bytes) of this struct + */ + public static long sizeof() { return layout().byteSize(); } + + /** + * Allocate a segment of size {@code layout().byteSize()} using {@code allocator} + */ + public static MemorySegment allocate(SegmentAllocator allocator) { + return allocator.allocate(layout()); } - public static MemoryAddress data$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)usbdevfs_bulktransfer.data$VH.get(seg.asSlice(index*sizeof())); + + /** + * Allocate an array of size {@code elementCount} using {@code allocator}. + * The returned segment has size {@code elementCount * layout().byteSize()}. + */ + public static MemorySegment allocateArray(long elementCount, SegmentAllocator allocator) { + return allocator.allocate(MemoryLayout.sequenceLayout(elementCount, layout())); } - public static void data$set(MemorySegment seg, long index, MemoryAddress x) { - usbdevfs_bulktransfer.data$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, Arena arena, Consumer cleanup) { + return reinterpret(addr, 1, arena, cleanup); } - public static long sizeof() { return $LAYOUT().byteSize(); } - public static MemorySegment allocate(SegmentAllocator allocator) { return allocator.allocate($LAYOUT()); } - public static MemorySegment allocateArray(int len, SegmentAllocator allocator) { - return allocator.allocate(MemoryLayout.sequenceLayout(len, $LAYOUT())); + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code elementCount * layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, long elementCount, Arena arena, Consumer cleanup) { + return addr.reinterpret(layout().byteSize() * elementCount, arena, cleanup); } - public static MemorySegment ofAddress(MemoryAddress addr, MemorySession session) { return RuntimeHelper.asArray(addr, $LAYOUT(), 1, session); } } - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevfs_ctrltransfer.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevfs_ctrltransfer.java index 2dd9c901..c5071566 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevfs_ctrltransfer.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevfs_ctrltransfer.java @@ -2,141 +2,403 @@ package net.codecrete.usb.linux.gen.usbdevice_fs; +import java.lang.invoke.*; import java.lang.foreign.*; -import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +/** + * {@snippet lang=c : + * struct usbdevfs_ctrltransfer { + * __u8 bRequestType; + * __u8 bRequest; + * __u16 wValue; + * __u16 wIndex; + * __u16 wLength; + * __u32 timeout; + * void *data; + * } + * } + */ public class usbdevfs_ctrltransfer { - static final GroupLayout $struct$LAYOUT = MemoryLayout.structLayout( - Constants$root.C_CHAR$LAYOUT.withName("bRequestType"), - Constants$root.C_CHAR$LAYOUT.withName("bRequest"), - Constants$root.C_SHORT$LAYOUT.withName("wValue"), - Constants$root.C_SHORT$LAYOUT.withName("wIndex"), - Constants$root.C_SHORT$LAYOUT.withName("wLength"), - Constants$root.C_INT$LAYOUT.withName("timeout"), - MemoryLayout.paddingLayout(32), - Constants$root.C_POINTER$LAYOUT.withName("data") - ).withName("usbdevfs_ctrltransfer"); - public static MemoryLayout $LAYOUT() { - return usbdevfs_ctrltransfer.$struct$LAYOUT; - } - static final VarHandle bRequestType$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("bRequestType")); - public static VarHandle bRequestType$VH() { - return usbdevfs_ctrltransfer.bRequestType$VH; + usbdevfs_ctrltransfer() { + // Should not be called directly } - public static byte bRequestType$get(MemorySegment seg) { - return (byte)usbdevfs_ctrltransfer.bRequestType$VH.get(seg); - } - public static void bRequestType$set( MemorySegment seg, byte x) { - usbdevfs_ctrltransfer.bRequestType$VH.set(seg, x); + + private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( + usbdevice_fs.C_CHAR.withName("bRequestType"), + usbdevice_fs.C_CHAR.withName("bRequest"), + usbdevice_fs.C_SHORT.withName("wValue"), + usbdevice_fs.C_SHORT.withName("wIndex"), + usbdevice_fs.C_SHORT.withName("wLength"), + usbdevice_fs.C_INT.withName("timeout"), + MemoryLayout.paddingLayout(4), + usbdevice_fs.C_POINTER.withName("data") + ).withName("usbdevfs_ctrltransfer"); + + /** + * The layout of this struct + */ + public static final GroupLayout layout() { + return $LAYOUT; } - public static byte bRequestType$get(MemorySegment seg, long index) { - return (byte)usbdevfs_ctrltransfer.bRequestType$VH.get(seg.asSlice(index*sizeof())); + + private static final OfByte bRequestType$LAYOUT = (OfByte)$LAYOUT.select(groupElement("bRequestType")); + + /** + * Layout for field: + * {@snippet lang=c : + * __u8 bRequestType + * } + */ + public static final OfByte bRequestType$layout() { + return bRequestType$LAYOUT; } - public static void bRequestType$set(MemorySegment seg, long index, byte x) { - usbdevfs_ctrltransfer.bRequestType$VH.set(seg.asSlice(index*sizeof()), x); + + private static final long bRequestType$OFFSET = $LAYOUT.byteOffset(groupElement("bRequestType")); + + /** + * Offset for field: + * {@snippet lang=c : + * __u8 bRequestType + * } + */ + public static final long bRequestType$offset() { + return bRequestType$OFFSET; } - static final VarHandle bRequest$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("bRequest")); - public static VarHandle bRequest$VH() { - return usbdevfs_ctrltransfer.bRequest$VH; + + /** + * Getter for field: + * {@snippet lang=c : + * __u8 bRequestType + * } + */ + public static byte bRequestType(MemorySegment struct) { + return struct.get(bRequestType$LAYOUT, bRequestType$OFFSET); } - public static byte bRequest$get(MemorySegment seg) { - return (byte)usbdevfs_ctrltransfer.bRequest$VH.get(seg); + + /** + * Setter for field: + * {@snippet lang=c : + * __u8 bRequestType + * } + */ + public static void bRequestType(MemorySegment struct, byte fieldValue) { + struct.set(bRequestType$LAYOUT, bRequestType$OFFSET, fieldValue); } - public static void bRequest$set( MemorySegment seg, byte x) { - usbdevfs_ctrltransfer.bRequest$VH.set(seg, x); + + private static final OfByte bRequest$LAYOUT = (OfByte)$LAYOUT.select(groupElement("bRequest")); + + /** + * Layout for field: + * {@snippet lang=c : + * __u8 bRequest + * } + */ + public static final OfByte bRequest$layout() { + return bRequest$LAYOUT; } - public static byte bRequest$get(MemorySegment seg, long index) { - return (byte)usbdevfs_ctrltransfer.bRequest$VH.get(seg.asSlice(index*sizeof())); + + private static final long bRequest$OFFSET = $LAYOUT.byteOffset(groupElement("bRequest")); + + /** + * Offset for field: + * {@snippet lang=c : + * __u8 bRequest + * } + */ + public static final long bRequest$offset() { + return bRequest$OFFSET; } - public static void bRequest$set(MemorySegment seg, long index, byte x) { - usbdevfs_ctrltransfer.bRequest$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Getter for field: + * {@snippet lang=c : + * __u8 bRequest + * } + */ + public static byte bRequest(MemorySegment struct) { + return struct.get(bRequest$LAYOUT, bRequest$OFFSET); } - static final VarHandle wValue$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("wValue")); - public static VarHandle wValue$VH() { - return usbdevfs_ctrltransfer.wValue$VH; + + /** + * Setter for field: + * {@snippet lang=c : + * __u8 bRequest + * } + */ + public static void bRequest(MemorySegment struct, byte fieldValue) { + struct.set(bRequest$LAYOUT, bRequest$OFFSET, fieldValue); } - public static short wValue$get(MemorySegment seg) { - return (short)usbdevfs_ctrltransfer.wValue$VH.get(seg); + + private static final OfShort wValue$LAYOUT = (OfShort)$LAYOUT.select(groupElement("wValue")); + + /** + * Layout for field: + * {@snippet lang=c : + * __u16 wValue + * } + */ + public static final OfShort wValue$layout() { + return wValue$LAYOUT; } - public static void wValue$set( MemorySegment seg, short x) { - usbdevfs_ctrltransfer.wValue$VH.set(seg, x); + + private static final long wValue$OFFSET = $LAYOUT.byteOffset(groupElement("wValue")); + + /** + * Offset for field: + * {@snippet lang=c : + * __u16 wValue + * } + */ + public static final long wValue$offset() { + return wValue$OFFSET; } - public static short wValue$get(MemorySegment seg, long index) { - return (short)usbdevfs_ctrltransfer.wValue$VH.get(seg.asSlice(index*sizeof())); + + /** + * Getter for field: + * {@snippet lang=c : + * __u16 wValue + * } + */ + public static short wValue(MemorySegment struct) { + return struct.get(wValue$LAYOUT, wValue$OFFSET); } - public static void wValue$set(MemorySegment seg, long index, short x) { - usbdevfs_ctrltransfer.wValue$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Setter for field: + * {@snippet lang=c : + * __u16 wValue + * } + */ + public static void wValue(MemorySegment struct, short fieldValue) { + struct.set(wValue$LAYOUT, wValue$OFFSET, fieldValue); } - static final VarHandle wIndex$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("wIndex")); - public static VarHandle wIndex$VH() { - return usbdevfs_ctrltransfer.wIndex$VH; + + private static final OfShort wIndex$LAYOUT = (OfShort)$LAYOUT.select(groupElement("wIndex")); + + /** + * Layout for field: + * {@snippet lang=c : + * __u16 wIndex + * } + */ + public static final OfShort wIndex$layout() { + return wIndex$LAYOUT; } - public static short wIndex$get(MemorySegment seg) { - return (short)usbdevfs_ctrltransfer.wIndex$VH.get(seg); + + private static final long wIndex$OFFSET = $LAYOUT.byteOffset(groupElement("wIndex")); + + /** + * Offset for field: + * {@snippet lang=c : + * __u16 wIndex + * } + */ + public static final long wIndex$offset() { + return wIndex$OFFSET; } - public static void wIndex$set( MemorySegment seg, short x) { - usbdevfs_ctrltransfer.wIndex$VH.set(seg, x); + + /** + * Getter for field: + * {@snippet lang=c : + * __u16 wIndex + * } + */ + public static short wIndex(MemorySegment struct) { + return struct.get(wIndex$LAYOUT, wIndex$OFFSET); } - public static short wIndex$get(MemorySegment seg, long index) { - return (short)usbdevfs_ctrltransfer.wIndex$VH.get(seg.asSlice(index*sizeof())); + + /** + * Setter for field: + * {@snippet lang=c : + * __u16 wIndex + * } + */ + public static void wIndex(MemorySegment struct, short fieldValue) { + struct.set(wIndex$LAYOUT, wIndex$OFFSET, fieldValue); } - public static void wIndex$set(MemorySegment seg, long index, short x) { - usbdevfs_ctrltransfer.wIndex$VH.set(seg.asSlice(index*sizeof()), x); + + private static final OfShort wLength$LAYOUT = (OfShort)$LAYOUT.select(groupElement("wLength")); + + /** + * Layout for field: + * {@snippet lang=c : + * __u16 wLength + * } + */ + public static final OfShort wLength$layout() { + return wLength$LAYOUT; } - static final VarHandle wLength$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("wLength")); - public static VarHandle wLength$VH() { - return usbdevfs_ctrltransfer.wLength$VH; + + private static final long wLength$OFFSET = $LAYOUT.byteOffset(groupElement("wLength")); + + /** + * Offset for field: + * {@snippet lang=c : + * __u16 wLength + * } + */ + public static final long wLength$offset() { + return wLength$OFFSET; } - public static short wLength$get(MemorySegment seg) { - return (short)usbdevfs_ctrltransfer.wLength$VH.get(seg); + + /** + * Getter for field: + * {@snippet lang=c : + * __u16 wLength + * } + */ + public static short wLength(MemorySegment struct) { + return struct.get(wLength$LAYOUT, wLength$OFFSET); } - public static void wLength$set( MemorySegment seg, short x) { - usbdevfs_ctrltransfer.wLength$VH.set(seg, x); + + /** + * Setter for field: + * {@snippet lang=c : + * __u16 wLength + * } + */ + public static void wLength(MemorySegment struct, short fieldValue) { + struct.set(wLength$LAYOUT, wLength$OFFSET, fieldValue); } - public static short wLength$get(MemorySegment seg, long index) { - return (short)usbdevfs_ctrltransfer.wLength$VH.get(seg.asSlice(index*sizeof())); + + private static final OfInt timeout$LAYOUT = (OfInt)$LAYOUT.select(groupElement("timeout")); + + /** + * Layout for field: + * {@snippet lang=c : + * __u32 timeout + * } + */ + public static final OfInt timeout$layout() { + return timeout$LAYOUT; } - public static void wLength$set(MemorySegment seg, long index, short x) { - usbdevfs_ctrltransfer.wLength$VH.set(seg.asSlice(index*sizeof()), x); + + private static final long timeout$OFFSET = $LAYOUT.byteOffset(groupElement("timeout")); + + /** + * Offset for field: + * {@snippet lang=c : + * __u32 timeout + * } + */ + public static final long timeout$offset() { + return timeout$OFFSET; } - static final VarHandle timeout$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("timeout")); - public static VarHandle timeout$VH() { - return usbdevfs_ctrltransfer.timeout$VH; + + /** + * Getter for field: + * {@snippet lang=c : + * __u32 timeout + * } + */ + public static int timeout(MemorySegment struct) { + return struct.get(timeout$LAYOUT, timeout$OFFSET); } - public static int timeout$get(MemorySegment seg) { - return (int)usbdevfs_ctrltransfer.timeout$VH.get(seg); + + /** + * Setter for field: + * {@snippet lang=c : + * __u32 timeout + * } + */ + public static void timeout(MemorySegment struct, int fieldValue) { + struct.set(timeout$LAYOUT, timeout$OFFSET, fieldValue); } - public static void timeout$set( MemorySegment seg, int x) { - usbdevfs_ctrltransfer.timeout$VH.set(seg, x); + + private static final AddressLayout data$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("data")); + + /** + * Layout for field: + * {@snippet lang=c : + * void *data + * } + */ + public static final AddressLayout data$layout() { + return data$LAYOUT; } - public static int timeout$get(MemorySegment seg, long index) { - return (int)usbdevfs_ctrltransfer.timeout$VH.get(seg.asSlice(index*sizeof())); + + private static final long data$OFFSET = $LAYOUT.byteOffset(groupElement("data")); + + /** + * Offset for field: + * {@snippet lang=c : + * void *data + * } + */ + public static final long data$offset() { + return data$OFFSET; } - public static void timeout$set(MemorySegment seg, long index, int x) { - usbdevfs_ctrltransfer.timeout$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Getter for field: + * {@snippet lang=c : + * void *data + * } + */ + public static MemorySegment data(MemorySegment struct) { + return struct.get(data$LAYOUT, data$OFFSET); } - static final VarHandle data$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("data")); - public static VarHandle data$VH() { - return usbdevfs_ctrltransfer.data$VH; + + /** + * Setter for field: + * {@snippet lang=c : + * void *data + * } + */ + public static void data(MemorySegment struct, MemorySegment fieldValue) { + struct.set(data$LAYOUT, data$OFFSET, fieldValue); } - public static MemoryAddress data$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)usbdevfs_ctrltransfer.data$VH.get(seg); + + /** + * Obtains a slice of {@code arrayParam} which selects the array element at {@code index}. + * The returned segment has address {@code arrayParam.address() + index * layout().byteSize()} + */ + public static MemorySegment asSlice(MemorySegment array, long index) { + return array.asSlice(layout().byteSize() * index); } - public static void data$set( MemorySegment seg, MemoryAddress x) { - usbdevfs_ctrltransfer.data$VH.set(seg, x); + + /** + * The size (in bytes) of this struct + */ + public static long sizeof() { return layout().byteSize(); } + + /** + * Allocate a segment of size {@code layout().byteSize()} using {@code allocator} + */ + public static MemorySegment allocate(SegmentAllocator allocator) { + return allocator.allocate(layout()); } - public static MemoryAddress data$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)usbdevfs_ctrltransfer.data$VH.get(seg.asSlice(index*sizeof())); + + /** + * Allocate an array of size {@code elementCount} using {@code allocator}. + * The returned segment has size {@code elementCount * layout().byteSize()}. + */ + public static MemorySegment allocateArray(long elementCount, SegmentAllocator allocator) { + return allocator.allocate(MemoryLayout.sequenceLayout(elementCount, layout())); } - public static void data$set(MemorySegment seg, long index, MemoryAddress x) { - usbdevfs_ctrltransfer.data$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, Arena arena, Consumer cleanup) { + return reinterpret(addr, 1, arena, cleanup); } - public static long sizeof() { return $LAYOUT().byteSize(); } - public static MemorySegment allocate(SegmentAllocator allocator) { return allocator.allocate($LAYOUT()); } - public static MemorySegment allocateArray(int len, SegmentAllocator allocator) { - return allocator.allocate(MemoryLayout.sequenceLayout(len, $LAYOUT())); + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code elementCount * layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, long elementCount, Arena arena, Consumer cleanup) { + return addr.reinterpret(layout().byteSize() * elementCount, arena, cleanup); } - public static MemorySegment ofAddress(MemoryAddress addr, MemorySession session) { return RuntimeHelper.asArray(addr, $LAYOUT(), 1, session); } } - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevfs_disconnect_claim.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevfs_disconnect_claim.java new file mode 100644 index 00000000..a41ba1db --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevfs_disconnect_claim.java @@ -0,0 +1,252 @@ +// Generated by jextract + +package net.codecrete.usb.linux.gen.usbdevice_fs; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +/** + * {@snippet lang=c : + * struct usbdevfs_disconnect_claim { + * unsigned int interface; + * unsigned int flags; + * char driver[256]; + * } + * } + */ +public class usbdevfs_disconnect_claim { + + usbdevfs_disconnect_claim() { + // Should not be called directly + } + + private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( + usbdevice_fs.C_INT.withName("interface"), + usbdevice_fs.C_INT.withName("flags"), + MemoryLayout.sequenceLayout(256, usbdevice_fs.C_CHAR).withName("driver") + ).withName("usbdevfs_disconnect_claim"); + + /** + * The layout of this struct + */ + public static final GroupLayout layout() { + return $LAYOUT; + } + + private static final OfInt interface_$LAYOUT = (OfInt)$LAYOUT.select(groupElement("interface")); + + /** + * Layout for field: + * {@snippet lang=c : + * unsigned int interface + * } + */ + public static final OfInt interface_$layout() { + return interface_$LAYOUT; + } + + private static final long interface_$OFFSET = $LAYOUT.byteOffset(groupElement("interface")); + + /** + * Offset for field: + * {@snippet lang=c : + * unsigned int interface + * } + */ + public static final long interface_$offset() { + return interface_$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * unsigned int interface + * } + */ + public static int interface_(MemorySegment struct) { + return struct.get(interface_$LAYOUT, interface_$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * unsigned int interface + * } + */ + public static void interface_(MemorySegment struct, int fieldValue) { + struct.set(interface_$LAYOUT, interface_$OFFSET, fieldValue); + } + + private static final OfInt flags$LAYOUT = (OfInt)$LAYOUT.select(groupElement("flags")); + + /** + * Layout for field: + * {@snippet lang=c : + * unsigned int flags + * } + */ + public static final OfInt flags$layout() { + return flags$LAYOUT; + } + + private static final long flags$OFFSET = $LAYOUT.byteOffset(groupElement("flags")); + + /** + * Offset for field: + * {@snippet lang=c : + * unsigned int flags + * } + */ + public static final long flags$offset() { + return flags$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * unsigned int flags + * } + */ + public static int flags(MemorySegment struct) { + return struct.get(flags$LAYOUT, flags$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * unsigned int flags + * } + */ + public static void flags(MemorySegment struct, int fieldValue) { + struct.set(flags$LAYOUT, flags$OFFSET, fieldValue); + } + + private static final SequenceLayout driver$LAYOUT = (SequenceLayout)$LAYOUT.select(groupElement("driver")); + + /** + * Layout for field: + * {@snippet lang=c : + * char driver[256] + * } + */ + public static final SequenceLayout driver$layout() { + return driver$LAYOUT; + } + + private static final long driver$OFFSET = $LAYOUT.byteOffset(groupElement("driver")); + + /** + * Offset for field: + * {@snippet lang=c : + * char driver[256] + * } + */ + public static final long driver$offset() { + return driver$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * char driver[256] + * } + */ + public static MemorySegment driver(MemorySegment struct) { + return struct.asSlice(driver$OFFSET, driver$LAYOUT.byteSize()); + } + + /** + * Setter for field: + * {@snippet lang=c : + * char driver[256] + * } + */ + public static void driver(MemorySegment struct, MemorySegment fieldValue) { + MemorySegment.copy(fieldValue, 0L, struct, driver$OFFSET, driver$LAYOUT.byteSize()); + } + + private static long[] driver$DIMS = { 256 }; + + /** + * Dimensions for array field: + * {@snippet lang=c : + * char driver[256] + * } + */ + public static long[] driver$dimensions() { + return driver$DIMS; + } + private static final VarHandle driver$ELEM_HANDLE = driver$LAYOUT.varHandle(sequenceElement()); + + /** + * Indexed getter for field: + * {@snippet lang=c : + * char driver[256] + * } + */ + public static byte driver(MemorySegment struct, long index0) { + return (byte)driver$ELEM_HANDLE.get(struct, 0L, index0); + } + + /** + * Indexed setter for field: + * {@snippet lang=c : + * char driver[256] + * } + */ + public static void driver(MemorySegment struct, long index0, byte fieldValue) { + driver$ELEM_HANDLE.set(struct, 0L, index0, fieldValue); + } + + /** + * Obtains a slice of {@code arrayParam} which selects the array element at {@code index}. + * The returned segment has address {@code arrayParam.address() + index * layout().byteSize()} + */ + public static MemorySegment asSlice(MemorySegment array, long index) { + return array.asSlice(layout().byteSize() * index); + } + + /** + * The size (in bytes) of this struct + */ + public static long sizeof() { return layout().byteSize(); } + + /** + * Allocate a segment of size {@code layout().byteSize()} using {@code allocator} + */ + public static MemorySegment allocate(SegmentAllocator allocator) { + return allocator.allocate(layout()); + } + + /** + * Allocate an array of size {@code elementCount} using {@code allocator}. + * The returned segment has size {@code elementCount * layout().byteSize()}. + */ + public static MemorySegment allocateArray(long elementCount, SegmentAllocator allocator) { + return allocator.allocate(MemoryLayout.sequenceLayout(elementCount, layout())); + } + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, Arena arena, Consumer cleanup) { + return reinterpret(addr, 1, arena, cleanup); + } + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code elementCount * layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, long elementCount, Arena arena, Consumer cleanup) { + return addr.reinterpret(layout().byteSize() * elementCount, arena, cleanup); + } +} + diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevfs_ioctl.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevfs_ioctl.java new file mode 100644 index 00000000..8d4b6143 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevfs_ioctl.java @@ -0,0 +1,219 @@ +// Generated by jextract + +package net.codecrete.usb.linux.gen.usbdevice_fs; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +/** + * {@snippet lang=c : + * struct usbdevfs_ioctl { + * int ifno; + * int ioctl_code; + * void *data; + * } + * } + */ +public class usbdevfs_ioctl { + + usbdevfs_ioctl() { + // Should not be called directly + } + + private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( + usbdevice_fs.C_INT.withName("ifno"), + usbdevice_fs.C_INT.withName("ioctl_code"), + usbdevice_fs.C_POINTER.withName("data") + ).withName("usbdevfs_ioctl"); + + /** + * The layout of this struct + */ + public static final GroupLayout layout() { + return $LAYOUT; + } + + private static final OfInt ifno$LAYOUT = (OfInt)$LAYOUT.select(groupElement("ifno")); + + /** + * Layout for field: + * {@snippet lang=c : + * int ifno + * } + */ + public static final OfInt ifno$layout() { + return ifno$LAYOUT; + } + + private static final long ifno$OFFSET = $LAYOUT.byteOffset(groupElement("ifno")); + + /** + * Offset for field: + * {@snippet lang=c : + * int ifno + * } + */ + public static final long ifno$offset() { + return ifno$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * int ifno + * } + */ + public static int ifno(MemorySegment struct) { + return struct.get(ifno$LAYOUT, ifno$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * int ifno + * } + */ + public static void ifno(MemorySegment struct, int fieldValue) { + struct.set(ifno$LAYOUT, ifno$OFFSET, fieldValue); + } + + private static final OfInt ioctl_code$LAYOUT = (OfInt)$LAYOUT.select(groupElement("ioctl_code")); + + /** + * Layout for field: + * {@snippet lang=c : + * int ioctl_code + * } + */ + public static final OfInt ioctl_code$layout() { + return ioctl_code$LAYOUT; + } + + private static final long ioctl_code$OFFSET = $LAYOUT.byteOffset(groupElement("ioctl_code")); + + /** + * Offset for field: + * {@snippet lang=c : + * int ioctl_code + * } + */ + public static final long ioctl_code$offset() { + return ioctl_code$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * int ioctl_code + * } + */ + public static int ioctl_code(MemorySegment struct) { + return struct.get(ioctl_code$LAYOUT, ioctl_code$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * int ioctl_code + * } + */ + public static void ioctl_code(MemorySegment struct, int fieldValue) { + struct.set(ioctl_code$LAYOUT, ioctl_code$OFFSET, fieldValue); + } + + private static final AddressLayout data$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("data")); + + /** + * Layout for field: + * {@snippet lang=c : + * void *data + * } + */ + public static final AddressLayout data$layout() { + return data$LAYOUT; + } + + private static final long data$OFFSET = $LAYOUT.byteOffset(groupElement("data")); + + /** + * Offset for field: + * {@snippet lang=c : + * void *data + * } + */ + public static final long data$offset() { + return data$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * void *data + * } + */ + public static MemorySegment data(MemorySegment struct) { + return struct.get(data$LAYOUT, data$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * void *data + * } + */ + public static void data(MemorySegment struct, MemorySegment fieldValue) { + struct.set(data$LAYOUT, data$OFFSET, fieldValue); + } + + /** + * Obtains a slice of {@code arrayParam} which selects the array element at {@code index}. + * The returned segment has address {@code arrayParam.address() + index * layout().byteSize()} + */ + public static MemorySegment asSlice(MemorySegment array, long index) { + return array.asSlice(layout().byteSize() * index); + } + + /** + * The size (in bytes) of this struct + */ + public static long sizeof() { return layout().byteSize(); } + + /** + * Allocate a segment of size {@code layout().byteSize()} using {@code allocator} + */ + public static MemorySegment allocate(SegmentAllocator allocator) { + return allocator.allocate(layout()); + } + + /** + * Allocate an array of size {@code elementCount} using {@code allocator}. + * The returned segment has size {@code elementCount * layout().byteSize()}. + */ + public static MemorySegment allocateArray(long elementCount, SegmentAllocator allocator) { + return allocator.allocate(MemoryLayout.sequenceLayout(elementCount, layout())); + } + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, Arena arena, Consumer cleanup) { + return reinterpret(addr, 1, arena, cleanup); + } + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code elementCount * layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, long elementCount, Arena arena, Consumer cleanup) { + return addr.reinterpret(layout().byteSize() * elementCount, arena, cleanup); + } +} + diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevfs_iso_packet_desc.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevfs_iso_packet_desc.java new file mode 100644 index 00000000..c269241e --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevfs_iso_packet_desc.java @@ -0,0 +1,219 @@ +// Generated by jextract + +package net.codecrete.usb.linux.gen.usbdevice_fs; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +/** + * {@snippet lang=c : + * struct usbdevfs_iso_packet_desc { + * unsigned int length; + * unsigned int actual_length; + * unsigned int status; + * } + * } + */ +public class usbdevfs_iso_packet_desc { + + usbdevfs_iso_packet_desc() { + // Should not be called directly + } + + private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( + usbdevice_fs.C_INT.withName("length"), + usbdevice_fs.C_INT.withName("actual_length"), + usbdevice_fs.C_INT.withName("status") + ).withName("usbdevfs_iso_packet_desc"); + + /** + * The layout of this struct + */ + public static final GroupLayout layout() { + return $LAYOUT; + } + + private static final OfInt length$LAYOUT = (OfInt)$LAYOUT.select(groupElement("length")); + + /** + * Layout for field: + * {@snippet lang=c : + * unsigned int length + * } + */ + public static final OfInt length$layout() { + return length$LAYOUT; + } + + private static final long length$OFFSET = $LAYOUT.byteOffset(groupElement("length")); + + /** + * Offset for field: + * {@snippet lang=c : + * unsigned int length + * } + */ + public static final long length$offset() { + return length$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * unsigned int length + * } + */ + public static int length(MemorySegment struct) { + return struct.get(length$LAYOUT, length$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * unsigned int length + * } + */ + public static void length(MemorySegment struct, int fieldValue) { + struct.set(length$LAYOUT, length$OFFSET, fieldValue); + } + + private static final OfInt actual_length$LAYOUT = (OfInt)$LAYOUT.select(groupElement("actual_length")); + + /** + * Layout for field: + * {@snippet lang=c : + * unsigned int actual_length + * } + */ + public static final OfInt actual_length$layout() { + return actual_length$LAYOUT; + } + + private static final long actual_length$OFFSET = $LAYOUT.byteOffset(groupElement("actual_length")); + + /** + * Offset for field: + * {@snippet lang=c : + * unsigned int actual_length + * } + */ + public static final long actual_length$offset() { + return actual_length$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * unsigned int actual_length + * } + */ + public static int actual_length(MemorySegment struct) { + return struct.get(actual_length$LAYOUT, actual_length$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * unsigned int actual_length + * } + */ + public static void actual_length(MemorySegment struct, int fieldValue) { + struct.set(actual_length$LAYOUT, actual_length$OFFSET, fieldValue); + } + + private static final OfInt status$LAYOUT = (OfInt)$LAYOUT.select(groupElement("status")); + + /** + * Layout for field: + * {@snippet lang=c : + * unsigned int status + * } + */ + public static final OfInt status$layout() { + return status$LAYOUT; + } + + private static final long status$OFFSET = $LAYOUT.byteOffset(groupElement("status")); + + /** + * Offset for field: + * {@snippet lang=c : + * unsigned int status + * } + */ + public static final long status$offset() { + return status$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * unsigned int status + * } + */ + public static int status(MemorySegment struct) { + return struct.get(status$LAYOUT, status$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * unsigned int status + * } + */ + public static void status(MemorySegment struct, int fieldValue) { + struct.set(status$LAYOUT, status$OFFSET, fieldValue); + } + + /** + * Obtains a slice of {@code arrayParam} which selects the array element at {@code index}. + * The returned segment has address {@code arrayParam.address() + index * layout().byteSize()} + */ + public static MemorySegment asSlice(MemorySegment array, long index) { + return array.asSlice(layout().byteSize() * index); + } + + /** + * The size (in bytes) of this struct + */ + public static long sizeof() { return layout().byteSize(); } + + /** + * Allocate a segment of size {@code layout().byteSize()} using {@code allocator} + */ + public static MemorySegment allocate(SegmentAllocator allocator) { + return allocator.allocate(layout()); + } + + /** + * Allocate an array of size {@code elementCount} using {@code allocator}. + * The returned segment has size {@code elementCount * layout().byteSize()}. + */ + public static MemorySegment allocateArray(long elementCount, SegmentAllocator allocator) { + return allocator.allocate(MemoryLayout.sequenceLayout(elementCount, layout())); + } + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, Arena arena, Consumer cleanup) { + return reinterpret(addr, 1, arena, cleanup); + } + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code elementCount * layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, long elementCount, Arena arena, Consumer cleanup) { + return addr.reinterpret(layout().byteSize() * elementCount, arena, cleanup); + } +} + diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevfs_setinterface.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevfs_setinterface.java new file mode 100644 index 00000000..fdae516e --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevfs_setinterface.java @@ -0,0 +1,173 @@ +// Generated by jextract + +package net.codecrete.usb.linux.gen.usbdevice_fs; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +/** + * {@snippet lang=c : + * struct usbdevfs_setinterface { + * unsigned int interface; + * unsigned int altsetting; + * } + * } + */ +public class usbdevfs_setinterface { + + usbdevfs_setinterface() { + // Should not be called directly + } + + private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( + usbdevice_fs.C_INT.withName("interface"), + usbdevice_fs.C_INT.withName("altsetting") + ).withName("usbdevfs_setinterface"); + + /** + * The layout of this struct + */ + public static final GroupLayout layout() { + return $LAYOUT; + } + + private static final OfInt interface_$LAYOUT = (OfInt)$LAYOUT.select(groupElement("interface")); + + /** + * Layout for field: + * {@snippet lang=c : + * unsigned int interface + * } + */ + public static final OfInt interface_$layout() { + return interface_$LAYOUT; + } + + private static final long interface_$OFFSET = $LAYOUT.byteOffset(groupElement("interface")); + + /** + * Offset for field: + * {@snippet lang=c : + * unsigned int interface + * } + */ + public static final long interface_$offset() { + return interface_$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * unsigned int interface + * } + */ + public static int interface_(MemorySegment struct) { + return struct.get(interface_$LAYOUT, interface_$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * unsigned int interface + * } + */ + public static void interface_(MemorySegment struct, int fieldValue) { + struct.set(interface_$LAYOUT, interface_$OFFSET, fieldValue); + } + + private static final OfInt altsetting$LAYOUT = (OfInt)$LAYOUT.select(groupElement("altsetting")); + + /** + * Layout for field: + * {@snippet lang=c : + * unsigned int altsetting + * } + */ + public static final OfInt altsetting$layout() { + return altsetting$LAYOUT; + } + + private static final long altsetting$OFFSET = $LAYOUT.byteOffset(groupElement("altsetting")); + + /** + * Offset for field: + * {@snippet lang=c : + * unsigned int altsetting + * } + */ + public static final long altsetting$offset() { + return altsetting$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * unsigned int altsetting + * } + */ + public static int altsetting(MemorySegment struct) { + return struct.get(altsetting$LAYOUT, altsetting$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * unsigned int altsetting + * } + */ + public static void altsetting(MemorySegment struct, int fieldValue) { + struct.set(altsetting$LAYOUT, altsetting$OFFSET, fieldValue); + } + + /** + * Obtains a slice of {@code arrayParam} which selects the array element at {@code index}. + * The returned segment has address {@code arrayParam.address() + index * layout().byteSize()} + */ + public static MemorySegment asSlice(MemorySegment array, long index) { + return array.asSlice(layout().byteSize() * index); + } + + /** + * The size (in bytes) of this struct + */ + public static long sizeof() { return layout().byteSize(); } + + /** + * Allocate a segment of size {@code layout().byteSize()} using {@code allocator} + */ + public static MemorySegment allocate(SegmentAllocator allocator) { + return allocator.allocate(layout()); + } + + /** + * Allocate an array of size {@code elementCount} using {@code allocator}. + * The returned segment has size {@code elementCount * layout().byteSize()}. + */ + public static MemorySegment allocateArray(long elementCount, SegmentAllocator allocator) { + return allocator.allocate(MemoryLayout.sequenceLayout(elementCount, layout())); + } + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, Arena arena, Consumer cleanup) { + return reinterpret(addr, 1, arena, cleanup); + } + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code elementCount * layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, long elementCount, Arena arena, Consumer cleanup) { + return addr.reinterpret(layout().byteSize() * elementCount, arena, cleanup); + } +} + diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevfs_urb.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevfs_urb.java new file mode 100644 index 00000000..556be6c4 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevfs_urb.java @@ -0,0 +1,640 @@ +// Generated by jextract + +package net.codecrete.usb.linux.gen.usbdevice_fs; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +/** + * {@snippet lang=c : + * struct usbdevfs_urb { + * unsigned char type; + * unsigned char endpoint; + * int status; + * unsigned int flags; + * void *buffer; + * int buffer_length; + * int actual_length; + * int start_frame; + * union { + * int number_of_packets; + * unsigned int stream_id; + * }; + * int error_count; + * unsigned int signr; + * void *usercontext; + * struct usbdevfs_iso_packet_desc iso_frame_desc[]; + * } + * } + */ +public class usbdevfs_urb { + + usbdevfs_urb() { + // Should not be called directly + } + + private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( + usbdevice_fs.C_CHAR.withName("type"), + usbdevice_fs.C_CHAR.withName("endpoint"), + MemoryLayout.paddingLayout(2), + usbdevice_fs.C_INT.withName("status"), + usbdevice_fs.C_INT.withName("flags"), + MemoryLayout.paddingLayout(4), + usbdevice_fs.C_POINTER.withName("buffer"), + usbdevice_fs.C_INT.withName("buffer_length"), + usbdevice_fs.C_INT.withName("actual_length"), + usbdevice_fs.C_INT.withName("start_frame"), + MemoryLayout.paddingLayout(4), + usbdevice_fs.C_INT.withName("error_count"), + usbdevice_fs.C_INT.withName("signr"), + usbdevice_fs.C_POINTER.withName("usercontext"), + MemoryLayout.sequenceLayout(0, usbdevfs_iso_packet_desc.layout()).withName("iso_frame_desc") + ).withName("usbdevfs_urb"); + + /** + * The layout of this struct + */ + public static final GroupLayout layout() { + return $LAYOUT; + } + + private static final OfByte type$LAYOUT = (OfByte)$LAYOUT.select(groupElement("type")); + + /** + * Layout for field: + * {@snippet lang=c : + * unsigned char type + * } + */ + public static final OfByte type$layout() { + return type$LAYOUT; + } + + private static final long type$OFFSET = $LAYOUT.byteOffset(groupElement("type")); + + /** + * Offset for field: + * {@snippet lang=c : + * unsigned char type + * } + */ + public static final long type$offset() { + return type$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * unsigned char type + * } + */ + public static byte type(MemorySegment struct) { + return struct.get(type$LAYOUT, type$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * unsigned char type + * } + */ + public static void type(MemorySegment struct, byte fieldValue) { + struct.set(type$LAYOUT, type$OFFSET, fieldValue); + } + + private static final OfByte endpoint$LAYOUT = (OfByte)$LAYOUT.select(groupElement("endpoint")); + + /** + * Layout for field: + * {@snippet lang=c : + * unsigned char endpoint + * } + */ + public static final OfByte endpoint$layout() { + return endpoint$LAYOUT; + } + + private static final long endpoint$OFFSET = $LAYOUT.byteOffset(groupElement("endpoint")); + + /** + * Offset for field: + * {@snippet lang=c : + * unsigned char endpoint + * } + */ + public static final long endpoint$offset() { + return endpoint$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * unsigned char endpoint + * } + */ + public static byte endpoint(MemorySegment struct) { + return struct.get(endpoint$LAYOUT, endpoint$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * unsigned char endpoint + * } + */ + public static void endpoint(MemorySegment struct, byte fieldValue) { + struct.set(endpoint$LAYOUT, endpoint$OFFSET, fieldValue); + } + + private static final OfInt status$LAYOUT = (OfInt)$LAYOUT.select(groupElement("status")); + + /** + * Layout for field: + * {@snippet lang=c : + * int status + * } + */ + public static final OfInt status$layout() { + return status$LAYOUT; + } + + private static final long status$OFFSET = $LAYOUT.byteOffset(groupElement("status")); + + /** + * Offset for field: + * {@snippet lang=c : + * int status + * } + */ + public static final long status$offset() { + return status$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * int status + * } + */ + public static int status(MemorySegment struct) { + return struct.get(status$LAYOUT, status$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * int status + * } + */ + public static void status(MemorySegment struct, int fieldValue) { + struct.set(status$LAYOUT, status$OFFSET, fieldValue); + } + + private static final OfInt flags$LAYOUT = (OfInt)$LAYOUT.select(groupElement("flags")); + + /** + * Layout for field: + * {@snippet lang=c : + * unsigned int flags + * } + */ + public static final OfInt flags$layout() { + return flags$LAYOUT; + } + + private static final long flags$OFFSET = $LAYOUT.byteOffset(groupElement("flags")); + + /** + * Offset for field: + * {@snippet lang=c : + * unsigned int flags + * } + */ + public static final long flags$offset() { + return flags$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * unsigned int flags + * } + */ + public static int flags(MemorySegment struct) { + return struct.get(flags$LAYOUT, flags$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * unsigned int flags + * } + */ + public static void flags(MemorySegment struct, int fieldValue) { + struct.set(flags$LAYOUT, flags$OFFSET, fieldValue); + } + + private static final AddressLayout buffer$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("buffer")); + + /** + * Layout for field: + * {@snippet lang=c : + * void *buffer + * } + */ + public static final AddressLayout buffer$layout() { + return buffer$LAYOUT; + } + + private static final long buffer$OFFSET = $LAYOUT.byteOffset(groupElement("buffer")); + + /** + * Offset for field: + * {@snippet lang=c : + * void *buffer + * } + */ + public static final long buffer$offset() { + return buffer$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * void *buffer + * } + */ + public static MemorySegment buffer(MemorySegment struct) { + return struct.get(buffer$LAYOUT, buffer$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * void *buffer + * } + */ + public static void buffer(MemorySegment struct, MemorySegment fieldValue) { + struct.set(buffer$LAYOUT, buffer$OFFSET, fieldValue); + } + + private static final OfInt buffer_length$LAYOUT = (OfInt)$LAYOUT.select(groupElement("buffer_length")); + + /** + * Layout for field: + * {@snippet lang=c : + * int buffer_length + * } + */ + public static final OfInt buffer_length$layout() { + return buffer_length$LAYOUT; + } + + private static final long buffer_length$OFFSET = $LAYOUT.byteOffset(groupElement("buffer_length")); + + /** + * Offset for field: + * {@snippet lang=c : + * int buffer_length + * } + */ + public static final long buffer_length$offset() { + return buffer_length$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * int buffer_length + * } + */ + public static int buffer_length(MemorySegment struct) { + return struct.get(buffer_length$LAYOUT, buffer_length$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * int buffer_length + * } + */ + public static void buffer_length(MemorySegment struct, int fieldValue) { + struct.set(buffer_length$LAYOUT, buffer_length$OFFSET, fieldValue); + } + + private static final OfInt actual_length$LAYOUT = (OfInt)$LAYOUT.select(groupElement("actual_length")); + + /** + * Layout for field: + * {@snippet lang=c : + * int actual_length + * } + */ + public static final OfInt actual_length$layout() { + return actual_length$LAYOUT; + } + + private static final long actual_length$OFFSET = $LAYOUT.byteOffset(groupElement("actual_length")); + + /** + * Offset for field: + * {@snippet lang=c : + * int actual_length + * } + */ + public static final long actual_length$offset() { + return actual_length$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * int actual_length + * } + */ + public static int actual_length(MemorySegment struct) { + return struct.get(actual_length$LAYOUT, actual_length$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * int actual_length + * } + */ + public static void actual_length(MemorySegment struct, int fieldValue) { + struct.set(actual_length$LAYOUT, actual_length$OFFSET, fieldValue); + } + + private static final OfInt start_frame$LAYOUT = (OfInt)$LAYOUT.select(groupElement("start_frame")); + + /** + * Layout for field: + * {@snippet lang=c : + * int start_frame + * } + */ + public static final OfInt start_frame$layout() { + return start_frame$LAYOUT; + } + + private static final long start_frame$OFFSET = $LAYOUT.byteOffset(groupElement("start_frame")); + + /** + * Offset for field: + * {@snippet lang=c : + * int start_frame + * } + */ + public static final long start_frame$offset() { + return start_frame$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * int start_frame + * } + */ + public static int start_frame(MemorySegment struct) { + return struct.get(start_frame$LAYOUT, start_frame$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * int start_frame + * } + */ + public static void start_frame(MemorySegment struct, int fieldValue) { + struct.set(start_frame$LAYOUT, start_frame$OFFSET, fieldValue); + } + + private static final OfInt error_count$LAYOUT = (OfInt)$LAYOUT.select(groupElement("error_count")); + + /** + * Layout for field: + * {@snippet lang=c : + * int error_count + * } + */ + public static final OfInt error_count$layout() { + return error_count$LAYOUT; + } + + private static final long error_count$OFFSET = $LAYOUT.byteOffset(groupElement("error_count")); + + /** + * Offset for field: + * {@snippet lang=c : + * int error_count + * } + */ + public static final long error_count$offset() { + return error_count$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * int error_count + * } + */ + public static int error_count(MemorySegment struct) { + return struct.get(error_count$LAYOUT, error_count$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * int error_count + * } + */ + public static void error_count(MemorySegment struct, int fieldValue) { + struct.set(error_count$LAYOUT, error_count$OFFSET, fieldValue); + } + + private static final OfInt signr$LAYOUT = (OfInt)$LAYOUT.select(groupElement("signr")); + + /** + * Layout for field: + * {@snippet lang=c : + * unsigned int signr + * } + */ + public static final OfInt signr$layout() { + return signr$LAYOUT; + } + + private static final long signr$OFFSET = $LAYOUT.byteOffset(groupElement("signr")); + + /** + * Offset for field: + * {@snippet lang=c : + * unsigned int signr + * } + */ + public static final long signr$offset() { + return signr$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * unsigned int signr + * } + */ + public static int signr(MemorySegment struct) { + return struct.get(signr$LAYOUT, signr$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * unsigned int signr + * } + */ + public static void signr(MemorySegment struct, int fieldValue) { + struct.set(signr$LAYOUT, signr$OFFSET, fieldValue); + } + + private static final AddressLayout usercontext$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("usercontext")); + + /** + * Layout for field: + * {@snippet lang=c : + * void *usercontext + * } + */ + public static final AddressLayout usercontext$layout() { + return usercontext$LAYOUT; + } + + private static final long usercontext$OFFSET = $LAYOUT.byteOffset(groupElement("usercontext")); + + /** + * Offset for field: + * {@snippet lang=c : + * void *usercontext + * } + */ + public static final long usercontext$offset() { + return usercontext$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * void *usercontext + * } + */ + public static MemorySegment usercontext(MemorySegment struct) { + return struct.get(usercontext$LAYOUT, usercontext$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * void *usercontext + * } + */ + public static void usercontext(MemorySegment struct, MemorySegment fieldValue) { + struct.set(usercontext$LAYOUT, usercontext$OFFSET, fieldValue); + } + + private static final SequenceLayout iso_frame_desc$LAYOUT = (SequenceLayout)$LAYOUT.select(groupElement("iso_frame_desc")); + + /** + * Layout for field: + * {@snippet lang=c : + * struct usbdevfs_iso_packet_desc iso_frame_desc[] + * } + */ + public static final SequenceLayout iso_frame_desc$layout() { + return iso_frame_desc$LAYOUT; + } + + private static final long iso_frame_desc$OFFSET = $LAYOUT.byteOffset(groupElement("iso_frame_desc")); + + /** + * Offset for field: + * {@snippet lang=c : + * struct usbdevfs_iso_packet_desc iso_frame_desc[] + * } + */ + public static final long iso_frame_desc$offset() { + return iso_frame_desc$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * struct usbdevfs_iso_packet_desc iso_frame_desc[] + * } + */ + public static MemorySegment iso_frame_desc(MemorySegment struct) { + return struct.asSlice(iso_frame_desc$OFFSET, iso_frame_desc$LAYOUT.byteSize()); + } + + /** + * Setter for field: + * {@snippet lang=c : + * struct usbdevfs_iso_packet_desc iso_frame_desc[] + * } + */ + public static void iso_frame_desc(MemorySegment struct, MemorySegment fieldValue) { + MemorySegment.copy(fieldValue, 0L, struct, iso_frame_desc$OFFSET, iso_frame_desc$LAYOUT.byteSize()); + } + + /** + * Obtains a slice of {@code arrayParam} which selects the array element at {@code index}. + * The returned segment has address {@code arrayParam.address() + index * layout().byteSize()} + */ + public static MemorySegment asSlice(MemorySegment array, long index) { + return array.asSlice(layout().byteSize() * index); + } + + /** + * The size (in bytes) of this struct + */ + public static long sizeof() { return layout().byteSize(); } + + /** + * Allocate a segment of size {@code layout().byteSize()} using {@code allocator} + */ + public static MemorySegment allocate(SegmentAllocator allocator) { + return allocator.allocate(layout()); + } + + /** + * Allocate an array of size {@code elementCount} using {@code allocator}. + * The returned segment has size {@code elementCount * layout().byteSize()}. + */ + public static MemorySegment allocateArray(long elementCount, SegmentAllocator allocator) { + return allocator.allocate(MemoryLayout.sequenceLayout(elementCount, layout())); + } + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, Arena arena, Consumer cleanup) { + return reinterpret(addr, 1, arena, cleanup); + } + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code elementCount * layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, long elementCount, Arena arena, Consumer cleanup) { + return addr.reinterpret(layout().byteSize() * elementCount, arena, cleanup); + } +} + diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevice_fs$shared.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevice_fs$shared.java new file mode 100644 index 00000000..bdf645da --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevice_fs$shared.java @@ -0,0 +1,63 @@ +// Generated by jextract + +package net.codecrete.usb.linux.gen.usbdevice_fs; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +public class usbdevice_fs$shared { + + usbdevice_fs$shared() { + // Should not be called directly + } + + public static final ValueLayout.OfBoolean C_BOOL = (ValueLayout.OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool"); + public static final ValueLayout.OfByte C_CHAR =(ValueLayout.OfByte)Linker.nativeLinker().canonicalLayouts().get("char"); + public static final ValueLayout.OfShort C_SHORT = (ValueLayout.OfShort) Linker.nativeLinker().canonicalLayouts().get("short"); + public static final ValueLayout.OfInt C_INT = (ValueLayout.OfInt) Linker.nativeLinker().canonicalLayouts().get("int"); + public static final ValueLayout.OfLong C_LONG_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long long"); + public static final ValueLayout.OfFloat C_FLOAT = (ValueLayout.OfFloat) Linker.nativeLinker().canonicalLayouts().get("float"); + public static final ValueLayout.OfDouble C_DOUBLE = (ValueLayout.OfDouble) Linker.nativeLinker().canonicalLayouts().get("double"); + public static final AddressLayout C_POINTER = ((AddressLayout) Linker.nativeLinker().canonicalLayouts().get("void*")) + .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, C_CHAR)); + public static final ValueLayout.OfLong C_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long"); + + static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + + static void traceDowncall(String name, Object... args) { + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("%s(%s)\n", name, traceArgs); + } + + static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { + try { + return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } + + static MemoryLayout align(MemoryLayout layout, long align) { + return switch (layout) { + case PaddingLayout p -> p; + case ValueLayout v -> v.withByteAlignment(align); + case GroupLayout g -> { + MemoryLayout[] alignedMembers = g.memberLayouts().stream() + .map(m -> align(m, align)).toArray(MemoryLayout[]::new); + yield g instanceof StructLayout ? + MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); + } + case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); + }; + } +} + diff --git a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevice_fs.java b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevice_fs.java index e396cb35..8521d71b 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevice_fs.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevice_fs.java @@ -2,18 +2,71 @@ package net.codecrete.usb.linux.gen.usbdevice_fs; +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + import static java.lang.foreign.ValueLayout.*; -public class usbdevice_fs { - - /* package-private */ usbdevice_fs() {} - public static OfByte C_CHAR = Constants$root.C_CHAR$LAYOUT; - public static OfShort C_SHORT = Constants$root.C_SHORT$LAYOUT; - public static OfInt C_INT = Constants$root.C_INT$LAYOUT; - public static OfLong C_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfLong C_LONG_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfFloat C_FLOAT = Constants$root.C_FLOAT$LAYOUT; - public static OfDouble C_DOUBLE = Constants$root.C_DOUBLE$LAYOUT; - public static OfAddress C_POINTER = Constants$root.C_POINTER$LAYOUT; -} +import static java.lang.foreign.MemoryLayout.PathElement.*; + +public class usbdevice_fs extends usbdevice_fs$shared { + + usbdevice_fs() { + // Should not be called directly + } + static final Arena LIBRARY_ARENA = Arena.ofAuto(); + + static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup() + .or(Linker.nativeLinker().defaultLookup()); + + private static final int USBDEVFS_URB_TYPE_ISO = (int)0L; + /** + * {@snippet lang=c : + * #define USBDEVFS_URB_TYPE_ISO 0 + * } + */ + public static int USBDEVFS_URB_TYPE_ISO() { + return USBDEVFS_URB_TYPE_ISO; + } + private static final int USBDEVFS_URB_TYPE_INTERRUPT = (int)1L; + /** + * {@snippet lang=c : + * #define USBDEVFS_URB_TYPE_INTERRUPT 1 + * } + */ + public static int USBDEVFS_URB_TYPE_INTERRUPT() { + return USBDEVFS_URB_TYPE_INTERRUPT; + } + private static final int USBDEVFS_URB_TYPE_CONTROL = (int)2L; + /** + * {@snippet lang=c : + * #define USBDEVFS_URB_TYPE_CONTROL 2 + * } + */ + public static int USBDEVFS_URB_TYPE_CONTROL() { + return USBDEVFS_URB_TYPE_CONTROL; + } + private static final int USBDEVFS_URB_TYPE_BULK = (int)3L; + /** + * {@snippet lang=c : + * #define USBDEVFS_URB_TYPE_BULK 3 + * } + */ + public static int USBDEVFS_URB_TYPE_BULK() { + return USBDEVFS_URB_TYPE_BULK; + } + private static final int USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER = (int)2L; + /** + * {@snippet lang=c : + * #define USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER 2 + * } + */ + public static int USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER() { + return USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER; + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/CoreFoundationHelper.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/CoreFoundationHelper.java index 6c663f8a..206d7a63 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/CoreFoundationHelper.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/CoreFoundationHelper.java @@ -10,39 +10,37 @@ import net.codecrete.usb.macos.gen.corefoundation.CFRange; import net.codecrete.usb.macos.gen.corefoundation.CoreFoundation; -import java.lang.foreign.MemoryAddress; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentAllocator; -import static java.lang.foreign.MemoryAddress.NULL; +import static java.lang.foreign.MemorySegment.NULL; import static java.lang.foreign.ValueLayout.JAVA_CHAR; /** * Core Foundation helper functions */ -public class CoreFoundationHelper { +class CoreFoundationHelper { + + private CoreFoundationHelper() { + } /** * Gets Java string as a copy of the {@code CFStringRef}. * * @param string the string to copy ({@code CFStringRef}) + * @param arena the arena to allocate memory * @return copied string */ - public static String stringFromCFStringRef(MemoryAddress string) { - - try (var session = MemorySession.openConfined()) { - - long strLen = CoreFoundation.CFStringGetLength(string); - var buffer = session.allocateArray(JAVA_CHAR, strLen); - var range = session.allocate(CFRange.$LAYOUT()); - CFRange.location$set(range, 0); - CFRange.length$set(range, strLen); - CoreFoundation.CFStringGetCharacters(string, range, buffer); - return new String(buffer.toArray(JAVA_CHAR)); - - } catch (Throwable t) { - throw new RuntimeException(t); - } + static String stringFromCFStringRef(MemorySegment string, Arena arena) { + + var strLen = CoreFoundation.CFStringGetLength(string); + var buffer = arena.allocate(JAVA_CHAR, strLen); + var range = CFRange.allocate(arena); + CFRange.location(range, 0); + CFRange.length(range, strLen); + CoreFoundation.CFStringGetCharacters(string, range, buffer); + return new String(buffer.toArray(JAVA_CHAR)); } /** @@ -52,15 +50,14 @@ public static String stringFromCFStringRef(MemoryAddress string) { * ownership without incrementing the reference count. *

* - * @param string the string + * @param string the string + * @param allocator the allocator for allocating memory * @return {@code CFStringRef} */ - public static MemoryAddress createCFStringRef(String string) { - try (var session = MemorySession.openConfined()) { - char[] charArray = string.toCharArray(); - var chars = session.allocateArray(JAVA_CHAR, charArray.length); - chars.copyFrom(MemorySegment.ofArray(charArray)); - return CoreFoundation.CFStringCreateWithCharacters(NULL, chars, string.length()); - } + static MemorySegment createCFStringRef(String string, SegmentAllocator allocator) { + var charArray = string.toCharArray(); + var chars = allocator.allocate(JAVA_CHAR, charArray.length); + chars.copyFrom(MemorySegment.ofArray(charArray)); + return CoreFoundation.CFStringCreateWithCharacters(NULL, chars, string.length()); } } diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/IoKitHelper.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/IoKitHelper.java index 4e03bda3..08d35279 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/IoKitHelper.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/IoKitHelper.java @@ -10,73 +10,125 @@ import net.codecrete.usb.macos.gen.corefoundation.CoreFoundation; import net.codecrete.usb.macos.gen.iokit.IOKit; -import java.lang.foreign.Addressable; -import java.lang.foreign.MemoryAddress; +import java.lang.foreign.Arena; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemoryLayout.PathElement; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.StructLayout; +import java.lang.invoke.VarHandle; -import static java.lang.foreign.MemoryAddress.NULL; +import static java.lang.foreign.MemorySegment.NULL; import static java.lang.foreign.ValueLayout.ADDRESS; import static java.lang.foreign.ValueLayout.JAVA_INT; +import static net.codecrete.usb.common.ForeignMemory.dereference; /** * Constants and helper functions for the IOKit framework. */ -public class IoKitHelper { - public static final MemoryAddress kIOUSBDeviceUserClientTypeID = UUID.CreateCFUUID( - new byte[]{(byte) 0x9d, (byte) 0xc7, (byte) 0xb7, (byte) 0x80, (byte) 0x9e, (byte) 0xc0, (byte) 0x11, - (byte) 0xD4, (byte) 0xa5, (byte) 0x4f, (byte) 0x00, (byte) 0x0a, (byte) 0x27, (byte) 0x05, - (byte) 0x28, (byte) 0x61}); - public static final MemoryAddress kIOUSBInterfaceUserClientTypeID = UUID.CreateCFUUID( - new byte[]{(byte) 0x2d, (byte) 0x97, (byte) 0x86, (byte) 0xc6, (byte) 0x9e, (byte) 0xf3, (byte) 0x11, - (byte) 0xD4, (byte) 0xad, (byte) 0x51, (byte) 0x00, (byte) 0x0a, (byte) 0x27, (byte) 0x05, - (byte) 0x28, (byte) 0x61}); - public static final MemoryAddress kIOUSBDeviceInterfaceID100 = UUID.CreateCFUUID( - new byte[]{(byte) 0x5c, (byte) 0x81, (byte) 0x87, (byte) 0xd0, (byte) 0x9e, (byte) 0xf3, (byte) 0x11, - (byte) 0xD4, (byte) 0x8b, (byte) 0x45, (byte) 0x00, (byte) 0x0a, (byte) 0x27, (byte) 0x05, - (byte) 0x28, (byte) 0x61}); - public static final MemoryAddress kIOUSBInterfaceInterfaceID100 = UUID.CreateCFUUID( - new byte[]{(byte) 0x73, (byte) 0xc9, (byte) 0x7a, (byte) 0xe8, (byte) 0x9e, (byte) 0xf3, (byte) 0x11, - (byte) 0xD4, (byte) 0xb1, (byte) 0xd0, (byte) 0x00, (byte) 0x0a, (byte) 0x27, (byte) 0x05, - (byte) 0x28, (byte) 0x61}); - public static final MemoryAddress kIOCFPlugInInterfaceID = UUID.CreateCFUUID( - new byte[]{(byte) 0xC2, (byte) 0x44, (byte) 0xE8, (byte) 0x58, (byte) 0x10, (byte) 0x9C, (byte) 0x11, - (byte) 0xD4, (byte) 0x91, (byte) 0xD4, (byte) 0x00, (byte) 0x50, (byte) 0xE4, (byte) 0xC6, - (byte) 0x42, (byte) 0x6F}); +class IoKitHelper { + + private IoKitHelper() { + } + + static final MemorySegment kIOUSBDeviceUserClientTypeID = UUID.createCFUUID(new byte[]{(byte) 0x9d, + (byte) 0xc7, (byte) 0xb7, (byte) 0x80, (byte) 0x9e, (byte) 0xc0, (byte) 0x11, (byte) 0xD4, (byte) 0xa5, + (byte) 0x4f, (byte) 0x00, (byte) 0x0a, (byte) 0x27, (byte) 0x05, (byte) 0x28, (byte) 0x61}); + static final MemorySegment kIOUSBInterfaceUserClientTypeID = UUID.createCFUUID(new byte[]{(byte) 0x2d, + (byte) 0x97, (byte) 0x86, (byte) 0xc6, (byte) 0x9e, (byte) 0xf3, (byte) 0x11, (byte) 0xD4, (byte) 0xad, + (byte) 0x51, (byte) 0x00, (byte) 0x0a, (byte) 0x27, (byte) 0x05, (byte) 0x28, (byte) 0x61}); + static final MemorySegment kIOUSBDeviceInterfaceID187 = UUID.createCFUUID(new byte[]{(byte) 0x3c, + (byte) 0x9e, (byte) 0xe1, (byte) 0xeb, (byte) 0x24, (byte) 0x02, (byte) 0x11, (byte) 0xb2, (byte) 0x8e, + (byte) 0x7e, (byte) 0x00, (byte) 0x0a, (byte) 0x27, (byte) 0x80, (byte) 0x1e, (byte) 0x86}); + static final MemorySegment kIOUSBInterfaceInterfaceID190 = UUID.createCFUUID(new byte[]{(byte) 0x8f, + (byte) 0xdb, (byte) 0x84, (byte) 0x55, (byte) 0x74, (byte) 0xa6, (byte) 0x11, (byte) 0xD6, (byte) 0x97, + (byte) 0xb1, (byte) 0x00, (byte) 0x30, (byte) 0x65, (byte) 0xd3, (byte) 0x60, (byte) 0x8e}); + static final MemorySegment kIOCFPlugInInterfaceID = UUID.createCFUUID(new byte[]{(byte) 0xC2, (byte) 0x44, + (byte) 0xE8, (byte) 0x58, (byte) 0x10, (byte) 0x9C, (byte) 0x11, (byte) 0xD4, (byte) 0x91, (byte) 0xD4, + (byte) 0x00, (byte) 0x50, (byte) 0xE4, (byte) 0xC6, (byte) 0x42, (byte) 0x6F}); + + /** + * Layout of COM object. + *

+ * Parts of I/O Kit use a plug-in architecture following the Component Object Model (COM). + * This layout is the basis for calling object methods (through the vtable) and + * accessing the reference count (for debugging purposes). + *

+ */ + static final StructLayout COM_OBJECT = + MemoryLayout.structLayout( + ADDRESS.withTargetLayout( + MemoryLayout.structLayout( + // up to 100 function pointers + MemoryLayout.sequenceLayout(100, ADDRESS) + ) + ).withName("vtable"), + ADDRESS.withTargetLayout( + MemoryLayout.structLayout( + ADDRESS.withName("unknown"), + JAVA_INT.withName("refCount") + ) + ).withName("data") + ); + + /** + * Var handle for accessing the vtable. + *

+ * The vtable is an array of function pointers. + *

+ */ + static final VarHandle vtable$VH = COM_OBJECT.varHandle(PathElement.groupElement("vtable")); /** - * Get an interface of the specified service. + * Var handle for accessing the reference count. + */ + static final VarHandle refCount$VH = COM_OBJECT.varHandle( + PathElement.groupElement("data"), + PathElement.dereferenceElement(), + PathElement.groupElement("refCount") + ); + + /** + * Get the vtable of the specified object instance. + * @param self object instance + * @return vtable + */ + static MemorySegment getVtable(MemorySegment self) { + return (MemorySegment) vtable$VH.get(self, 0); + } + + /** + * Gets an object instance implementing the specified service. *

- * This method first request the specified plugin interfaces and then + * This method first requests the specified plugin interface and then * queries for the specified interface. *

* * @param service the service * @param pluginType the plugin interface type * @param interfaceId the interface ID - * @return the interface, or null if the plugin type or interface is not available + * @return object instance implementing the interface, or {@code null} if the plugin type or interface is not available */ - public static MemoryAddress getInterface(int service, Addressable pluginType, MemoryAddress interfaceId) { - try (var session = MemorySession.openConfined()) { + static MemorySegment getInterface(int service, MemorySegment pluginType, MemorySegment interfaceId) { + try (var arena = Arena.ofConfined()) { // MemorySegment for holding IOCFPlugInInterface** - var plugHolder = session.allocate(ADDRESS, NULL); + var plugHolder = arena.allocate(ADDRESS); // MemorySegment for holding score - var score = session.allocate(JAVA_INT, 0); - int ret = IOKit.IOCreatePlugInInterfaceForService(service, pluginType, kIOCFPlugInInterfaceID, - plugHolder, score); + var score = arena.allocate(JAVA_INT); + var ret = IOKit.IOCreatePlugInInterfaceForService(service, pluginType, kIOCFPlugInInterfaceID, plugHolder + , score); if (ret != 0) return null; - var plug = (MemoryAddress) plugHolder.get(ADDRESS, 0); + var plug = dereference(plugHolder, COM_OBJECT); - // MemorySegment for holding xxxInterface** - var intfHolder = session.allocate(ADDRESS, NULL); // UUID bytes - var refiid = CoreFoundation.CFUUIDGetUUIDBytes(session, interfaceId); - ret = IoKitUSB.QueryInterface(plug, refiid, intfHolder.address()); - IoKitUSB.Release(plug); + var refiid = CoreFoundation.CFUUIDGetUUIDBytes(arena, interfaceId); + // MemorySegment for holding xxxInterface** + var intfHolder = arena.allocate(ADDRESS); + ret = IoKitUsb.QueryInterface(plug, refiid, intfHolder); + IoKitUsb.Release(plug); if (ret != 0) return null; - return intfHolder.get(ADDRESS, 0); + return dereference(intfHolder, COM_OBJECT); } } @@ -88,27 +140,25 @@ public static MemoryAddress getInterface(int service, Addressable pluginType, Me * * @param service the service * @param key the property key + * @param arena the arena for allocating memory * @return the property value, or {@code null} if the service doesn't have the property */ - public static Integer getPropertyInt(int service, String key) { + static Integer getPropertyInt(int service, MemorySegment key, Arena arena) { - var cfKey = CoreFoundationHelper.createCFStringRef(key); - var value = IOKit.IORegistryEntryCreateCFProperty(service, cfKey, NULL, 0); - if (value == NULL) + var value = IOKit.IORegistryEntryCreateCFProperty(service, key, NULL, 0); + if (value.address() == 0) return null; Integer result = null; var type = CoreFoundation.CFGetTypeID(value); if (type == CoreFoundation.CFNumberGetTypeID()) { - - try (var session = MemorySession.openConfined()) { - var numberValue = session.allocate(JAVA_INT, 0); - if (CoreFoundation.CFNumberGetValue(value, CoreFoundation.kCFNumberSInt32Type(), numberValue) != 0) - result = numberValue.get(JAVA_INT, 0); - } + var numberValue = arena.allocate(JAVA_INT); + if (CoreFoundation.CFNumberGetValue(value, CoreFoundation.kCFNumberSInt32Type(), numberValue) != 0) + result = numberValue.get(JAVA_INT, 0); } CoreFoundation.CFRelease(value); + return result; } @@ -120,31 +170,27 @@ public static Integer getPropertyInt(int service, String key) { * * @param service the service * @param key the property key + * @param arena the arena for allocating memory * @return the property value, or {@code null} if the service doesn't have the property */ - public static String getPropertyString(int service, String key) { + static String getPropertyString(int service, MemorySegment key, Arena arena) { - var cfKey = CoreFoundationHelper.createCFStringRef(key); - var value = IOKit.IORegistryEntryCreateCFProperty(service, cfKey, NULL, 0); - if (value == NULL) + var value = IOKit.IORegistryEntryCreateCFProperty(service, key, NULL, 0); + if (value.address() == 0) return null; String result = null; var type = CoreFoundation.CFGetTypeID(value); if (type == CoreFoundation.CFStringGetTypeID()) - result = CoreFoundationHelper.stringFromCFStringRef(value); + result = CoreFoundationHelper.stringFromCFStringRef(value, arena); CoreFoundation.CFRelease(value); + return result; } // debugging aid - public static int getRefCount(MemoryAddress self) { - try (var session = MemorySession.openConfined()) { - var object = MemorySegment.ofAddress(self, 16, session); - var dataAddr = object.get(ADDRESS, ADDRESS.byteSize()); - var data = MemorySegment.ofAddress(dataAddr, 12, session); - return data.get(JAVA_INT, 8); - } + static int getRefCount(MemorySegment self) { + return (int) refCount$VH.get(self); } } diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/IoKitUSB.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/IoKitUSB.java deleted file mode 100644 index 4ac6d155..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/IoKitUSB.java +++ /dev/null @@ -1,148 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// - -package net.codecrete.usb.macos; - -import net.codecrete.usb.macos.gen.iokit.IOUSBDeviceInterface; -import net.codecrete.usb.macos.gen.iokit.IOUSBInterfaceInterface; - -import java.lang.foreign.MemoryAddress; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; - -import static java.lang.foreign.ValueLayout.ADDRESS; - -/** - * Helper functions to call the virtual methods of IOKit USB interfaces. - */ -public class IoKitUSB { - - private static MemorySegment getVtable(MemoryAddress self, MemorySession session) { - var object = MemorySegment.ofAddress(self, ADDRESS.byteSize(), session); - var vtableAddr = object.get(ADDRESS, 0); - // 800: size of biggest vtable and then some - return MemorySegment.ofAddress(vtableAddr, 800, session); - } - - // HRESULT (STDMETHODCALLTYPE *QueryInterface)(void *thisPointer, REFIID iid, LPVOID *ppv); - public static int QueryInterface(MemoryAddress self, MemorySegment iid, MemoryAddress ppv) { - try (var session = MemorySession.openConfined()) { - return IOUSBDeviceInterface.QueryInterface(getVtable(self, session), session).apply(self, iid, ppv); - } - } - - // ULONG (STDMETHODCALLTYPE *AddRef)(void *thisPointer); - public static int AddRef(MemoryAddress self) { - try (var session = MemorySession.openConfined()) { - return IOUSBDeviceInterface.AddRef(getVtable(self, session), session).apply(self); - } - } - - // ULONG (STDMETHODCALLTYPE *Release)(void *thisPointer) - public static int Release(MemoryAddress self) { - try (var session = MemorySession.openConfined()) { - return IOUSBDeviceInterface.Release(getVtable(self, session), session).apply(self); - } - } - - // IOReturn (*USBDeviceOpen)(void *self); - public static int USBDeviceOpen(MemoryAddress self) { - try (var session = MemorySession.openConfined()) { - return IOUSBDeviceInterface.USBDeviceOpen(getVtable(self, session), session).apply(self); - } - } - - // IOReturn (*USBDeviceClose)(void *self); - public static int USBDeviceClose(MemoryAddress self) { - try (var session = MemorySession.openConfined()) { - return IOUSBDeviceInterface.USBDeviceClose(getVtable(self, session), session).apply(self); - } - } - - // IOReturn (*GetConfigurationDescriptorPtr)(void *self, UInt8 configIndex, IOUSBConfigurationDescriptorPtr *desc); - public static int GetConfigurationDescriptorPtr(MemoryAddress self, byte configIndex, - MemoryAddress descHolder) { - try (var session = MemorySession.openConfined()) { - return IOUSBDeviceInterface.GetConfigurationDescriptorPtr(getVtable(self, session), session).apply(self, configIndex, descHolder); - } - } - - // IOReturn (*SetConfiguration)(void *self, UInt8 configNum); - public static int SetConfiguration(MemoryAddress self, byte configValue) { - try (var session = MemorySession.openConfined()) { - return IOUSBDeviceInterface.SetConfiguration(getVtable(self, session), session).apply(self, configValue); - } - } - - // IOReturn (*CreateInterfaceIterator)(void *self, IOUSBFindInterfaceRequest *req, io_iterator_t *iter); - public static int CreateInterfaceIterator(MemoryAddress self, MemoryAddress req, MemoryAddress iter) { - try (var session = MemorySession.openConfined()) { - return IOUSBDeviceInterface.CreateInterfaceIterator(getVtable(self, session), session).apply(self, req, iter); - } - } - - // IOReturn (*DeviceRequest)(void *self, IOUSBDevRequest *req); - public static int DeviceRequest(MemoryAddress self, MemoryAddress deviceRequest) { - try (var session = MemorySession.openConfined()) { - return IOUSBDeviceInterface.DeviceRequest(getVtable(self, session), session).apply(self, deviceRequest); - } - } - - // IOReturn (*USBInterfaceOpen)(void *self);; - public static int USBInterfaceOpen(MemoryAddress self) { - try (var session = MemorySession.openConfined()) { - return IOUSBInterfaceInterface.USBInterfaceOpen(getVtable(self, session), session).apply(self); - } - } - - // IOReturn (*USBInterfaceClose)(void *self);; - public static int USBInterfaceClose(MemoryAddress self) { - try (var session = MemorySession.openConfined()) { - return IOUSBInterfaceInterface.USBInterfaceClose(getVtable(self, session), session).apply(self); - } - } - - // IOReturn (*GetInterfaceNumber)(void *self, UInt8 *intfNumber); - public static int GetInterfaceNumber(MemoryAddress self, MemoryAddress intfNumberHolder) { - try (var session = MemorySession.openConfined()) { - return IOUSBInterfaceInterface.GetInterfaceNumber(getVtable(self, session), session).apply(self, intfNumberHolder); - } - } - - // IOReturn (*GetNumEndpoints)(void *self, UInt8 *intfNumEndpoints); - public static int GetNumEndpoints(MemoryAddress self, MemoryAddress intfNumEndpointsHolder) { - try (var session = MemorySession.openConfined()) { - return IOUSBInterfaceInterface.GetNumEndpoints(getVtable(self, session), session).apply(self, intfNumEndpointsHolder); - } - } - - // IOReturn (*GetPipeProperties)(void *self, UInt8 pipeRef, UInt8 *direction, UInt8 *number, UInt8 *transferType, - // UInt16 *maxPacketSize, UInt8 *interval); - public static int GetPipeProperties(MemoryAddress self, byte pipeRef, MemoryAddress directionHolder, - MemoryAddress numberHolder, MemoryAddress transferTypeHolder, - MemoryAddress maxPacketSizeHolder, MemoryAddress intervalHolder) { - try (var session = MemorySession.openConfined()) { - return IOUSBInterfaceInterface.GetPipeProperties(getVtable(self, session), session).apply(self, pipeRef, - directionHolder, numberHolder, transferTypeHolder, maxPacketSizeHolder, intervalHolder); - } - } - - // IOReturn (*ReadPipe)(void *self, UInt8 pipeRef, void *buf, UInt32 *size); - public static int ReadPipe(MemoryAddress self, byte pipeRef, MemoryAddress buf, MemoryAddress sizeHolder) { - try (var session = MemorySession.openConfined()) { - return IOUSBInterfaceInterface.ReadPipe(getVtable(self, session), session) - .apply(self, pipeRef, buf, sizeHolder); - } - } - - // IOReturn (*WritePipe)(void *self, UInt8 pipeRef, void *buf, UInt32 size); - public static int WritePipe(MemoryAddress self, byte pipeRef, MemoryAddress buf, int size) { - try (var session = MemorySession.openConfined()) { - return IOUSBInterfaceInterface.WritePipe(getVtable(self, session), session).apply(self, pipeRef, buf, size); - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/IoKitUsb.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/IoKitUsb.java new file mode 100644 index 00000000..b1fd6fbe --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/IoKitUsb.java @@ -0,0 +1,186 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.macos; + +import net.codecrete.usb.macos.gen.iokit.IOUSBDeviceStruct187; +import net.codecrete.usb.macos.gen.iokit.IOUSBInterfaceStruct190; + +import java.lang.foreign.MemorySegment; + +import static net.codecrete.usb.macos.IoKitHelper.getVtable; + +/** + * Helper functions to call the virtual methods of IOKit USB interfaces. + */ +@SuppressWarnings({"java:S100", "java:S107", "UnusedReturnValue", "SameParameterValue"}) +class IoKitUsb { + + private IoKitUsb() { + } + + // HRESULT (STDMETHODCALLTYPE *QueryInterface)(void *thisPointer, REFIID iid, LPVOID *ppv) + static int QueryInterface(MemorySegment self, MemorySegment iid, MemorySegment ppv) { + return IOUSBDeviceStruct187.QueryInterface.invoke(IOUSBDeviceStruct187.QueryInterface(getVtable(self)), self, iid, ppv); + } + + // ULONG (STDMETHODCALLTYPE *AddRef)(void *thisPointer) + static int AddRef(MemorySegment self) { + return IOUSBDeviceStruct187.AddRef.invoke(IOUSBDeviceStruct187.AddRef(getVtable(self)), self); + } + + // ULONG (STDMETHODCALLTYPE *Release)(void *thisPointer) + static int Release(MemorySegment self) { + return IOUSBDeviceStruct187.Release.invoke(IOUSBDeviceStruct187.Release(getVtable(self)), self); + } + + // IOReturn (* CreateDeviceAsyncEventSource)(void* self, CFRunLoopSourceRef* source) + static int CreateDeviceAsyncEventSource(MemorySegment self, MemorySegment source) { + return IOUSBDeviceStruct187.CreateDeviceAsyncEventSource.invoke(IOUSBDeviceStruct187.CreateDeviceAsyncEventSource(getVtable(self)), self, + source); + } + + // CFRunLoopSourceRef (* GetDeviceAsyncEventSource)(void* self) + static MemorySegment GetDeviceAsyncEventSource(MemorySegment self) { + return IOUSBDeviceStruct187.GetDeviceAsyncEventSource.invoke(IOUSBDeviceStruct187.GetDeviceAsyncEventSource(getVtable(self)), self); + } + + // IOReturn (*USBDeviceOpenSeize)(void *self) + static int USBDeviceOpenSeize(MemorySegment self) { + return IOUSBDeviceStruct187.USBDeviceOpenSeize.invoke(IOUSBDeviceStruct187.USBDeviceOpenSeize(getVtable(self)), self); + } + + // IOReturn (*USBDeviceClose)(void *self) + static int USBDeviceClose(MemorySegment self) { + return IOUSBDeviceStruct187.USBDeviceClose.invoke(IOUSBDeviceStruct187.USBDeviceClose(getVtable(self)), self); + } + + // IOReturn (* USBDeviceReEnumerate)(void* self, UInt32 options) + static int USBDeviceReEnumerate(MemorySegment self, int options) { + return IOUSBDeviceStruct187.USBDeviceReEnumerate.invoke(IOUSBDeviceStruct187.USBDeviceReEnumerate(getVtable(self)), self, options); + } + + // IOReturn (*GetConfigurationDescriptorPtr)(void *self, UInt8 configIndex, IOUSBConfigurationDescriptorPtr *desc) + static int GetConfigurationDescriptorPtr(MemorySegment self, byte configIndex, MemorySegment descHolder) { + return IOUSBDeviceStruct187.GetConfigurationDescriptorPtr.invoke(IOUSBDeviceStruct187.GetConfigurationDescriptorPtr(getVtable(self)), self, + configIndex, descHolder); + } + + // IOReturn (*SetConfiguration)(void *self, UInt8 configNum) + static int SetConfiguration(MemorySegment self, byte configValue) { + return IOUSBDeviceStruct187.SetConfiguration.invoke(IOUSBDeviceStruct187.SetConfiguration(getVtable(self)), self, configValue); + } + + // IOReturn (*CreateInterfaceIterator)(void *self, IOUSBFindInterfaceRequest *req, io_iterator_t *iter) + static int CreateInterfaceIterator(MemorySegment self, MemorySegment req, MemorySegment iter) { + return IOUSBDeviceStruct187.CreateInterfaceIterator.invoke(IOUSBDeviceStruct187.CreateInterfaceIterator(getVtable(self)), self, req, iter); + } + + // IOReturn (* DeviceRequest)(void* self, IOUSBDevRequest* req) + static int DeviceRequest(MemorySegment self, MemorySegment deviceRequest) { + return IOUSBDeviceStruct187.DeviceRequest.invoke(IOUSBDeviceStruct187.DeviceRequest(getVtable(self)), self, deviceRequest); + } + + // IOReturn (* DeviceRequestAsync)(void* self, IOUSBDevRequest* req, IOAsyncCallback1 callback, void* refCon) + static int DeviceRequestAsync(MemorySegment self, MemorySegment deviceRequest, MemorySegment callback, + MemorySegment refCon) { + return IOUSBDeviceStruct187.DeviceRequestAsync.invoke(IOUSBDeviceStruct187.DeviceRequestAsync(getVtable(self)), self, deviceRequest, + callback, refCon); + } + + // IOReturn (*USBInterfaceOpen)(void *self) + static int USBInterfaceOpen(MemorySegment self) { + return IOUSBInterfaceStruct190.USBInterfaceOpen.invoke(IOUSBInterfaceStruct190.USBInterfaceOpen(getVtable(self)), self); + } + + // IOReturn (*USBInterfaceClose)(void *self) + static int USBInterfaceClose(MemorySegment self) { + return IOUSBInterfaceStruct190.USBInterfaceClose.invoke(IOUSBInterfaceStruct190.USBInterfaceClose(getVtable(self)), self); + } + + // IOReturn (*GetInterfaceNumber)(void *self, UInt8 *intfNumber) + static int GetInterfaceNumber(MemorySegment self, MemorySegment intfNumberHolder) { + return IOUSBInterfaceStruct190.GetInterfaceNumber.invoke(IOUSBInterfaceStruct190.GetInterfaceNumber(getVtable(self)), self, + intfNumberHolder); + } + + // IOReturn (*GetNumEndpoints)(void *self, UInt8 *intfNumEndpoints) + static int GetNumEndpoints(MemorySegment self, MemorySegment intfNumEndpointsHolder) { + return IOUSBInterfaceStruct190.GetNumEndpoints.invoke(IOUSBInterfaceStruct190.GetNumEndpoints(getVtable(self)), self, + intfNumEndpointsHolder); + } + + // IOReturn (*GetPipeProperties)(void *self, UInt8 pipeRef, UInt8 *direction, UInt8 *number, UInt8 *transferType, + // UInt16 *maxPacketSize, UInt8 *interval) + static int GetPipeProperties(MemorySegment self, byte pipeRef, MemorySegment directionHolder, + MemorySegment numberHolder, MemorySegment transferTypeHolder, + MemorySegment maxPacketSizeHolder, MemorySegment intervalHolder) { + return IOUSBInterfaceStruct190.GetPipeProperties.invoke(IOUSBInterfaceStruct190.GetPipeProperties(getVtable(self)), self, pipeRef, + directionHolder, numberHolder, transferTypeHolder, maxPacketSizeHolder, intervalHolder); + } + + // IOReturn (*ReadPipeAsync)(void *self, UInt8 pipeRef, void *buf, UInt32 size, IOAsyncCallback1 callback, void + // *refcon) + static int ReadPipeAsync(MemorySegment self, byte pipeRef, MemorySegment buf, int size, + MemorySegment callback, MemorySegment refcon) { + return IOUSBInterfaceStruct190.ReadPipeAsync.invoke(IOUSBInterfaceStruct190.ReadPipeAsync(getVtable(self)), self, pipeRef, buf, + size, callback, refcon); + } + + // IOReturn (*ReadPipeAsyncTO)(void *self, UInt8 pipeRef, void *buf, UInt32 size, UInt32 noDataTimeout, UInt32 + // completionTimeout, IOAsyncCallback1 callback, void *refcon) + static int ReadPipeAsyncTO(MemorySegment self, byte pipeRef, MemorySegment buf, int size, + int noDataTimeout, int completionTimeout, MemorySegment callback, + MemorySegment refcon) { + return IOUSBInterfaceStruct190.ReadPipeAsyncTO.invoke(IOUSBInterfaceStruct190.ReadPipeAsyncTO(getVtable(self)), self, pipeRef, buf, + size, noDataTimeout, completionTimeout, callback, refcon); + } + + // IOReturn (*WritePipeAsync)(vovoid *self, UInt8 pipeRef, void *buf, UInt32 size, IOAsyncCallback1 callback, + // void *refcon) + static int WritePipeAsync(MemorySegment self, byte pipeRef, MemorySegment buf, int size, + MemorySegment callback, MemorySegment refcon) { + return IOUSBInterfaceStruct190.WritePipeAsync.invoke(IOUSBInterfaceStruct190.WritePipeAsync(getVtable(self)), self, pipeRef, buf, + size, callback, refcon); + } + + // IOReturn (*WritePipeAsyncTO)(void *self, UInt8 pipeRef, void *buf, UInt32 size, UInt32 noDataTimeout, UInt32 + // completionTimeout, IOAsyncCallback1 callback, void *refcon) + static int WritePipeAsyncTO(MemorySegment self, byte pipeRef, MemorySegment buf, int size, + int noDataTimeout, int completionTimeout, MemorySegment callback, + MemorySegment refcon) { + return IOUSBInterfaceStruct190.WritePipeAsyncTO.invoke(IOUSBInterfaceStruct190.WritePipeAsyncTO(getVtable(self)), self, pipeRef, buf, + size, noDataTimeout, completionTimeout, callback, refcon); + } + + // IOReturn (* AbortPipe)(void* self, UInt8 pipeRef) + static int AbortPipe(MemorySegment self, byte pipeRef) { + return IOUSBInterfaceStruct190.AbortPipe.invoke(IOUSBInterfaceStruct190.AbortPipe(getVtable(self)), self, pipeRef); + } + + // IOReturn (*SetAlternateInterface)(void *self, UInt8 alternateSetting) + static int SetAlternateInterface(MemorySegment self, byte alternateSetting) { + return IOUSBInterfaceStruct190.SetAlternateInterface.invoke(IOUSBInterfaceStruct190.SetAlternateInterface(getVtable(self)), self, + alternateSetting); + } + + // IOReturn (* ClearPipeStallBothEnds)(void* self, UInt8 pipeRef) + static int ClearPipeStallBothEnds(MemorySegment self, byte pipeRef) { + return IOUSBInterfaceStruct190.ClearPipeStallBothEnds.invoke(IOUSBInterfaceStruct190.ClearPipeStallBothEnds(getVtable(self)), self, pipeRef); + } + + // CFRunLoopSourceRef (*GetInterfaceAsyncEventSource)(void* self) + static MemorySegment GetInterfaceAsyncEventSource(MemorySegment self) { + return IOUSBInterfaceStruct190.GetInterfaceAsyncEventSource.invoke(IOUSBInterfaceStruct190.GetInterfaceAsyncEventSource(getVtable(self)), self); + } + + // IOReturn (*CreateInterfaceAsyncEventSource)(void *self, CFRunLoopSourceRef *source) + static int CreateInterfaceAsyncEventSource(MemorySegment self, MemorySegment source) { + return IOUSBInterfaceStruct190.CreateInterfaceAsyncEventSource.invoke(IOUSBInterfaceStruct190.CreateInterfaceAsyncEventSource(getVtable(self)), self + , source); + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosAsyncTask.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosAsyncTask.java new file mode 100644 index 00000000..b4b71b62 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosAsyncTask.java @@ -0,0 +1,282 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.macos; + +import net.codecrete.usb.UsbException; +import net.codecrete.usb.macos.gen.corefoundation.CFMessagePortCreateLocal$callout; +import net.codecrete.usb.macos.gen.corefoundation.CoreFoundation; +import net.codecrete.usb.macos.gen.iokit.IOKit; + +import java.lang.foreign.Arena; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemorySegment; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +import static java.lang.System.Logger.Level.ERROR; +import static java.lang.System.Logger.Level.WARNING; +import static java.lang.foreign.MemorySegment.NULL; +import static java.lang.foreign.ValueLayout.ADDRESS; +import static java.lang.foreign.ValueLayout.JAVA_INT; +import static java.lang.foreign.ValueLayout.JAVA_LONG; +import static java.lang.foreign.ValueLayout.JAVA_LONG_UNALIGNED; + + +/** + * Background task for handling asynchronous transfers. + *

+ * Each USB device and interface must register its event source with this task. + *

+ *

+ * The task assigns a consecutive number to transfers. It is used in the + * {@code refcon} argument to match callbacks with the submitted transfer. + *

+ */ +@SuppressWarnings("java:S6548") +class MacosAsyncTask { + + enum TaskState { + NOT_STARTED, + STARTING, + RUNNING + } + + /** + * Singleton instance of background task. + */ + static final MacosAsyncTask INSTANCE = new MacosAsyncTask(); + + private static final System.Logger LOG = System.getLogger(MacosAsyncTask.class.getName()); + + private final ReentrantLock asyncIoLock = new ReentrantLock(); + private final Condition asyncIoReady = asyncIoLock.newCondition(); + private TaskState state = TaskState.NOT_STARTED; + private MemorySegment asyncIoRunLoop; + private MemorySegment completionUpcallStub; + private MemorySegment messagePort; + private long lastTransferId; + private final Map transfersById = new HashMap<>(); + + /** + * Adds an event source to this background. + * + * @param source event source + */ + void addEventSource(MemorySegment source) { + try { + asyncIoLock.lock(); + + if (state != TaskState.RUNNING) { + if (state == TaskState.NOT_STARTED) + startAsyncIOThread(); + waitForRunLoopReady(); + } + + CoreFoundation.CFRunLoopAddSource(asyncIoRunLoop, source, IOKit.kCFRunLoopDefaultMode()); + + } finally { + asyncIoLock.unlock(); + } + } + + private void waitForRunLoopReady() { + while (state != TaskState.RUNNING) + asyncIoReady.awaitUninterruptibly(); + } + + /** + * Removes an event source from this background task. + *

+ * The event source is not immediately removed. Instead, it is posted to a message queue + * processed by the same background thread processing the completion callbacks. This ensures + * that the events from releasing interfaces and closing devices are processed. + *

+ * @param source event source + */ + void removeEventSource(MemorySegment source) { + try (var arena = Arena.ofConfined()) { + var eventSourceRef = arena.allocate(JAVA_LONG, 1); + eventSourceRef.set(JAVA_LONG, 0, source.address()); + var dataRef = CoreFoundation.CFDataCreate(NULL, eventSourceRef, eventSourceRef.byteSize()); + CoreFoundation.CFMessagePortSendRequest(messagePort, 0, dataRef, 0, 0, NULL, NULL); + CoreFoundation.CFRelease(dataRef); + } + } + + /** + * Starts the background thread. + */ + @SuppressWarnings("java:S125") + private void startAsyncIOThread() { + MemorySegment messagePortSource = NULL; + MemorySegment localPort = NULL; + + try { + state = TaskState.STARTING; + + // create descriptor for completion callback function + var completionHandlerFuncDesc = FunctionDescriptor.ofVoid(ADDRESS, JAVA_INT, ADDRESS); + var asyncIOCompletedMH = MethodHandles.lookup().findVirtual(MacosAsyncTask.class, "asyncIOCompleted", + MethodType.methodType(void.class, MemorySegment.class, int.class, MemorySegment.class)); + var methodHandle = asyncIOCompletedMH.bindTo(this); + completionUpcallStub = Linker.nativeLinker().upcallStub(methodHandle, completionHandlerFuncDesc, Arena.global()); + + // create local and remote message ports (all three CF creations can return NULL, + // e.g. if bootstrap port registration fails in a sandboxed process) + var pid = ProcessHandle.current().pid(); + var portName = CoreFoundationHelper.createCFStringRef("net.codecrete.usb.macos.eventsource." + pid, Arena.global()); + var messagePortCallback = CFMessagePortCreateLocal$callout.allocate(this::messagePortCallback, Arena.global()); + localPort = CoreFoundation.CFMessagePortCreateLocal(NULL, portName, messagePortCallback, NULL, NULL); + if (localPort.address() == 0) + throw new UsbException("internal error (CFMessagePortCreateLocal failed)"); + messagePortSource = CoreFoundation.CFMessagePortCreateRunLoopSource(NULL, localPort, 0); + if (messagePortSource.address() == 0) + throw new UsbException("internal error (CFMessagePortCreateRunLoopSource failed)"); + var remotePort = CoreFoundation.CFMessagePortCreateRemote(NULL, portName); + if (remotePort.address() == 0) + throw new UsbException("internal error (CFMessagePortCreateRemote failed)"); + messagePort = remotePort; + + } catch (Exception e) { + // release partially created ports and reset the state; otherwise the task would be + // stuck in STARTING and all later addEventSource() calls would wait forever + if (messagePortSource.address() != 0) + CoreFoundation.CFRelease(messagePortSource); + if (localPort.address() != 0) + CoreFoundation.CFRelease(localPort); + state = TaskState.NOT_STARTED; + if (e instanceof RuntimeException runtimeException) + throw runtimeException; + throw new UsbException("internal error (creating method handle)", e); + } + + var source = messagePortSource; + var thread = new Thread(() -> asyncIOCompletionTask(source), "USB async IO"); + thread.setDaemon(true); + thread.start(); + } + + /** + * Background task calling the completion handlers. + *

+ * Without an initial event source, the run loop will immediately exit. + * Later it has no problems if the number of event sources drops to 0. + *

+ * + * @param firstSource first event source + */ + private void asyncIOCompletionTask(MemorySegment firstSource) { + try { + asyncIoLock.lock(); + asyncIoRunLoop = CoreFoundation.CFRunLoopGetCurrent(); + CoreFoundation.CFRunLoopAddSource(asyncIoRunLoop, firstSource, IOKit.kCFRunLoopDefaultMode()); + state = TaskState.RUNNING; + asyncIoReady.signalAll(); + } finally { + asyncIoLock.unlock(); + } + + // loop forever + CoreFoundation.CFRunLoopRun(); + LOG.log(WARNING, "unexpected end of CFRunLoopRun"); + } + + /** + * Prepare a transfer for submission by assigning it an ID + * and remembering the association to the transfer. + *

+ * Each submission needs to be prepared separately. + *

+ * + * @param transfer transfer + */ + synchronized void prepareForSubmission(MacosTransfer transfer) { + lastTransferId += 1; + transfer.setId(lastTransferId); + transfer.setResultSize(-1); + transfersById.put(lastTransferId, transfer); + } + + /** + * Undoes the registration performed by {@link #prepareForSubmission(MacosTransfer)}. + *

+ * Must be called if the native submission of a prepared transfer fails. In that case, + * no completion callback will ever fire for the transfer, so its map entry would leak + * unless it is removed here. + *

+ * + * @param transfer transfer whose submission failed + */ + synchronized void submissionFailed(MacosTransfer transfer) { + transfersById.remove(transfer.id()); + } + + /** + * Callback function called when an asynchronous transfer has completed. + * + * @param refcon contains transfer ID + * @param result contains result code + * @param arg0 contains actual length of transferred data + */ + @SuppressWarnings("java:S1144") + private void asyncIOCompleted(MemorySegment refcon, int result, MemorySegment arg0) { + + try { + MacosTransfer transfer; + synchronized (this) { + transfer = transfersById.remove(refcon.address()); + } + + if (transfer == null) { + // A completion for an unknown transfer ID (e.g. a duplicate or spurious + // callback). Ignore it rather than dereferencing null. + LOG.log(WARNING, "Ignoring async IO completion for unknown transfer ID {0}", refcon.address()); + return; + } + + transfer.setResultCode(result); + transfer.setResultSize((int) arg0.address()); + transfer.completion().completed(transfer); + + } catch (Exception e) { + // This method is a native upcall running on the process-wide async IO thread. + // Any exception escaping into CFRunLoopRun() would kill that thread and hang + // all async transfers for the entire library, so nothing must escape here. + LOG.log(ERROR, "Unexpected exception while handling async IO completion", e); + } + } + + /** + * Callback function called when a message is received on the message port. + *

+ * All messages are related to removing event sources. They just contain the run loop source reference. + *

+ */ + @SuppressWarnings({"java:S1144", "unused"}) + private MemorySegment messagePortCallback(MemorySegment local, int msgid, MemorySegment data, MemorySegment info) { + var runloopSourceRefPtr = CoreFoundation.CFDataGetBytePtr(data); + var runloopSourceRef = MemorySegment.ofAddress(runloopSourceRefPtr.get(JAVA_LONG_UNALIGNED, 0)); + CoreFoundation.CFRunLoopRemoveSource(asyncIoRunLoop, runloopSourceRef, IOKit.kCFRunLoopDefaultMode()); + return NULL; + } + + /** + * Gets the native IO completion callback function for asynchronous transfers + * to be handled by this background task. + * + * @return function pointer + */ + MemorySegment nativeCompletionCallback() { + return completionUpcallStub; + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosEndpointInputStream.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosEndpointInputStream.java new file mode 100644 index 00000000..06dff333 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosEndpointInputStream.java @@ -0,0 +1,23 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.macos; + +import net.codecrete.usb.common.EndpointInputStream; +import net.codecrete.usb.common.Transfer; + +public class MacosEndpointInputStream extends EndpointInputStream { + + MacosEndpointInputStream(MacosUsbDevice device, int endpointNumber, int bufferSize) { + super(device, endpointNumber, bufferSize); + } + + @Override + protected void submitTransferIn(Transfer transfer) { + ((MacosUsbDevice) device).submitTransferIn(endpointNumber, (MacosTransfer) transfer, 0); + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosEndpointOutputStream.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosEndpointOutputStream.java new file mode 100644 index 00000000..898ab5e1 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosEndpointOutputStream.java @@ -0,0 +1,23 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.macos; + +import net.codecrete.usb.common.EndpointOutputStream; +import net.codecrete.usb.common.Transfer; + +public class MacosEndpointOutputStream extends EndpointOutputStream { + + MacosEndpointOutputStream(MacosUsbDevice device, int endpointNumber, int bufferSize) { + super(device, endpointNumber, bufferSize); + } + + @Override + protected void submitTransferOut(Transfer request) { + ((MacosUsbDevice) device).submitTransferOut(endpointNumber, (MacosTransfer) request, 0); + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosTransfer.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosTransfer.java new file mode 100644 index 00000000..6610234d --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosTransfer.java @@ -0,0 +1,22 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.macos; + +import net.codecrete.usb.common.Transfer; + +class MacosTransfer extends Transfer { + private long id; + + public long id() { + return id; + } + + public void setId(long id) { + this.id = id; + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosUSBDevice.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosUSBDevice.java deleted file mode 100644 index af2b69fb..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosUSBDevice.java +++ /dev/null @@ -1,373 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// - -package net.codecrete.usb.macos; - -import net.codecrete.usb.USBControlTransfer; -import net.codecrete.usb.USBDirection; -import net.codecrete.usb.USBException; -import net.codecrete.usb.USBTransferType; -import net.codecrete.usb.common.DescriptorParser; -import net.codecrete.usb.common.USBDescriptors; -import net.codecrete.usb.common.USBDeviceImpl; -import net.codecrete.usb.macos.gen.iokit.IOKit; -import net.codecrete.usb.macos.gen.iokit.IOUSBDevRequest; -import net.codecrete.usb.macos.gen.iokit.IOUSBFindInterfaceRequest; - -import java.lang.foreign.MemoryAddress; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static java.lang.foreign.MemoryAddress.ofLong; -import static java.lang.foreign.ValueLayout.*; - -public class MacosUSBDevice extends USBDeviceImpl { - - private final MemoryAddress device; - private int configurationValue; - private List claimedInterfaces; - private Map endpoints; - - MacosUSBDevice(MemoryAddress device, Object id, int vendorId, int productId, String manufacturer, String product, String serial) { - super(id, vendorId, productId, manufacturer, product, serial); - this.device = device; - - loadDescription(); - - IoKitUSB.AddRef(device); - } - - @Override - public boolean isOpen() { - return claimedInterfaces != null; - } - - @Override - public void open() { - if (isOpen()) - throw new USBException("the device is already open"); - - // open device - int ret = IoKitUSB.USBDeviceOpen(device); - if (ret != 0) - throw new MacosUSBException("unable to open USB device", ret); - - // set configuration - ret = IoKitUSB.SetConfiguration(device, (byte) configurationValue); - if (ret != 0) - throw new MacosUSBException("failed to set configuration", ret); - - claimedInterfaces = new ArrayList<>(); - updateEndpointList(); - } - - @Override - public void close() { - if (!isOpen()) - return; - - for (InterfaceInfo interfaceInfo : claimedInterfaces) { - IoKitUSB.USBInterfaceClose(interfaceInfo.asAddress()); - IoKitUSB.Release(interfaceInfo.asAddress()); - setClaimed(interfaceInfo.interfaceNumber, false); - } - - claimedInterfaces = null; - endpoints = null; - IoKitUSB.USBDeviceClose(device); - } - - void closeFully() { - close(); - IoKitUSB.Release(device); - } - - private void loadDescription() { - try (var session = MemorySession.openConfined()) { - - try { - // retrieve information of first configuration - var descPtrHolder = session.allocate(ADDRESS); - int ret = IoKitUSB.GetConfigurationDescriptorPtr(device, (byte) 0, descPtrHolder.address()); - if (ret != 0) - throw new MacosUSBException("failed to query first configuration", ret); - - // get value of first configuration - var configDescHeader = MemorySegment.ofAddress(descPtrHolder.get(ADDRESS, 0), - USBDescriptors.Configuration.byteSize(), session); - int totalLength = (short) USBDescriptors.Configuration_wTotalLength.get(configDescHeader); - var configDesc = MemorySegment.ofAddress(descPtrHolder.get(ADDRESS, 0), - totalLength, session); - - var configuration = DescriptorParser.parseConfigurationDescriptor(configDesc, vendorId(), productId()); - - configurationValue = 255 & configuration.configValue; - setInterfaces(configuration.interfaces); - - } catch (Throwable e) { - configurationValue = 0; - throw e; - } - } - } - - private InterfaceInfo findInterface(int interfaceNumber) { - - try (var outerSession = MemorySession.openConfined()) { - var request = outerSession.allocate(IOUSBFindInterfaceRequest.$LAYOUT()); - IOUSBFindInterfaceRequest.bInterfaceClass$set(request, (short) IOKit.kIOUSBFindInterfaceDontCare()); - IOUSBFindInterfaceRequest.bInterfaceSubClass$set(request, (short) IOKit.kIOUSBFindInterfaceDontCare()); - IOUSBFindInterfaceRequest.bInterfaceProtocol$set(request, (short) IOKit.kIOUSBFindInterfaceDontCare()); - IOUSBFindInterfaceRequest.bAlternateSetting$set(request, (short) IOKit.kIOUSBFindInterfaceDontCare()); - - var iterHolder = outerSession.allocate(JAVA_INT); - int ret = IoKitUSB.CreateInterfaceIterator(device, request.address(), iterHolder.address()); - final var iter = iterHolder.get(JAVA_INT, 0); - if (ret != 0) throw new RuntimeException("CreateInterfaceIterator failed"); - outerSession.addCloseAction(() -> IOKit.IOObjectRelease(iter)); - - int service; - while ((service = IOKit.IOIteratorNext(iter)) != 0) { - try (var session = MemorySession.openConfined()) { - - final int service_final = service; - session.addCloseAction(() -> IOKit.IOObjectRelease(service_final)); - - final MemoryAddress intf = IoKitHelper.getInterface(service, - IoKitHelper.kIOUSBInterfaceUserClientTypeID, IoKitHelper.kIOUSBInterfaceInterfaceID100); - if (intf == null) continue; - - var intfNumberHolder = session.allocate(JAVA_INT); - IoKitUSB.GetInterfaceNumber(intf, intfNumberHolder.address()); - if (intfNumberHolder.get(JAVA_INT, 0) != interfaceNumber) { - IoKitUSB.Release(intf); - continue; - } - - return new InterfaceInfo(intf.toRawLongValue(), interfaceNumber); - } - } - } - - throw new MacosUSBException(String.format("Invalid interface number: %d", interfaceNumber)); - } - - public void claimInterface(int interfaceNumber) { - checkIsOpen(); - - var interfaceInfo = findInterface(interfaceNumber); - - try { - var ret = IoKitUSB.USBInterfaceOpen(interfaceInfo.asAddress()); - if (ret != 0) - throw new MacosUSBException("Failed to claim interface", ret); - setClaimed(interfaceNumber, true); - - } catch (Throwable t) { - IoKitUSB.Release(interfaceInfo.asAddress()); - throw t; - } - - claimedInterfaces.add(interfaceInfo); - - updateEndpointList(); - } - - public void releaseInterface(int interfaceNumber) { - checkIsOpen(); - - var interfaceInfoOptional = - claimedInterfaces.stream().filter(info -> info.interfaceNumber == interfaceNumber).findFirst(); - if (interfaceInfoOptional.isEmpty()) - throw new MacosUSBException(String.format("Invalid interface number: %d", interfaceNumber)); - - var interfaceInfo = interfaceInfoOptional.get(); - - int ret = IoKitUSB.USBInterfaceClose(interfaceInfo.asAddress()); - if (ret != 0) throw new MacosUSBException("Failed to release interface", ret); - - claimedInterfaces.remove(interfaceInfo); - IoKitUSB.Release(interfaceInfo.asAddress()); - setClaimed(interfaceNumber, false); - - updateEndpointList(); - } - - /** - * Update the map of active endpoints. - *

- * MacOS uses a pipe index to refer to endpoints. This method - * builds a map from endpoint address to pipe index. - *

- */ - private void updateEndpointList() { - endpoints = new HashMap<>(); - - for (InterfaceInfo interfaceInfo : claimedInterfaces) { - try (var session = MemorySession.openConfined()) { - - var intf = interfaceInfo.asAddress(); - var numEndpointsHolder = session.allocate(JAVA_BYTE); - int ret = IoKitUSB.GetNumEndpoints(intf, numEndpointsHolder.address()); - if (ret != 0) - throw new MacosUSBException("Failed to get number of endpoints", ret); - int numEndpoints = numEndpointsHolder.get(JAVA_BYTE, 0) & 255; - - for (int pipeIndex = 1; pipeIndex <= numEndpoints; pipeIndex++) { - - var directionHolder = session.allocate(JAVA_BYTE); - var numberHolder = session.allocate(JAVA_BYTE); - var transferTypeHolder = session.allocate(JAVA_BYTE); - var maxPacketSizeHolder = session.allocate(JAVA_SHORT); - var intervalHolder = session.allocate(JAVA_BYTE); - - ret = IoKitUSB.GetPipeProperties(intf, (byte) pipeIndex, directionHolder.address(), - numberHolder.address(), transferTypeHolder.address(), maxPacketSizeHolder.address(), - intervalHolder.address()); - if (ret != 0) throw new MacosUSBException("Failed to get pipe properties", ret); - - int endpointNumber = numberHolder.get(JAVA_BYTE, 0) & 255; - int direction = directionHolder.get(JAVA_BYTE, 0) & 255; - byte endpointAddress = (byte) (endpointNumber | (direction << 7)); - byte transferType = transferTypeHolder.get(JAVA_BYTE, 0); - var endpointInfo = new EndpointInfo(interfaceInfo.addr, (byte) pipeIndex, getTransferType(transferType)); - endpoints.put(endpointAddress, endpointInfo); - } - } - } - } - - private EndpointInfo getEndpointInfo(int endpointNumber, USBDirection direction, - USBTransferType transferType1, USBTransferType transferType2) { - if (endpoints != null) { - byte endpointAddress = (byte) (endpointNumber | (direction == USBDirection.IN ? 0x80 : 0)); - var endpointInfo = endpoints.get(endpointAddress); - if (endpointInfo != null - && (endpointInfo.transferType == transferType1 || endpointInfo.transferType == transferType2)) - return endpointInfo; - } - - String transferTypeDesc; - if (transferType2 == null) - transferTypeDesc = transferType1.name(); - else - transferTypeDesc = String.format("%s or %s", transferType1.name(), transferType2.name()); - throw new USBException(String.format("Endpoint number %d does not exist, is not part of a claimed interface " + - "or is not valid for %s transfer in %s direction", endpointNumber, transferTypeDesc, - direction.name())); - } - - private static MemorySegment createDeviceRequest(MemorySession session, USBDirection direction, - USBControlTransfer setup, MemorySegment data) { - var deviceRequest = session.allocate(IOUSBDevRequest.$LAYOUT()); - var bmRequestType = - (direction == USBDirection.IN ? 0x80 : 0x00) | (setup.requestType().ordinal() << 5) | setup.recipient().ordinal(); - IOUSBDevRequest.bmRequestType$set(deviceRequest, (byte) bmRequestType); - IOUSBDevRequest.bRequest$set(deviceRequest, setup.request()); - IOUSBDevRequest.wValue$set(deviceRequest, setup.value()); - IOUSBDevRequest.wIndex$set(deviceRequest, setup.index()); - IOUSBDevRequest.wLength$set(deviceRequest, (short) data.byteSize()); - IOUSBDevRequest.pData$set(deviceRequest, data.address()); - return deviceRequest; - } - - @Override - public byte[] controlTransferIn(USBControlTransfer setup, int length) { - checkIsOpen(); - - try (var session = MemorySession.openConfined()) { - var data = session.allocate(length); - var deviceRequest = createDeviceRequest(session, USBDirection.IN, setup, data); - - int ret = IoKitUSB.DeviceRequest(device, deviceRequest.address()); - if (ret != 0) throw new MacosUSBException("Control IN transfer failed", ret); - - int lenDone = IOUSBDevRequest.wLenDone$get(deviceRequest); - return data.asSlice(0, lenDone).toArray(JAVA_BYTE); - } - } - - @Override - public void controlTransferOut(USBControlTransfer setup, byte[] data) { - checkIsOpen(); - - try (var session = MemorySession.openConfined()) { - int dataLength = data != null ? data.length : 0; - var dataSegment = session.allocate(dataLength); - if (dataLength > 0) dataSegment.copyFrom(MemorySegment.ofArray(data)); - var deviceRequest = createDeviceRequest(session, USBDirection.OUT, setup, dataSegment); - - int ret = IoKitUSB.DeviceRequest(device, deviceRequest.address()); - if (ret != 0) throw new MacosUSBException("Control IN transfer failed", ret); - } - } - - @Override - public void transferOut(int endpointNumber, byte[] data) { - - var endpointInfo = getEndpointInfo(endpointNumber, USBDirection.OUT, - USBTransferType.BULK, USBTransferType.INTERRUPT); - - try (var session = MemorySession.openConfined()) { - var nativeData = session.allocateArray(JAVA_BYTE, data.length); - nativeData.copyFrom(MemorySegment.ofArray(data)); - int ret = IoKitUSB.WritePipe(endpointInfo.interfacAddress(), endpointInfo.pipeIndex, - nativeData.address(), data.length); - if (ret != 0) - throw new MacosUSBException(String.format("Sending data to endpoint %d failed", endpointNumber), ret); - } - } - - @Override - public byte[] transferIn(int endpointNumber, int maxLength) { - - var endpointInfo = getEndpointInfo(endpointNumber, USBDirection.IN, - USBTransferType.BULK, USBTransferType.INTERRUPT); - - try (var session = MemorySession.openConfined()) { - var nativeData = session.allocateArray(JAVA_BYTE, maxLength); - var sizeHolder = session.allocate(JAVA_INT, maxLength); - int ret = IoKitUSB.ReadPipe(endpointInfo.interfacAddress(), endpointInfo.pipeIndex, - nativeData.address(), sizeHolder.address()); - if (ret != 0) - throw new MacosUSBException(String.format("Receiving data from endpoint %d failed", endpointNumber), - ret); - - int size = sizeHolder.get(JAVA_INT, 0); - var result = new byte[size]; - var resultSegment = MemorySegment.ofArray(result); - resultSegment.copyFrom(nativeData.asSlice(0, size)); - - return result; - } - } - - private static USBTransferType getTransferType(byte macosTransferType) { - return switch (macosTransferType) { - case 1 -> USBTransferType.ISOCHRONOUS; - case 2 -> USBTransferType.BULK; - case 3 -> USBTransferType.INTERRUPT; - default -> null; - }; - } - - record InterfaceInfo(long addr, int interfaceNumber) { - MemoryAddress asAddress() { - return ofLong(addr); - } - } - - record EndpointInfo(long interfaceAddr, byte pipeIndex, USBTransferType transferType) { - MemoryAddress interfacAddress() { - return ofLong(interfaceAddr); - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosUSBDeviceRegistry.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosUSBDeviceRegistry.java deleted file mode 100644 index dc8b5d75..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosUSBDeviceRegistry.java +++ /dev/null @@ -1,247 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// - -package net.codecrete.usb.macos; - -import net.codecrete.usb.USBDevice; -import net.codecrete.usb.common.USBDeviceRegistry; -import net.codecrete.usb.macos.gen.corefoundation.CoreFoundation; -import net.codecrete.usb.macos.gen.iokit.IOKit; - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.util.ArrayList; -import java.util.function.Consumer; - -import static java.lang.foreign.MemoryAddress.NULL; -import static java.lang.foreign.ValueLayout.*; - -/** - * MacOS implementation of USB device registry. - */ -public class MacosUSBDeviceRegistry extends USBDeviceRegistry { - - /** - * Monitors the USB devices. - *

- * This method is the core of the background thread. It runs forever and does not terminate. - *

- */ - @Override - protected void monitorDevices() { - - // as the method runs forever, there is no need to clean up one-time allocations - try (var session = MemorySession.openConfined()) { - - try { - - // setup run loop, run loop source and notification port - var notifyPort = IOKit.IONotificationPortCreate(IOKit.kIOMasterPortDefault$get()); - var runLoopSource = IOKit.IONotificationPortGetRunLoopSource(notifyPort); - var runLoop = CoreFoundation.CFRunLoopGetCurrent(); - CoreFoundation.CFRunLoopAddSource(runLoop, runLoopSource, IOKit.kCFRunLoopDefaultMode$get()); - - // setup notification for connected devices - var onDeviceConnectedMH = MethodHandles.lookup().findVirtual(MacosUSBDeviceRegistry.class, - "onDevicesConnected", MethodType.methodType(void.class, MemoryAddress.class, int.class)); - int deviceConnectedIter = setupNotification(session, notifyPort, IOKit.kIOFirstMatchNotification(), - onDeviceConnectedMH); - - // iterate current devices in order to arm the notifications (and build initial device list) - var deviceList = new ArrayList(); - iterateDevices(deviceConnectedIter, (device) -> deviceList.add(device)); - setInitialDeviceList(deviceList); - - // setup notification for disconnected devices - var onDeviceDisconnectedMH = MethodHandles.lookup().findVirtual(MacosUSBDeviceRegistry.class, - "onDevicesDisconnected", MethodType.methodType(void.class, MemoryAddress.class, int.class)); - int deviceDisconnectedIter = setupNotification(session, notifyPort, IOKit.kIOTerminatedNotification(), - onDeviceDisconnectedMH); - - // iterate current devices in order to arm the notifications - onDevicesDisconnected(NULL, deviceDisconnectedIter); - - } catch (Throwable e) { - enumerationFailed(e); - return; - } - - // loop forever - CoreFoundation.CFRunLoopRun(); - } - } - - /** - * Process the devices resulting from the iterator - * - * @param iterator the iterator - * @param consumer a consumer that will be called for each device with the entry ID and the service - */ - private void iterateDevices(int iterator, IOKitDeviceConsumer consumer) { - - int svc; - while ((svc = IOKit.IOIteratorNext(iterator)) != 0) { - try (var session = MemorySession.openConfined()) { - - final int service = svc; - session.addCloseAction(() -> IOKit.IOObjectRelease(service)); - - var device = IoKitHelper.getInterface(service, IoKitHelper.kIOUSBDeviceUserClientTypeID, - IoKitHelper.kIOUSBDeviceInterfaceID100); - - if (device != null) - session.addCloseAction(() -> IoKitUSB.Release(device)); - - // get entry ID (as unique ID) - var entryIdHolder = session.allocate(JAVA_LONG); - int ret = IOKit.IORegistryEntryGetRegistryEntryID(service, entryIdHolder); - if (ret != 0) - throw new MacosUSBException("IORegistryEntryGetRegistryEntryID failed", ret); - var entryId = entryIdHolder.get(JAVA_LONG, 0); - - // call consumer to process device - consumer.accept(entryId, service, device); - } - } - } - - /** - * Calls the consumer for all devices produced by the iterator. - *

- * This method tries to create a {@link USBDevice} instance. - * If it fails, an information is printed, but the consumer is not called. - *

- * - * @param iterator the iterator - * @param consumer the consumer - */ - private void iterateDevices(int iterator, Consumer consumer) { - iterateDevices(iterator, (entryId, service, deviceIntf) -> { - - var deviceInfo = new VidPid(); - try { - var device = createDevice(entryId, service, deviceIntf, deviceInfo); - if (device != null) - consumer.accept(device); - - } catch (Throwable e) { - System.err.printf("Info: [JavaDoesUSB] failed to retrieve information about device 0x%04x/0x%04x - " + "ignoring device%n", deviceInfo.vid, deviceInfo.pid); - e.printStackTrace(System.err); - } - }); - } - - private USBDevice createDevice(Long entryID, int service, MemoryAddress deviceIntf, VidPid info) { - - if (deviceIntf == null) - return null; - - Integer vendorId = IoKitHelper.getPropertyInt(service, "idVendor"); - Integer productId = IoKitHelper.getPropertyInt(service, "idProduct"); - if (vendorId == null || productId == null) - return null; - - info.vid = vendorId; - info.pid = productId; - - String manufacturer = IoKitHelper.getPropertyString(service, "kUSBVendorString"); - String product = IoKitHelper.getPropertyString(service, "kUSBProductString"); - String serial = IoKitHelper.getPropertyString(service, "kUSBSerialNumberString"); - - var device = new MacosUSBDevice(deviceIntf, entryID, vendorId, productId, manufacturer, product, serial); - - Integer classCode = IoKitHelper.getPropertyInt(service, "bDeviceClass"); - Integer subclassCode = IoKitHelper.getPropertyInt(service, "bDeviceSubClass"); - Integer protocolCode = IoKitHelper.getPropertyInt(service, "bDeviceProtocol"); - - device.setClassCodes(classCode != null ? classCode : 0, subclassCode != null ? subclassCode : 0, - protocolCode != null ? protocolCode : 0); - - Integer usbVersion = IoKitHelper.getPropertyInt(service, "bcdUSB"); - Integer deviceVersion = IoKitHelper.getPropertyInt(service, "bcdDevice"); - //noinspection ConstantConditions - device.setVersions(usbVersion, deviceVersion); - - return device; - } - - private int setupNotification(MemorySession session, MemoryAddress notifyPort, MemorySegment notificationType, - MethodHandle callback) { - - // new matching dictionary for (dis)connected device notifications - MemoryAddress matchingDict = IOKit.IOServiceMatching(IOKit.kIOUSBDeviceClassName()); - - // create callback stub - var onDeviceCallbackStub = Linker.nativeLinker().upcallStub(callback.bindTo(this), - FunctionDescriptor.ofVoid(ADDRESS, JAVA_INT), session); - - // Set up a notification to be called when a device is first matched / terminated by I/O Kit. - // This method consumes the matchingDict reference. - var deviceIterHolder = session.allocate(JAVA_INT); - int ret = IOKit.IOServiceAddMatchingNotification(notifyPort, notificationType, matchingDict, - onDeviceCallbackStub, NULL, deviceIterHolder); - if (ret != 0) - throw new MacosUSBException("IOServiceAddMatchingNotification failed", ret); - - return deviceIterHolder.get(JAVA_INT, 0); - } - - /** - * Callback function for monitoring connected USB devices. - *

- * This method is used in an upcall from native code. - *

- * - * @param ignoredRefCon ignored parameter - * @param iterator device iterator - */ - private void onDevicesConnected(MemoryAddress ignoredRefCon, int iterator) { - - // process device iterator for connected devices - iterateDevices(iterator, this::addDevice); - } - - /** - * Callback function for monitoring disconnected USB devices. - *

- * This method is used in an upcall from native code. - *

- * - * @param ignoredRefCon ignored parameter - * @param iterator device iterator - */ - private void onDevicesDisconnected(MemoryAddress ignoredRefCon, int iterator) { - - // process device iterator for disconnected devices - iterateDevices(iterator, (entryId, service, deviceIntf) -> { - var device = findDevice(entryId); - if (device == null) - return; - - try { - ((MacosUSBDevice) device).closeFully(); - } catch (Throwable e) { - System.err.println("Info: [JavaDoesUSB] failed to close USB device - ignoring exception"); - e.printStackTrace(System.err); - } - - removeDevice(entryId); - }); - } - - @FunctionalInterface - interface IOKitDeviceConsumer { - void accept(long entryId, int service, MemoryAddress deviceIntf); - } - - static class VidPid { - int vid; - int pid; - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosUSBException.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosUSBException.java deleted file mode 100644 index 3d59df8d..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosUSBException.java +++ /dev/null @@ -1,29 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// -package net.codecrete.usb.macos; - -import net.codecrete.usb.USBException; -import net.codecrete.usb.macos.gen.mach.mach; - -public class MacosUSBException extends USBException { - public MacosUSBException(String message) { - super(message); - } - - public MacosUSBException(String message, int errorCode) { - super(String.format("%s - %s", message, machErrorMessage(errorCode)), errorCode); - } - - public MacosUSBException(String message, Throwable cause) { - super(message, cause); - } - - private static String machErrorMessage(int errorCode) { - var msg = mach.mach_error_string(errorCode); - return msg.getUtf8String(0); - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosUsbDevice.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosUsbDevice.java new file mode 100644 index 00000000..57065c7c --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosUsbDevice.java @@ -0,0 +1,678 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.macos; + +import net.codecrete.usb.UsbControlTransfer; +import net.codecrete.usb.UsbDevice; +import net.codecrete.usb.UsbDirection; +import net.codecrete.usb.UsbRecipient; +import net.codecrete.usb.UsbRequestType; +import net.codecrete.usb.UsbTransferType; +import net.codecrete.usb.common.ScopeCleanup; +import net.codecrete.usb.common.Transfer; +import net.codecrete.usb.common.UsbDeviceImpl; +import net.codecrete.usb.macos.gen.iokit.IOKit; +import net.codecrete.usb.macos.gen.iokit.IOUSBDevRequest; +import net.codecrete.usb.macos.gen.iokit.IOUSBFindInterfaceRequest; +import net.codecrete.usb.usbstandard.ConfigurationDescriptor; +import net.codecrete.usb.usbstandard.Constants; +import org.jetbrains.annotations.NotNull; + +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static java.lang.foreign.ValueLayout.ADDRESS; +import static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static java.lang.foreign.ValueLayout.JAVA_INT; +import static java.lang.foreign.ValueLayout.JAVA_SHORT; +import static net.codecrete.usb.common.ForeignMemory.dereference; +import static net.codecrete.usb.macos.MacosUsbException.throwException; + +/** + * MacOS implementation of {@link UsbDevice}. + *

+ * All read and write operations on endpoints are submitted through synchronized methods in order to control + * concurrency. If it wasn't controlled, the danger is that device and interface pointers are used, which have + * just been closed and thus deallocated by another thread, likely leading to crashes. + *

+ *

+ * As a consequence of the synchronized submission, blocking operations consists of submitting an + * asynchronous transfer and waiting for the completion. + *

+ */ +@SuppressWarnings({"SynchronizationOnLocalVariableOrMethodParameter", "java:S2160", "java:S3077"}) +public class MacosUsbDevice extends UsbDeviceImpl { + + private final MacosAsyncTask asyncTask; + // Native USB device interface (IOUSBDeviceInterface**) + private MemorySegment device; + // Currently selected configuration + private int configurationValue; + // Details about interfaces that have been claimed + // (volatile: written under the device monitor, read unlocked via isOpened(); + // the list contents are only accessed while holding the device monitor) + private volatile List claimedInterfaces; + // Details about endpoints of current alternate settings (for claimed interfaces) + private Map endpoints; + + private final long discoveryTime; + + MacosUsbDevice(MemorySegment device, Object id, int vendorId, int productId) { + super(id, vendorId, productId); + discoveryTime = System.currentTimeMillis(); + asyncTask = MacosAsyncTask.INSTANCE; + + loadDescription(device); + + this.device = device; + IoKitUsb.AddRef(device); + } + + @Override + public synchronized void detachStandardDrivers() { + checkIsClosed("detachStandardDrivers() must not be called while the device is open"); + var ret = IoKitUsb.USBDeviceReEnumerate(device, IOKit.kUSBReEnumerateCaptureDeviceMask()); + if (ret != 0) + throwException(ret, "detaching standard drivers failed"); + } + + @Override + public synchronized void attachStandardDrivers() { + checkIsClosed("attachStandardDrivers() must not be called while the device is open"); + var ret = IoKitUsb.USBDeviceReEnumerate(device, IOKit.kUSBReEnumerateReleaseDeviceMask()); + if (ret != 0) + throwException(ret, "attaching standard drivers failed"); + } + + @Override + public boolean isOpened() { + return claimedInterfaces != null; + } + + @SuppressWarnings({"java:S2276", "java:S2142"}) + @Override + public synchronized void open() { + checkIsClosed("device is already open"); + + // open device (several retries if device has just been connected/discovered) + var duration = System.currentTimeMillis() - discoveryTime; + var numTries = duration < 1000 ? 4 : 1; + var ret = 0; + // Defer interruption: keep a local flag instead of re-asserting the interrupt + // (which would make the remaining backoff sleeps throw immediately and defeat + // the retry delay). Re-assert once the retries are done. + var wasInterrupted = false; + while (numTries > 0) { + numTries -= 1; + ret = IoKitUsb.USBDeviceOpenSeize(device); + if (ret != IOKit.kIOReturnExclusiveAccess()) + break; + + // sleep and retry + try { + Thread.sleep(90); + } catch (InterruptedException _) { + wasInterrupted = true; + } + } + if (wasInterrupted) + Thread.currentThread().interrupt(); + if (ret != 0) + throwException(ret, "opening USB device failed"); + + claimedInterfaces = new ArrayList<>(); + addDeviceEventSource(); + + // set configuration + ret = IoKitUsb.SetConfiguration(device, (byte) configurationValue); + if (ret != 0) + throwException(ret, "setting configuration failed"); + + updateEndpointList(); + } + + @Override + public synchronized void close() { + if (!isOpened()) + return; + + for (var interfaceInfo : claimedInterfaces) { + setClaimed(interfaceInfo.interfaceNumber, false); + var source = IoKitUsb.GetInterfaceAsyncEventSource(interfaceInfo.iokitInterface()); + IoKitUsb.USBInterfaceClose(interfaceInfo.iokitInterface); + IoKitUsb.Release(interfaceInfo.iokitInterface); + if (source.address() != 0) + asyncTask.removeEventSource(source); + } + + claimedInterfaces = null; + endpoints = null; + + var source = IoKitUsb.GetDeviceAsyncEventSource(device); + IoKitUsb.USBDeviceClose(device); + if (source.address() != 0) + asyncTask.removeEventSource(source); + } + + @Override + protected synchronized void disconnect() { + super.disconnect(); + IoKitUsb.Release(device); + device = null; + } + + private void loadDescription(MemorySegment device) { + try (var arena = Arena.ofConfined()) { + + // retrieve device descriptor using synchronous control transfer + var data = arena.allocate(255); + var deviceRequest = createDeviceRequest(arena, UsbDirection.IN, new UsbControlTransfer( + UsbRequestType.STANDARD, + UsbRecipient.DEVICE, + 6, // get descriptor + Constants.DEVICE_DESCRIPTOR_TYPE << 8, + 0 + ), data); + var ret = IoKitUsb.DeviceRequest(device, deviceRequest); + if (ret != 0) + throwException(ret, "querying device descriptor failed"); + + var len = IOUSBDevRequest.wLenDone(deviceRequest); + rawDeviceDescriptor = data.asSlice(0, len).toArray(JAVA_BYTE); + + configurationValue = 0; + + // retrieve information of first configuration + var descPtrHolder = arena.allocate(ADDRESS); + ret = IoKitUsb.GetConfigurationDescriptorPtr(device, (byte) 0, descPtrHolder); + if (ret != 0) + throwException(ret, "querying first configuration failed"); + + // read the descriptor header with a minimally sized view first, then resize to the + // total length the header reports (the kernel buffer was sized from the same field) + var headerSize = ConfigurationDescriptor.LAYOUT.byteSize(); + var configDescHeader = new ConfigurationDescriptor(dereference(descPtrHolder).reinterpret(headerSize)); + var totalLength = configDescHeader.totalLength(); + if (totalLength < headerSize) + throwException("invalid configuration descriptor (wTotalLength: %d)", totalLength); + var configDesc = dereference(descPtrHolder).reinterpret(totalLength); + + var configuration = setConfigurationDescriptor(configDesc); + configurationValue = 255 & configuration.configValue(); + } + } + + @SuppressWarnings("java:S135") + private InterfaceInfo findInterfaceInfo(int interfaceNumber) { + + try (var arena = Arena.ofConfined(); var outerCleanup = new ScopeCleanup()) { + var request = IOUSBFindInterfaceRequest.allocate(arena); + IOUSBFindInterfaceRequest.bInterfaceClass(request, (short) IOKit.kIOUSBFindInterfaceDontCare()); + IOUSBFindInterfaceRequest.bInterfaceSubClass(request, (short) IOKit.kIOUSBFindInterfaceDontCare()); + IOUSBFindInterfaceRequest.bInterfaceProtocol(request, (short) IOKit.kIOUSBFindInterfaceDontCare()); + IOUSBFindInterfaceRequest.bAlternateSetting(request, (short) IOKit.kIOUSBFindInterfaceDontCare()); + + var iterHolder = arena.allocate(JAVA_INT); + var ret = IoKitUsb.CreateInterfaceIterator(device, request, iterHolder); + if (ret != 0) + throwException("internal error (CreateInterfaceIterator)"); + + final var iter = iterHolder.get(JAVA_INT, 0); + outerCleanup.add(() -> IOKit.IOObjectRelease(iter)); + + var intfNumberHolder = arena.allocate(JAVA_INT); + + int service; + while ((service = IOKit.IOIteratorNext(iter)) != 0) { + try (var cleanup = new ScopeCleanup()) { + + final var service_final = service; + cleanup.add(() -> IOKit.IOObjectRelease(service_final)); + + final var intf = IoKitHelper.getInterface(service, IoKitHelper.kIOUSBInterfaceUserClientTypeID, + IoKitHelper.kIOUSBInterfaceInterfaceID190); + if (intf == null) + continue; + + cleanup.add(() -> IoKitUsb.Release(intf)); + + IoKitUsb.GetInterfaceNumber(intf, intfNumberHolder); + if (intfNumberHolder.get(JAVA_INT, 0) != interfaceNumber) + continue; + + IoKitUsb.AddRef(intf); + return new InterfaceInfo(intf, interfaceNumber); + } + } + } + + throwException("invalid interface number: %d", interfaceNumber); + throw new AssertionError("not reached"); + } + + public synchronized void claimInterface(int interfaceNumber) { + checkIsOpen(); + getInterfaceWithCheck(interfaceNumber, false); + + try (var cleanup = new ScopeCleanup()) { + + var interfaceInfo = findInterfaceInfo(interfaceNumber); + cleanup.add(() -> IoKitUsb.Release(interfaceInfo.iokitInterface())); + + var ret = IoKitUsb.USBInterfaceOpen(interfaceInfo.iokitInterface()); + if (ret != 0) + throwException(ret, "claiming interface failed"); + + IoKitUsb.AddRef(interfaceInfo.iokitInterface()); + claimedInterfaces.add(interfaceInfo); + setClaimed(interfaceNumber, true); + addInterfaceEventSource(interfaceInfo); + } + + updateEndpointList(); + } + + @SuppressWarnings({"OptionalGetWithoutIsPresent", "java:S3655"}) + public synchronized void selectAlternateSetting(int interfaceNumber, int alternateNumber) { + // check interface + var intf = getInterfaceWithCheck(interfaceNumber, true); + + // check alternate setting + var altSetting = intf.getAlternate(alternateNumber); + var intfInfo = + claimedInterfaces.stream().filter(interf -> interf.interfaceNumber() == interfaceNumber).findFirst().get(); + + var ret = IoKitUsb.SetAlternateInterface(intfInfo.iokitInterface(), (byte) alternateNumber); + if (ret != 0) + throwException(ret, "setting alternate interface failed"); + + intf.setAlternate(altSetting); + updateEndpointList(); + } + + @SuppressWarnings({"OptionalGetWithoutIsPresent", "java:S3655"}) + public synchronized void releaseInterface(int interfaceNumber) { + checkIsOpen(); + getInterfaceWithCheck(interfaceNumber, true); + + @SuppressWarnings("OptionalGetWithoutIsPresent") + var interfaceInfo = + claimedInterfaces.stream().filter(info -> info.interfaceNumber == interfaceNumber).findFirst().get(); + + var source = IoKitUsb.GetInterfaceAsyncEventSource(interfaceInfo.iokitInterface()); + if (source.address() != 0) + asyncTask.removeEventSource(source); + + var ret = IoKitUsb.USBInterfaceClose(interfaceInfo.iokitInterface()); + if (ret != 0) + throwException(ret, "releasing interface failed"); + + claimedInterfaces.remove(interfaceInfo); + IoKitUsb.Release(interfaceInfo.iokitInterface()); + setClaimed(interfaceNumber, false); + + updateEndpointList(); + } + + /** + * Update the map of active endpoints. + *

+ * MacOS uses a pipe index to refer to endpoints. This method + * builds a map from endpoint address to pipe index. + *

+ */ + private void updateEndpointList() { + endpoints = new HashMap<>(); + + try (var arena = Arena.ofConfined()) { + + var directionHolder = arena.allocate(JAVA_BYTE); + var numberHolder = arena.allocate(JAVA_BYTE); + var transferTypeHolder = arena.allocate(JAVA_BYTE); + var maxPacketSizeHolder = arena.allocate(JAVA_SHORT); + var intervalHolder = arena.allocate(JAVA_BYTE); + + for (var interfaceInfo : claimedInterfaces) { + + var intf = interfaceInfo.iokitInterface(); + var numEndpointsHolder = arena.allocate(JAVA_BYTE); + var ret = IoKitUsb.GetNumEndpoints(intf, numEndpointsHolder); + if (ret != 0) + throwException(ret, "internal error (GetNumEndpoints)"); + var numEndpoints = numEndpointsHolder.get(JAVA_BYTE, 0) & 255; + + for (var pipeIndex = 1; pipeIndex <= numEndpoints; pipeIndex++) { + + ret = IoKitUsb.GetPipeProperties(intf, (byte) pipeIndex, directionHolder, numberHolder, + transferTypeHolder, maxPacketSizeHolder, intervalHolder); + if (ret != 0) + throwException(ret, "internal error (GetPipeProperties)"); + + var endpointNumber = numberHolder.get(JAVA_BYTE, 0) & 0xff; + var direction = directionHolder.get(JAVA_BYTE, 0) & 0xff; + var endpointAddress = (byte) (endpointNumber | (direction << 7)); + var transferType = transferTypeHolder.get(JAVA_BYTE, 0); + var maxPacketSize = maxPacketSizeHolder.get(JAVA_SHORT, 0) & 0xffff; + var endpointInfo = new EndpointInfo(interfaceInfo.iokitInterface(), (byte) pipeIndex, + getTransferType(transferType), maxPacketSize); + endpoints.put(endpointAddress, endpointInfo); + } + } + } + } + + @SuppressWarnings("SameParameterValue") + private synchronized EndpointInfo getEndpointInfo(int endpointNumber, UsbDirection direction, + UsbTransferType transferType1, UsbTransferType transferType2) { + if (endpoints != null) { + var endpointAddress = (byte) (endpointNumber | (direction == UsbDirection.IN ? 0x80 : 0)); + var endpointInfo = endpoints.get(endpointAddress); + if (endpointInfo != null && (endpointInfo.transferType == transferType1 || endpointInfo.transferType == transferType2)) + return endpointInfo; + } + + String transferTypeDesc; + if (transferType2 == null) + transferTypeDesc = transferType1.name(); + else + transferTypeDesc = String.format("%s or %s", transferType1.name(), transferType2.name()); + + throwException( + "endpoint number %d does not exist, is not part of a claimed interface or is not valid for %s transfer in %s direction", + endpointNumber, transferTypeDesc, direction.name()); + throw new AssertionError("not reached"); + } + + private static MemorySegment createDeviceRequest(Arena arena, UsbDirection direction, UsbControlTransfer setup, + MemorySegment data) { + var deviceRequest = IOUSBDevRequest.allocate(arena); + var bmRequestType = + (direction == UsbDirection.IN ? 0x80 : 0x00) | (setup.requestType().ordinal() << 5) | setup.recipient().ordinal(); + IOUSBDevRequest.bmRequestType(deviceRequest, (byte) bmRequestType); + IOUSBDevRequest.bRequest(deviceRequest, (byte) setup.request()); + IOUSBDevRequest.wValue(deviceRequest, (short) setup.value()); + IOUSBDevRequest.wIndex(deviceRequest, (short) setup.index()); + IOUSBDevRequest.wLength(deviceRequest, (short) data.byteSize()); + IOUSBDevRequest.pData(deviceRequest, data); + return deviceRequest; + } + + @Override + public byte @NotNull [] controlTransferIn(@NotNull UsbControlTransfer setup, int length) { + try (var arena = Arena.ofConfined()) { + var data = arena.allocate(length); + var deviceRequest = createDeviceRequest(arena, UsbDirection.IN, setup, data); + + var transfer = new MacosTransfer(); + transfer.setCompletion(UsbDeviceImpl::onSyncTransferCompleted); + + synchronized (transfer) { + submitControlTransfer(deviceRequest, transfer); + waitForTransfer(transfer, 0, UsbDirection.IN, 0); + } + + return data.asSlice(0, transfer.resultSize()).toArray(JAVA_BYTE); + } + } + + @Override + public void controlTransferOut(@NotNull UsbControlTransfer setup, byte[] data) { + try (var arena = Arena.ofConfined()) { + var dataLength = data != null ? data.length : 0; + var dataSegment = arena.allocate(dataLength); + if (dataLength > 0) + dataSegment.copyFrom(MemorySegment.ofArray(data)); + var deviceRequest = createDeviceRequest(arena, UsbDirection.OUT, setup, dataSegment); + + var transfer = new MacosTransfer(); + transfer.setCompletion(UsbDeviceImpl::onSyncTransferCompleted); + + synchronized (transfer) { + submitControlTransfer(deviceRequest, transfer); + waitForTransfer(transfer, 0, UsbDirection.OUT, 0); + } + } + } + + @Override + public void transferOut(int endpointNumber, byte @NotNull [] data, int offset, int length, int timeout) { + + var epInfo = getEndpointInfo(endpointNumber, UsbDirection.OUT, UsbTransferType.BULK, + UsbTransferType.INTERRUPT); + + // Auto arena: a transfer that times out may be abandoned (see UsbDeviceImpl.waitForTransfer), + // so the buffer must outlive a possible late completion instead of being freed deterministically. + var arena = Arena.ofAuto(); + var nativeData = arena.allocate(JAVA_BYTE, length); + nativeData.copyFrom(MemorySegment.ofArray(data).asSlice(offset, length)); + + var transfer = new MacosTransfer(); + transfer.setData(nativeData); + transfer.setDataSize(length); + transfer.setCompletion(UsbDeviceImpl::onSyncTransferCompleted); + + synchronized (transfer) { + if (timeout <= 0 || epInfo.transferType() == UsbTransferType.BULK) { + // no timeout or timeout handled by operating system + submitTransferOut(endpointNumber, transfer, timeout); + waitForTransfer(transfer, 0, UsbDirection.OUT, endpointNumber); + + } else { + // interrupt transfer with timeout + submitTransferOut(endpointNumber, transfer, 0); + waitForTransfer(transfer, timeout, UsbDirection.OUT, endpointNumber); + } + } + } + + @Override + public byte @NotNull [] transferIn(int endpointNumber, int timeout) { + + var epInfo = getEndpointInfo(endpointNumber, UsbDirection.IN, UsbTransferType.BULK, + UsbTransferType.INTERRUPT); + + // Auto arena: a transfer that times out may be abandoned (see UsbDeviceImpl.waitForTransfer), + // so the buffer must outlive a possible late completion instead of being freed deterministically. + var arena = Arena.ofAuto(); + var nativeData = arena.allocate(JAVA_BYTE, epInfo.packetSize()); + + var transfer = new MacosTransfer(); + transfer.setData(nativeData); + transfer.setDataSize(epInfo.packetSize()); + transfer.setCompletion(UsbDeviceImpl::onSyncTransferCompleted); + + synchronized (transfer) { + if (timeout <= 0 || epInfo.transferType() == UsbTransferType.BULK) { + // no timeout, or timeout handled by operating system + submitTransferIn(endpointNumber, transfer, timeout); + waitForTransfer(transfer, 0, UsbDirection.IN, endpointNumber); + + } else { + // interrupt transfer with timeout + submitTransferIn(endpointNumber, transfer, 0); + waitForTransfer(transfer, timeout, UsbDirection.IN, endpointNumber); + } + } + + return nativeData.asSlice(0, transfer.resultSize()).toArray(JAVA_BYTE); + } + + /** + * Submits a transfer IN to the specified BULK or INTERRUPT endpoint. + *

+ * A timeout may only be specified for BULK endpoints. + *

+ * + * @param endpointNumber endpoint number + * @param transfer transfer to execute + * @param timeout the timeout, in milliseconds, or 0 for no timeout + */ + synchronized void submitTransferIn(int endpointNumber, MacosTransfer transfer, int timeout) { + + var epInfo = getEndpointInfo(endpointNumber, UsbDirection.IN, UsbTransferType.BULK, + UsbTransferType.INTERRUPT); + asyncTask.prepareForSubmission(transfer); + + // submit transfer + int ret; + if (timeout <= 0) + ret = IoKitUsb.ReadPipeAsync(epInfo.iokitInterface(), epInfo.pipeIndex(), transfer.data(), + transfer.dataSize(), asyncTask.nativeCompletionCallback(), MemorySegment.ofAddress(transfer.id())); + else + ret = IoKitUsb.ReadPipeAsyncTO(epInfo.iokitInterface(), epInfo.pipeIndex(), transfer.data(), + transfer.dataSize(), timeout, timeout, asyncTask.nativeCompletionCallback(), + MemorySegment.ofAddress(transfer.id())); + + if (ret != 0) { + asyncTask.submissionFailed(transfer); + throwException(ret, "error occurred while reading from endpoint %d", endpointNumber); + } + } + + /** + * Submits a transfer OUT to the specified BULK or INTERRUPT endpoint. + *

+ * A timeout may only be specified for BULK endpoints. + *

+ * + * @param endpointNumber endpoint number + * @param transfer transfer request to execute + * @param timeout the timeout, in milliseconds, or 0 for no timeout + */ + synchronized void submitTransferOut(int endpointNumber, MacosTransfer transfer, int timeout) { + + var epInfo = getEndpointInfo(endpointNumber, UsbDirection.OUT, UsbTransferType.BULK, + UsbTransferType.INTERRUPT); + asyncTask.prepareForSubmission(transfer); + + // submit transfer + int ret; + if (timeout <= 0) + ret = IoKitUsb.WritePipeAsync(epInfo.iokitInterface(), epInfo.pipeIndex(), transfer.data(), + transfer.dataSize(), asyncTask.nativeCompletionCallback(), MemorySegment.ofAddress(transfer.id())); + else + ret = IoKitUsb.WritePipeAsyncTO(epInfo.iokitInterface(), epInfo.pipeIndex(), transfer.data(), + transfer.dataSize(), timeout, timeout, asyncTask.nativeCompletionCallback(), + MemorySegment.ofAddress(transfer.id())); + + if (ret != 0) { + asyncTask.submissionFailed(transfer); + throwException(ret, "error occurred while transmitting to endpoint %d", endpointNumber); + } + } + + /** + * Submits a control transfer. + * + * @param deviceRequest control transfer request + * @param transfer transfer request (for completion handling) + */ + synchronized void submitControlTransfer(MemorySegment deviceRequest, MacosTransfer transfer) { + + checkIsOpen(); + asyncTask.prepareForSubmission(transfer); + + // submit transfer + var ret = IoKitUsb.DeviceRequestAsync(device, deviceRequest, asyncTask.nativeCompletionCallback(), + MemorySegment.ofAddress(transfer.id())); + + if (ret != 0) { + asyncTask.submissionFailed(transfer); + throwException(ret, "control transfer failed"); + } + } + + @Override + protected Transfer createTransfer() { + return new MacosTransfer(); + } + + @Override + public synchronized void abortTransfers(UsbDirection direction, int endpointNumber) { + var epInfo = getEndpointInfo(endpointNumber, direction, UsbTransferType.BULK, + UsbTransferType.INTERRUPT); + + var ret = IoKitUsb.AbortPipe(epInfo.iokitInterface(), epInfo.pipeIndex()); + if (ret != 0) + throwException(ret, "aborting transfers failed"); + } + + @Override + public synchronized void clearHalt(UsbDirection direction, int endpointNumber) { + var epInfo = getEndpointInfo(endpointNumber, direction, UsbTransferType.BULK, + UsbTransferType.INTERRUPT); + + var ret = IoKitUsb.ClearPipeStallBothEnds(epInfo.iokitInterface(), epInfo.pipeIndex()); + if (ret != 0) + throwException(ret, "clearing halt condition failed"); + } + + @Override + public synchronized @NotNull InputStream openInputStream(int endpointNumber, int bufferSize) { + // check that endpoint number is valid + getEndpointInfo(endpointNumber, UsbDirection.IN, UsbTransferType.BULK, null); + + return new MacosEndpointInputStream(this, endpointNumber, bufferSize); + } + + @Override + public synchronized @NotNull OutputStream openOutputStream(int endpointNumber, int bufferSize) { + // check that endpoint number is valid + getEndpointInfo(endpointNumber, UsbDirection.OUT, UsbTransferType.BULK, null); + + return new MacosEndpointOutputStream(this, endpointNumber, bufferSize); + } + + @Override + protected void throwOSException(int errorCode, String message, Object... args) { + throwException(errorCode, message, args); + } + + private static UsbTransferType getTransferType(byte macosTransferType) { + return switch (macosTransferType) { + case 1 -> UsbTransferType.ISOCHRONOUS; + case 2 -> UsbTransferType.BULK; + case 3 -> UsbTransferType.INTERRUPT; + default -> null; + }; + } + + private synchronized void addDeviceEventSource() { + try (var innerArena = Arena.ofConfined()) { + var sourceHolder = innerArena.allocate(ADDRESS); + var ret = IoKitUsb.CreateDeviceAsyncEventSource(device, sourceHolder); + if (ret != 0) + throwException(ret, "internal error (CreateDeviceAsyncEventSource)"); + var source = dereference(sourceHolder); + asyncTask.addEventSource(source); + } + } + + private synchronized void addInterfaceEventSource(InterfaceInfo interfaceInfo) { + try (var innerArena = Arena.ofConfined()) { + var sourceHolder = innerArena.allocate(ADDRESS); + var ret = IoKitUsb.CreateInterfaceAsyncEventSource(interfaceInfo.iokitInterface(), sourceHolder); + if (ret != 0) + throwException(ret, "internal error (CreateInterfaceAsyncEventSource)"); + var source = dereference(sourceHolder); + asyncTask.addEventSource(source); + } + } + + record InterfaceInfo(MemorySegment iokitInterface, int interfaceNumber) { + } + + record EndpointInfo(MemorySegment iokitInterface, byte pipeIndex, UsbTransferType transferType, int packetSize) { + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosUsbDeviceRegistry.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosUsbDeviceRegistry.java new file mode 100644 index 00000000..b2126246 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosUsbDeviceRegistry.java @@ -0,0 +1,274 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.macos; + +import net.codecrete.usb.UsbDevice; +import net.codecrete.usb.common.ScopeCleanup; +import net.codecrete.usb.common.UsbDeviceRegistry; +import net.codecrete.usb.macos.gen.corefoundation.CoreFoundation; +import net.codecrete.usb.macos.gen.iokit.IOKit; +import net.codecrete.usb.macos.gen.iokit.IOServiceAddMatchingNotification$callback; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SegmentAllocator; +import java.util.ArrayList; +import java.util.function.Consumer; + +import static java.lang.System.Logger.Level.INFO; +import static java.lang.System.Logger.Level.WARNING; +import static java.lang.foreign.MemorySegment.NULL; +import static java.lang.foreign.ValueLayout.JAVA_INT; +import static java.lang.foreign.ValueLayout.JAVA_LONG; +import static net.codecrete.usb.macos.CoreFoundationHelper.createCFStringRef; +import static net.codecrete.usb.macos.MacosUsbException.throwException; + +/** + * MacOS implementation of USB device registry. + */ +@SuppressWarnings("java:S116") +public class MacosUsbDeviceRegistry extends UsbDeviceRegistry { + + private static final System.Logger LOG = System.getLogger(MacosUsbDeviceRegistry.class.getName()); + + private static final MemorySegment KEY_ID_VENDOR; + private static final MemorySegment KEY_ID_PRODUCT; + private static final MemorySegment KEY_VENDOR; + private static final MemorySegment KEY_PRODUCT; + private static final MemorySegment KEY_SERIAL_NUM; + private static final MemorySegment KEY_DEVICE_CLASS; + private static final MemorySegment KEY_DEVICE_SUBCLASS; + private static final MemorySegment KEY_DEVICE_PROTOCOL; + private static final MemorySegment KEY_USB_BCD; + private static final MemorySegment KEY_DEVICE_BCD; + + static { + SegmentAllocator global = Arena.global(); + KEY_ID_VENDOR = createCFStringRef("idVendor", global); + KEY_ID_PRODUCT = createCFStringRef("idProduct", global); + KEY_VENDOR = createCFStringRef("kUSBVendorString", global); + KEY_PRODUCT = createCFStringRef("kUSBProductString", global); + KEY_SERIAL_NUM = createCFStringRef("kUSBSerialNumberString", global); + KEY_DEVICE_CLASS = createCFStringRef("bDeviceClass", global); + KEY_DEVICE_SUBCLASS = createCFStringRef("bDeviceSubClass", global); + KEY_DEVICE_PROTOCOL = createCFStringRef("bDeviceProtocol", global); + KEY_USB_BCD = createCFStringRef("bcdUSB", global); + KEY_DEVICE_BCD = createCFStringRef("bcdDevice", global); + } + + /** + * Monitors the USB devices. + *

+ * This method is the core of the background thread. It runs forever and does not terminate. + *

+ */ + @Override + protected void monitorDevices() { + + // as the method runs forever, there is no need to clean up one-time allocations + try (var arena = Arena.ofConfined()) { + + try { + + // setup run loop, run loop source and notification port + var notifyPort = IOKit.IONotificationPortCreate(IOKit.kIOMasterPortDefault()); + var runLoopSource = IOKit.IONotificationPortGetRunLoopSource(notifyPort); + var runLoop = CoreFoundation.CFRunLoopGetCurrent(); + CoreFoundation.CFRunLoopAddSource(runLoop, runLoopSource, IOKit.kCFRunLoopDefaultMode()); + + // setup notification for connected devices + var deviceConnectedIter = setupNotification(arena, notifyPort, IOKit.kIOFirstMatchNotification(), + this::onDevicesConnected); + + // iterate current devices in order to arm the notifications (and build initial device list) + var deviceList = new ArrayList(); + iterateDevices(deviceConnectedIter, device -> deviceList.add(device)); // NOSONAR + setInitialDeviceList(deviceList); + + // setup notification for disconnected devices + var deviceDisconnectedIter = setupNotification(arena, notifyPort, IOKit.kIOTerminatedNotification(), + this::onDevicesDisconnected); + + // iterate current devices in order to arm the notifications + onDevicesDisconnected(NULL, deviceDisconnectedIter); + + } catch (Exception e) { + enumerationFailed(e); + return; + } + + // loop forever + CoreFoundation.CFRunLoopRun(); + LOG.log(WARNING, "unexpected end of CFRunLoopRun"); + } + } + + /** + * Process the devices resulting from the iterator + * + * @param iterator the iterator + * @param consumer a consumer that will be called for each device with the entry ID and the service + */ + private void iterateDevices(int iterator, IOKitDeviceConsumer consumer) { + + try (var arena = Arena.ofConfined()) { + var entryIdHolder = arena.allocate(JAVA_LONG); + + int svc; + while ((svc = IOKit.IOIteratorNext(iterator)) != 0) { + try (var cleanup = new ScopeCleanup()) { + + final var service = svc; + cleanup.add(() -> IOKit.IOObjectRelease(service)); + + var device = IoKitHelper.getInterface(service, IoKitHelper.kIOUSBDeviceUserClientTypeID, + IoKitHelper.kIOUSBDeviceInterfaceID187); + if (device != null) + cleanup.add(() -> IoKitUsb.Release(device)); + + // get entry ID (as unique ID) + var ret = IOKit.IORegistryEntryGetRegistryEntryID(service, entryIdHolder); + if (ret != 0) + throwException(ret, "internal error (IORegistryEntryGetRegistryEntryID)"); + var entryId = entryIdHolder.get(JAVA_LONG, 0); + + // call consumer to process device + consumer.accept(entryId, service, device); + } + } + } + } + + /** + * Calls the consumer for all devices produced by the iterator. + *

+ * This method tries to create a {@link UsbDevice} instance. + * If it fails, an information is printed, but the consumer is not called. + *

+ * + * @param iterator the iterator + * @param consumer the consumer + */ + @SuppressWarnings("java:S106") + private void iterateDevices(int iterator, Consumer consumer) { + iterateDevices(iterator, (entryId, service, deviceIntf) -> { + + var deviceInfo = new VidPid(); + try { + var device = createDevice(entryId, service, deviceIntf, deviceInfo); + if (device != null) + consumer.accept(device); + + } catch (Exception e) { + LOG.log(INFO, String.format("failed to retrieve information about device 0x%04x/0x%04x - ignoring device", + deviceInfo.vid, deviceInfo.pid), e); + } + }); + } + + private UsbDevice createDevice(Long entryID, int service, MemorySegment deviceIntf, VidPid info) { + + if (deviceIntf == null) + return null; + + try (var arena = Arena.ofConfined()) { + + var vendorId = IoKitHelper.getPropertyInt(service, KEY_ID_VENDOR, arena); + var productId = IoKitHelper.getPropertyInt(service, KEY_ID_PRODUCT, arena); + if (vendorId == null || productId == null) + return null; + + info.vid = vendorId; + info.pid = productId; + + var device = new MacosUsbDevice(deviceIntf, entryID, vendorId, productId); + + var manufacturer = IoKitHelper.getPropertyString(service, KEY_VENDOR, arena); + var product = IoKitHelper.getPropertyString(service, KEY_PRODUCT, arena); + var serial = IoKitHelper.getPropertyString(service, KEY_SERIAL_NUM, arena); + + device.setProductStrings(manufacturer, product, serial); + + var classCode = IoKitHelper.getPropertyInt(service, KEY_DEVICE_CLASS, arena); + var subclassCode = IoKitHelper.getPropertyInt(service, KEY_DEVICE_SUBCLASS, arena); + var protocolCode = IoKitHelper.getPropertyInt(service, KEY_DEVICE_PROTOCOL, arena); + + device.setClassCodes(classCode != null ? classCode : 0, subclassCode != null ? subclassCode : 0, + protocolCode != null ? protocolCode : 0); + + var usbVersion = IoKitHelper.getPropertyInt(service, KEY_USB_BCD, arena); + var deviceVersion = IoKitHelper.getPropertyInt(service, KEY_DEVICE_BCD, arena); + //noinspection DataFlowIssue + device.setVersions(usbVersion, deviceVersion != null ? deviceVersion : 0); + + return device; + } + } + + private int setupNotification(Arena arena, MemorySegment notifyPort, MemorySegment notificationType, + IOServiceAddMatchingNotification$callback.Function callback) { + + // new matching dictionary for (dis)connected device notifications (NOSONAR) + var matchingDict = IOKit.IOServiceMatching(IOKit.kIOUSBDeviceClassName()); + + // create callback stub + var onDeviceCallbackStub = IOServiceAddMatchingNotification$callback.allocate(callback, Arena.global()); + + // Set up a notification to be called when a device is first matched / terminated by I/O Kit. + // This method consumes the matchingDict reference. + var deviceIterHolder = arena.allocate(JAVA_INT); + var ret = IOKit.IOServiceAddMatchingNotification(notifyPort, notificationType, matchingDict, + onDeviceCallbackStub, NULL, deviceIterHolder); + if (ret != 0) + throwException(ret, "internal error (IOServiceAddMatchingNotification)"); + + return deviceIterHolder.get(JAVA_INT, 0); + } + + /** + * Callback function for monitoring connected USB devices. + *

+ * This method is used in an upcall from native code. + *

+ * + * @param ignoredRefCon ignored parameter + * @param iterator device iterator + */ + @SuppressWarnings("java:S1172") + private void onDevicesConnected(MemorySegment ignoredRefCon, int iterator) { + + // process device iterator for connected devices + iterateDevices(iterator, this::addDevice); + } + + /** + * Callback function for monitoring disconnected USB devices. + *

+ * This method is used in an upcall from native code. + *

+ * + * @param ignoredRefCon ignored parameter + * @param iterator device iterator + */ + @SuppressWarnings({"SameParameterValue", "java:S1172", "java:S106"}) + private void onDevicesDisconnected(MemorySegment ignoredRefCon, int iterator) { + + // process device iterator for disconnected devices + iterateDevices(iterator, (entryId, _, _) -> closeAndRemoveDevice(entryId)); + } + + @FunctionalInterface + interface IOKitDeviceConsumer { + void accept(long entryId, int service, MemorySegment deviceIntf); + } + + static class VidPid { + int vid; + int pid; + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosUsbException.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosUsbException.java new file mode 100644 index 00000000..8731c61f --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/MacosUsbException.java @@ -0,0 +1,69 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +package net.codecrete.usb.macos; + +import net.codecrete.usb.UsbException; +import net.codecrete.usb.UsbStallException; +import net.codecrete.usb.UsbTimeoutException; +import net.codecrete.usb.macos.gen.iokit.IOKit; +import net.codecrete.usb.macos.gen.mach.mach; + +/** + * Exception thrown if a macOS specific error occurs. + */ +public class MacosUsbException extends UsbException { + + /** + * Creates a new instance. + *

+ * The message for the macOS error code is looked up and appended to the message. + *

+ * + * @param message exception message + * @param errorCode macOS error code (usually returned by macOS functions) + */ + public MacosUsbException(String message, int errorCode) { + super(String.format("%s: %s", message, machErrorMessage(errorCode)), errorCode); + } + + private static String machErrorMessage(int errorCode) { + var msg = mach.mach_error_string(errorCode); + return msg.getString(0); + } + + /** + * Throws an exception for the specified macOS error code. + *

+ * The message for the macOS error code is looked up and appended to the message. + *

+ * + * @param errorCode macOS error code (usually returned by macOS functions) + * @param message exception message format ({@link String#format(String, Object...)} style) + * @param args arguments for exception message + */ + static void throwException(int errorCode, String message, Object... args) { + var formattedMessage = String.format(message, args); + if (errorCode == IOKit.kIOUSBPipeStalled()) { + throw new UsbStallException(formattedMessage); + } else if (errorCode == IOKit.kIOUSBTransactionTimeout()) { + throw new UsbTimeoutException(formattedMessage); + } else { + throw new MacosUsbException(formattedMessage, errorCode); + } + } + + /** + * Throws a USB exception. + * + * @param message exception message format ({@link String#format(String, Object...)} style) + * @param args arguments for exception message + */ + static void throwException(String message, Object... args) { + throw new UsbException(String.format(message, args)); + } + +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/UUID.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/UUID.java index cccaa04c..5aef8e98 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/UUID.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/UUID.java @@ -9,28 +9,32 @@ import net.codecrete.usb.macos.gen.corefoundation.CoreFoundation; -import java.lang.foreign.MemoryAddress; -import java.lang.foreign.MemorySession; +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; -import static java.lang.foreign.MemoryAddress.NULL; +import static java.lang.foreign.MemorySegment.NULL; /** * Memory layouts and helpers for CFUUID. */ -public class UUID { +class UUID { + + private UUID() { + } /** * Creates a CFUUID struct from a byte array. + * * @param bytes UUID as 16 bytes * @return the CFUUID */ - public static MemoryAddress CreateCFUUID(byte[] bytes) { - try (var session = MemorySession.openConfined()) { - var uuidBytes = session.allocate(16); - uuidBytes.asByteBuffer().put(bytes); + static MemorySegment createCFUUID(byte[] bytes) { + try (var arena = Arena.ofConfined()) { + var uuidBytes = arena.allocate(16); + uuidBytes.copyFrom(MemorySegment.ofArray(bytes)); return CoreFoundation.CFUUIDCreateFromUUIDBytes(NULL, uuidBytes); - } catch (Throwable t) { - throw new RuntimeException(t); + } catch (Exception e) { + throw new AssertionError("internal error (CFUUIDCreateFromUUIDBytes)", e); } } } diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/CFMessagePortCreateLocal$callout.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/CFMessagePortCreateLocal$callout.java new file mode 100644 index 00000000..760f9200 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/CFMessagePortCreateLocal$callout.java @@ -0,0 +1,73 @@ +// Generated by jextract + +package net.codecrete.usb.macos.gen.corefoundation; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +/** + * {@snippet lang=c : + * CFMessagePortCallBack callout + * } + */ +public final class CFMessagePortCreateLocal$callout { + + private CFMessagePortCreateLocal$callout() { + // Should not be called directly + } + + /** + * The function pointer signature, expressed as a functional interface + */ + public interface Function { + MemorySegment apply(MemorySegment _x0, int _x1, MemorySegment _x2, MemorySegment _x3); + } + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + CoreFoundation.C_POINTER, + CoreFoundation.C_POINTER, + CoreFoundation.C_INT, + CoreFoundation.C_POINTER, + CoreFoundation.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + private static final MethodHandle UP$MH = CoreFoundation.upcallHandle(CFMessagePortCreateLocal$callout.Function.class, "apply", $DESC); + + /** + * Allocates a new upcall stub, whose implementation is defined by {@code fi}. + * The lifetime of the returned segment is managed by {@code arena} + */ + public static MemorySegment allocate(CFMessagePortCreateLocal$callout.Function fi, Arena arena) { + return Linker.nativeLinker().upcallStub(UP$MH.bindTo(fi), $DESC, arena); + } + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static MemorySegment invoke(MemorySegment funcPtr, MemorySegment _x0, int _x1, MemorySegment _x2, MemorySegment _x3) { + try { + return (MemorySegment) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2, _x3); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } +} + diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/CFRange.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/CFRange.java index d53e92ea..52df333e 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/CFRange.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/CFRange.java @@ -2,55 +2,172 @@ package net.codecrete.usb.macos.gen.corefoundation; +import java.lang.invoke.*; import java.lang.foreign.*; -import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +/** + * {@snippet lang=c : + * struct { + * CFIndex location; + * CFIndex length; + * } + * } + */ public class CFRange { - static final GroupLayout $struct$LAYOUT = MemoryLayout.structLayout( - Constants$root.C_LONG_LONG$LAYOUT.withName("location"), - Constants$root.C_LONG_LONG$LAYOUT.withName("length") - ); - public static MemoryLayout $LAYOUT() { - return CFRange.$struct$LAYOUT; + CFRange() { + // Should not be called directly } - static final VarHandle location$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("location")); - public static VarHandle location$VH() { - return CFRange.location$VH; + + private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( + CoreFoundation.C_LONG.withName("location"), + CoreFoundation.C_LONG.withName("length") + ).withName("CFRange"); + + /** + * The layout of this struct + */ + public static final GroupLayout layout() { + return $LAYOUT; + } + + private static final OfLong location$LAYOUT = (OfLong)$LAYOUT.select(groupElement("location")); + + /** + * Layout for field: + * {@snippet lang=c : + * CFIndex location + * } + */ + public static final OfLong location$layout() { + return location$LAYOUT; } - public static long location$get(MemorySegment seg) { - return (long)CFRange.location$VH.get(seg); + + private static final long location$OFFSET = $LAYOUT.byteOffset(groupElement("location")); + + /** + * Offset for field: + * {@snippet lang=c : + * CFIndex location + * } + */ + public static final long location$offset() { + return location$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * CFIndex location + * } + */ + public static long location(MemorySegment struct) { + return struct.get(location$LAYOUT, location$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * CFIndex location + * } + */ + public static void location(MemorySegment struct, long fieldValue) { + struct.set(location$LAYOUT, location$OFFSET, fieldValue); } - public static void location$set( MemorySegment seg, long x) { - CFRange.location$VH.set(seg, x); + + private static final OfLong length$LAYOUT = (OfLong)$LAYOUT.select(groupElement("length")); + + /** + * Layout for field: + * {@snippet lang=c : + * CFIndex length + * } + */ + public static final OfLong length$layout() { + return length$LAYOUT; } - public static long location$get(MemorySegment seg, long index) { - return (long)CFRange.location$VH.get(seg.asSlice(index*sizeof())); + + private static final long length$OFFSET = $LAYOUT.byteOffset(groupElement("length")); + + /** + * Offset for field: + * {@snippet lang=c : + * CFIndex length + * } + */ + public static final long length$offset() { + return length$OFFSET; } - public static void location$set(MemorySegment seg, long index, long x) { - CFRange.location$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Getter for field: + * {@snippet lang=c : + * CFIndex length + * } + */ + public static long length(MemorySegment struct) { + return struct.get(length$LAYOUT, length$OFFSET); } - static final VarHandle length$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("length")); - public static VarHandle length$VH() { - return CFRange.length$VH; + + /** + * Setter for field: + * {@snippet lang=c : + * CFIndex length + * } + */ + public static void length(MemorySegment struct, long fieldValue) { + struct.set(length$LAYOUT, length$OFFSET, fieldValue); } - public static long length$get(MemorySegment seg) { - return (long)CFRange.length$VH.get(seg); + + /** + * Obtains a slice of {@code arrayParam} which selects the array element at {@code index}. + * The returned segment has address {@code arrayParam.address() + index * layout().byteSize()} + */ + public static MemorySegment asSlice(MemorySegment array, long index) { + return array.asSlice(layout().byteSize() * index); } - public static void length$set( MemorySegment seg, long x) { - CFRange.length$VH.set(seg, x); + + /** + * The size (in bytes) of this struct + */ + public static long sizeof() { return layout().byteSize(); } + + /** + * Allocate a segment of size {@code layout().byteSize()} using {@code allocator} + */ + public static MemorySegment allocate(SegmentAllocator allocator) { + return allocator.allocate(layout()); } - public static long length$get(MemorySegment seg, long index) { - return (long)CFRange.length$VH.get(seg.asSlice(index*sizeof())); + + /** + * Allocate an array of size {@code elementCount} using {@code allocator}. + * The returned segment has size {@code elementCount * layout().byteSize()}. + */ + public static MemorySegment allocateArray(long elementCount, SegmentAllocator allocator) { + return allocator.allocate(MemoryLayout.sequenceLayout(elementCount, layout())); } - public static void length$set(MemorySegment seg, long index, long x) { - CFRange.length$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, Arena arena, Consumer cleanup) { + return reinterpret(addr, 1, arena, cleanup); } - public static long sizeof() { return $LAYOUT().byteSize(); } - public static MemorySegment allocate(SegmentAllocator allocator) { return allocator.allocate($LAYOUT()); } - public static MemorySegment allocateArray(int len, SegmentAllocator allocator) { - return allocator.allocate(MemoryLayout.sequenceLayout(len, $LAYOUT())); + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code elementCount * layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, long elementCount, Arena arena, Consumer cleanup) { + return addr.reinterpret(layout().byteSize() * elementCount, arena, cleanup); } - public static MemorySegment ofAddress(MemoryAddress addr, MemorySession session) { return RuntimeHelper.asArray(addr, $LAYOUT(), 1, session); } } - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/CFUUIDBytes.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/CFUUIDBytes.java index f69fbaa5..5b52339e 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/CFUUIDBytes.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/CFUUIDBytes.java @@ -2,293 +2,816 @@ package net.codecrete.usb.macos.gen.corefoundation; +import java.lang.invoke.*; import java.lang.foreign.*; -import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +/** + * {@snippet lang=c : + * struct { + * UInt8 byte0; + * UInt8 byte1; + * UInt8 byte2; + * UInt8 byte3; + * UInt8 byte4; + * UInt8 byte5; + * UInt8 byte6; + * UInt8 byte7; + * UInt8 byte8; + * UInt8 byte9; + * UInt8 byte10; + * UInt8 byte11; + * UInt8 byte12; + * UInt8 byte13; + * UInt8 byte14; + * UInt8 byte15; + * } + * } + */ public class CFUUIDBytes { - static final GroupLayout $struct$LAYOUT = MemoryLayout.structLayout( - Constants$root.C_CHAR$LAYOUT.withName("byte0"), - Constants$root.C_CHAR$LAYOUT.withName("byte1"), - Constants$root.C_CHAR$LAYOUT.withName("byte2"), - Constants$root.C_CHAR$LAYOUT.withName("byte3"), - Constants$root.C_CHAR$LAYOUT.withName("byte4"), - Constants$root.C_CHAR$LAYOUT.withName("byte5"), - Constants$root.C_CHAR$LAYOUT.withName("byte6"), - Constants$root.C_CHAR$LAYOUT.withName("byte7"), - Constants$root.C_CHAR$LAYOUT.withName("byte8"), - Constants$root.C_CHAR$LAYOUT.withName("byte9"), - Constants$root.C_CHAR$LAYOUT.withName("byte10"), - Constants$root.C_CHAR$LAYOUT.withName("byte11"), - Constants$root.C_CHAR$LAYOUT.withName("byte12"), - Constants$root.C_CHAR$LAYOUT.withName("byte13"), - Constants$root.C_CHAR$LAYOUT.withName("byte14"), - Constants$root.C_CHAR$LAYOUT.withName("byte15") - ); - public static MemoryLayout $LAYOUT() { - return CFUUIDBytes.$struct$LAYOUT; - } - static final VarHandle byte0$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("byte0")); - public static VarHandle byte0$VH() { - return CFUUIDBytes.byte0$VH; - } - public static byte byte0$get(MemorySegment seg) { - return (byte)CFUUIDBytes.byte0$VH.get(seg); - } - public static void byte0$set( MemorySegment seg, byte x) { - CFUUIDBytes.byte0$VH.set(seg, x); - } - public static byte byte0$get(MemorySegment seg, long index) { - return (byte)CFUUIDBytes.byte0$VH.get(seg.asSlice(index*sizeof())); - } - public static void byte0$set(MemorySegment seg, long index, byte x) { - CFUUIDBytes.byte0$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle byte1$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("byte1")); - public static VarHandle byte1$VH() { - return CFUUIDBytes.byte1$VH; + CFUUIDBytes() { + // Should not be called directly } - public static byte byte1$get(MemorySegment seg) { - return (byte)CFUUIDBytes.byte1$VH.get(seg); - } - public static void byte1$set( MemorySegment seg, byte x) { - CFUUIDBytes.byte1$VH.set(seg, x); - } - public static byte byte1$get(MemorySegment seg, long index) { - return (byte)CFUUIDBytes.byte1$VH.get(seg.asSlice(index*sizeof())); - } - public static void byte1$set(MemorySegment seg, long index, byte x) { - CFUUIDBytes.byte1$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle byte2$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("byte2")); - public static VarHandle byte2$VH() { - return CFUUIDBytes.byte2$VH; - } - public static byte byte2$get(MemorySegment seg) { - return (byte)CFUUIDBytes.byte2$VH.get(seg); + + private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( + CoreFoundation.C_CHAR.withName("byte0"), + CoreFoundation.C_CHAR.withName("byte1"), + CoreFoundation.C_CHAR.withName("byte2"), + CoreFoundation.C_CHAR.withName("byte3"), + CoreFoundation.C_CHAR.withName("byte4"), + CoreFoundation.C_CHAR.withName("byte5"), + CoreFoundation.C_CHAR.withName("byte6"), + CoreFoundation.C_CHAR.withName("byte7"), + CoreFoundation.C_CHAR.withName("byte8"), + CoreFoundation.C_CHAR.withName("byte9"), + CoreFoundation.C_CHAR.withName("byte10"), + CoreFoundation.C_CHAR.withName("byte11"), + CoreFoundation.C_CHAR.withName("byte12"), + CoreFoundation.C_CHAR.withName("byte13"), + CoreFoundation.C_CHAR.withName("byte14"), + CoreFoundation.C_CHAR.withName("byte15") + ).withName("CFUUIDBytes"); + + /** + * The layout of this struct + */ + public static final GroupLayout layout() { + return $LAYOUT; } - public static void byte2$set( MemorySegment seg, byte x) { - CFUUIDBytes.byte2$VH.set(seg, x); + + private static final OfByte byte0$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte0")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte0 + * } + */ + public static final OfByte byte0$layout() { + return byte0$LAYOUT; } - public static byte byte2$get(MemorySegment seg, long index) { - return (byte)CFUUIDBytes.byte2$VH.get(seg.asSlice(index*sizeof())); + + private static final long byte0$OFFSET = $LAYOUT.byteOffset(groupElement("byte0")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte0 + * } + */ + public static final long byte0$offset() { + return byte0$OFFSET; } - public static void byte2$set(MemorySegment seg, long index, byte x) { - CFUUIDBytes.byte2$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte0 + * } + */ + public static byte byte0(MemorySegment struct) { + return struct.get(byte0$LAYOUT, byte0$OFFSET); } - static final VarHandle byte3$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("byte3")); - public static VarHandle byte3$VH() { - return CFUUIDBytes.byte3$VH; + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte0 + * } + */ + public static void byte0(MemorySegment struct, byte fieldValue) { + struct.set(byte0$LAYOUT, byte0$OFFSET, fieldValue); } - public static byte byte3$get(MemorySegment seg) { - return (byte)CFUUIDBytes.byte3$VH.get(seg); + + private static final OfByte byte1$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte1")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte1 + * } + */ + public static final OfByte byte1$layout() { + return byte1$LAYOUT; } - public static void byte3$set( MemorySegment seg, byte x) { - CFUUIDBytes.byte3$VH.set(seg, x); + + private static final long byte1$OFFSET = $LAYOUT.byteOffset(groupElement("byte1")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte1 + * } + */ + public static final long byte1$offset() { + return byte1$OFFSET; } - public static byte byte3$get(MemorySegment seg, long index) { - return (byte)CFUUIDBytes.byte3$VH.get(seg.asSlice(index*sizeof())); + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte1 + * } + */ + public static byte byte1(MemorySegment struct) { + return struct.get(byte1$LAYOUT, byte1$OFFSET); } - public static void byte3$set(MemorySegment seg, long index, byte x) { - CFUUIDBytes.byte3$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte1 + * } + */ + public static void byte1(MemorySegment struct, byte fieldValue) { + struct.set(byte1$LAYOUT, byte1$OFFSET, fieldValue); } - static final VarHandle byte4$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("byte4")); - public static VarHandle byte4$VH() { - return CFUUIDBytes.byte4$VH; + + private static final OfByte byte2$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte2")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte2 + * } + */ + public static final OfByte byte2$layout() { + return byte2$LAYOUT; } - public static byte byte4$get(MemorySegment seg) { - return (byte)CFUUIDBytes.byte4$VH.get(seg); + + private static final long byte2$OFFSET = $LAYOUT.byteOffset(groupElement("byte2")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte2 + * } + */ + public static final long byte2$offset() { + return byte2$OFFSET; } - public static void byte4$set( MemorySegment seg, byte x) { - CFUUIDBytes.byte4$VH.set(seg, x); + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte2 + * } + */ + public static byte byte2(MemorySegment struct) { + return struct.get(byte2$LAYOUT, byte2$OFFSET); } - public static byte byte4$get(MemorySegment seg, long index) { - return (byte)CFUUIDBytes.byte4$VH.get(seg.asSlice(index*sizeof())); + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte2 + * } + */ + public static void byte2(MemorySegment struct, byte fieldValue) { + struct.set(byte2$LAYOUT, byte2$OFFSET, fieldValue); } - public static void byte4$set(MemorySegment seg, long index, byte x) { - CFUUIDBytes.byte4$VH.set(seg.asSlice(index*sizeof()), x); + + private static final OfByte byte3$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte3")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte3 + * } + */ + public static final OfByte byte3$layout() { + return byte3$LAYOUT; } - static final VarHandle byte5$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("byte5")); - public static VarHandle byte5$VH() { - return CFUUIDBytes.byte5$VH; + + private static final long byte3$OFFSET = $LAYOUT.byteOffset(groupElement("byte3")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte3 + * } + */ + public static final long byte3$offset() { + return byte3$OFFSET; } - public static byte byte5$get(MemorySegment seg) { - return (byte)CFUUIDBytes.byte5$VH.get(seg); + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte3 + * } + */ + public static byte byte3(MemorySegment struct) { + return struct.get(byte3$LAYOUT, byte3$OFFSET); } - public static void byte5$set( MemorySegment seg, byte x) { - CFUUIDBytes.byte5$VH.set(seg, x); + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte3 + * } + */ + public static void byte3(MemorySegment struct, byte fieldValue) { + struct.set(byte3$LAYOUT, byte3$OFFSET, fieldValue); } - public static byte byte5$get(MemorySegment seg, long index) { - return (byte)CFUUIDBytes.byte5$VH.get(seg.asSlice(index*sizeof())); + + private static final OfByte byte4$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte4")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte4 + * } + */ + public static final OfByte byte4$layout() { + return byte4$LAYOUT; } - public static void byte5$set(MemorySegment seg, long index, byte x) { - CFUUIDBytes.byte5$VH.set(seg.asSlice(index*sizeof()), x); + + private static final long byte4$OFFSET = $LAYOUT.byteOffset(groupElement("byte4")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte4 + * } + */ + public static final long byte4$offset() { + return byte4$OFFSET; } - static final VarHandle byte6$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("byte6")); - public static VarHandle byte6$VH() { - return CFUUIDBytes.byte6$VH; + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte4 + * } + */ + public static byte byte4(MemorySegment struct) { + return struct.get(byte4$LAYOUT, byte4$OFFSET); } - public static byte byte6$get(MemorySegment seg) { - return (byte)CFUUIDBytes.byte6$VH.get(seg); + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte4 + * } + */ + public static void byte4(MemorySegment struct, byte fieldValue) { + struct.set(byte4$LAYOUT, byte4$OFFSET, fieldValue); } - public static void byte6$set( MemorySegment seg, byte x) { - CFUUIDBytes.byte6$VH.set(seg, x); + + private static final OfByte byte5$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte5")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte5 + * } + */ + public static final OfByte byte5$layout() { + return byte5$LAYOUT; } - public static byte byte6$get(MemorySegment seg, long index) { - return (byte)CFUUIDBytes.byte6$VH.get(seg.asSlice(index*sizeof())); + + private static final long byte5$OFFSET = $LAYOUT.byteOffset(groupElement("byte5")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte5 + * } + */ + public static final long byte5$offset() { + return byte5$OFFSET; } - public static void byte6$set(MemorySegment seg, long index, byte x) { - CFUUIDBytes.byte6$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte5 + * } + */ + public static byte byte5(MemorySegment struct) { + return struct.get(byte5$LAYOUT, byte5$OFFSET); } - static final VarHandle byte7$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("byte7")); - public static VarHandle byte7$VH() { - return CFUUIDBytes.byte7$VH; + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte5 + * } + */ + public static void byte5(MemorySegment struct, byte fieldValue) { + struct.set(byte5$LAYOUT, byte5$OFFSET, fieldValue); } - public static byte byte7$get(MemorySegment seg) { - return (byte)CFUUIDBytes.byte7$VH.get(seg); + + private static final OfByte byte6$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte6")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte6 + * } + */ + public static final OfByte byte6$layout() { + return byte6$LAYOUT; } - public static void byte7$set( MemorySegment seg, byte x) { - CFUUIDBytes.byte7$VH.set(seg, x); + + private static final long byte6$OFFSET = $LAYOUT.byteOffset(groupElement("byte6")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte6 + * } + */ + public static final long byte6$offset() { + return byte6$OFFSET; } - public static byte byte7$get(MemorySegment seg, long index) { - return (byte)CFUUIDBytes.byte7$VH.get(seg.asSlice(index*sizeof())); + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte6 + * } + */ + public static byte byte6(MemorySegment struct) { + return struct.get(byte6$LAYOUT, byte6$OFFSET); } - public static void byte7$set(MemorySegment seg, long index, byte x) { - CFUUIDBytes.byte7$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte6 + * } + */ + public static void byte6(MemorySegment struct, byte fieldValue) { + struct.set(byte6$LAYOUT, byte6$OFFSET, fieldValue); } - static final VarHandle byte8$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("byte8")); - public static VarHandle byte8$VH() { - return CFUUIDBytes.byte8$VH; + + private static final OfByte byte7$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte7")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte7 + * } + */ + public static final OfByte byte7$layout() { + return byte7$LAYOUT; } - public static byte byte8$get(MemorySegment seg) { - return (byte)CFUUIDBytes.byte8$VH.get(seg); + + private static final long byte7$OFFSET = $LAYOUT.byteOffset(groupElement("byte7")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte7 + * } + */ + public static final long byte7$offset() { + return byte7$OFFSET; } - public static void byte8$set( MemorySegment seg, byte x) { - CFUUIDBytes.byte8$VH.set(seg, x); + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte7 + * } + */ + public static byte byte7(MemorySegment struct) { + return struct.get(byte7$LAYOUT, byte7$OFFSET); } - public static byte byte8$get(MemorySegment seg, long index) { - return (byte)CFUUIDBytes.byte8$VH.get(seg.asSlice(index*sizeof())); + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte7 + * } + */ + public static void byte7(MemorySegment struct, byte fieldValue) { + struct.set(byte7$LAYOUT, byte7$OFFSET, fieldValue); } - public static void byte8$set(MemorySegment seg, long index, byte x) { - CFUUIDBytes.byte8$VH.set(seg.asSlice(index*sizeof()), x); + + private static final OfByte byte8$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte8")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte8 + * } + */ + public static final OfByte byte8$layout() { + return byte8$LAYOUT; } - static final VarHandle byte9$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("byte9")); - public static VarHandle byte9$VH() { - return CFUUIDBytes.byte9$VH; + + private static final long byte8$OFFSET = $LAYOUT.byteOffset(groupElement("byte8")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte8 + * } + */ + public static final long byte8$offset() { + return byte8$OFFSET; } - public static byte byte9$get(MemorySegment seg) { - return (byte)CFUUIDBytes.byte9$VH.get(seg); + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte8 + * } + */ + public static byte byte8(MemorySegment struct) { + return struct.get(byte8$LAYOUT, byte8$OFFSET); } - public static void byte9$set( MemorySegment seg, byte x) { - CFUUIDBytes.byte9$VH.set(seg, x); + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte8 + * } + */ + public static void byte8(MemorySegment struct, byte fieldValue) { + struct.set(byte8$LAYOUT, byte8$OFFSET, fieldValue); } - public static byte byte9$get(MemorySegment seg, long index) { - return (byte)CFUUIDBytes.byte9$VH.get(seg.asSlice(index*sizeof())); + + private static final OfByte byte9$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte9")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte9 + * } + */ + public static final OfByte byte9$layout() { + return byte9$LAYOUT; } - public static void byte9$set(MemorySegment seg, long index, byte x) { - CFUUIDBytes.byte9$VH.set(seg.asSlice(index*sizeof()), x); + + private static final long byte9$OFFSET = $LAYOUT.byteOffset(groupElement("byte9")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte9 + * } + */ + public static final long byte9$offset() { + return byte9$OFFSET; } - static final VarHandle byte10$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("byte10")); - public static VarHandle byte10$VH() { - return CFUUIDBytes.byte10$VH; + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte9 + * } + */ + public static byte byte9(MemorySegment struct) { + return struct.get(byte9$LAYOUT, byte9$OFFSET); } - public static byte byte10$get(MemorySegment seg) { - return (byte)CFUUIDBytes.byte10$VH.get(seg); + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte9 + * } + */ + public static void byte9(MemorySegment struct, byte fieldValue) { + struct.set(byte9$LAYOUT, byte9$OFFSET, fieldValue); } - public static void byte10$set( MemorySegment seg, byte x) { - CFUUIDBytes.byte10$VH.set(seg, x); + + private static final OfByte byte10$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte10")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte10 + * } + */ + public static final OfByte byte10$layout() { + return byte10$LAYOUT; } - public static byte byte10$get(MemorySegment seg, long index) { - return (byte)CFUUIDBytes.byte10$VH.get(seg.asSlice(index*sizeof())); + + private static final long byte10$OFFSET = $LAYOUT.byteOffset(groupElement("byte10")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte10 + * } + */ + public static final long byte10$offset() { + return byte10$OFFSET; } - public static void byte10$set(MemorySegment seg, long index, byte x) { - CFUUIDBytes.byte10$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte10 + * } + */ + public static byte byte10(MemorySegment struct) { + return struct.get(byte10$LAYOUT, byte10$OFFSET); } - static final VarHandle byte11$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("byte11")); - public static VarHandle byte11$VH() { - return CFUUIDBytes.byte11$VH; + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte10 + * } + */ + public static void byte10(MemorySegment struct, byte fieldValue) { + struct.set(byte10$LAYOUT, byte10$OFFSET, fieldValue); } - public static byte byte11$get(MemorySegment seg) { - return (byte)CFUUIDBytes.byte11$VH.get(seg); + + private static final OfByte byte11$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte11")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte11 + * } + */ + public static final OfByte byte11$layout() { + return byte11$LAYOUT; } - public static void byte11$set( MemorySegment seg, byte x) { - CFUUIDBytes.byte11$VH.set(seg, x); + + private static final long byte11$OFFSET = $LAYOUT.byteOffset(groupElement("byte11")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte11 + * } + */ + public static final long byte11$offset() { + return byte11$OFFSET; } - public static byte byte11$get(MemorySegment seg, long index) { - return (byte)CFUUIDBytes.byte11$VH.get(seg.asSlice(index*sizeof())); + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte11 + * } + */ + public static byte byte11(MemorySegment struct) { + return struct.get(byte11$LAYOUT, byte11$OFFSET); } - public static void byte11$set(MemorySegment seg, long index, byte x) { - CFUUIDBytes.byte11$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte11 + * } + */ + public static void byte11(MemorySegment struct, byte fieldValue) { + struct.set(byte11$LAYOUT, byte11$OFFSET, fieldValue); } - static final VarHandle byte12$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("byte12")); - public static VarHandle byte12$VH() { - return CFUUIDBytes.byte12$VH; + + private static final OfByte byte12$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte12")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte12 + * } + */ + public static final OfByte byte12$layout() { + return byte12$LAYOUT; } - public static byte byte12$get(MemorySegment seg) { - return (byte)CFUUIDBytes.byte12$VH.get(seg); + + private static final long byte12$OFFSET = $LAYOUT.byteOffset(groupElement("byte12")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte12 + * } + */ + public static final long byte12$offset() { + return byte12$OFFSET; } - public static void byte12$set( MemorySegment seg, byte x) { - CFUUIDBytes.byte12$VH.set(seg, x); + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte12 + * } + */ + public static byte byte12(MemorySegment struct) { + return struct.get(byte12$LAYOUT, byte12$OFFSET); } - public static byte byte12$get(MemorySegment seg, long index) { - return (byte)CFUUIDBytes.byte12$VH.get(seg.asSlice(index*sizeof())); + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte12 + * } + */ + public static void byte12(MemorySegment struct, byte fieldValue) { + struct.set(byte12$LAYOUT, byte12$OFFSET, fieldValue); } - public static void byte12$set(MemorySegment seg, long index, byte x) { - CFUUIDBytes.byte12$VH.set(seg.asSlice(index*sizeof()), x); + + private static final OfByte byte13$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte13")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte13 + * } + */ + public static final OfByte byte13$layout() { + return byte13$LAYOUT; } - static final VarHandle byte13$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("byte13")); - public static VarHandle byte13$VH() { - return CFUUIDBytes.byte13$VH; + + private static final long byte13$OFFSET = $LAYOUT.byteOffset(groupElement("byte13")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte13 + * } + */ + public static final long byte13$offset() { + return byte13$OFFSET; } - public static byte byte13$get(MemorySegment seg) { - return (byte)CFUUIDBytes.byte13$VH.get(seg); + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte13 + * } + */ + public static byte byte13(MemorySegment struct) { + return struct.get(byte13$LAYOUT, byte13$OFFSET); } - public static void byte13$set( MemorySegment seg, byte x) { - CFUUIDBytes.byte13$VH.set(seg, x); + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte13 + * } + */ + public static void byte13(MemorySegment struct, byte fieldValue) { + struct.set(byte13$LAYOUT, byte13$OFFSET, fieldValue); } - public static byte byte13$get(MemorySegment seg, long index) { - return (byte)CFUUIDBytes.byte13$VH.get(seg.asSlice(index*sizeof())); + + private static final OfByte byte14$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte14")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte14 + * } + */ + public static final OfByte byte14$layout() { + return byte14$LAYOUT; } - public static void byte13$set(MemorySegment seg, long index, byte x) { - CFUUIDBytes.byte13$VH.set(seg.asSlice(index*sizeof()), x); + + private static final long byte14$OFFSET = $LAYOUT.byteOffset(groupElement("byte14")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte14 + * } + */ + public static final long byte14$offset() { + return byte14$OFFSET; } - static final VarHandle byte14$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("byte14")); - public static VarHandle byte14$VH() { - return CFUUIDBytes.byte14$VH; + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte14 + * } + */ + public static byte byte14(MemorySegment struct) { + return struct.get(byte14$LAYOUT, byte14$OFFSET); } - public static byte byte14$get(MemorySegment seg) { - return (byte)CFUUIDBytes.byte14$VH.get(seg); + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte14 + * } + */ + public static void byte14(MemorySegment struct, byte fieldValue) { + struct.set(byte14$LAYOUT, byte14$OFFSET, fieldValue); } - public static void byte14$set( MemorySegment seg, byte x) { - CFUUIDBytes.byte14$VH.set(seg, x); + + private static final OfByte byte15$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte15")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte15 + * } + */ + public static final OfByte byte15$layout() { + return byte15$LAYOUT; } - public static byte byte14$get(MemorySegment seg, long index) { - return (byte)CFUUIDBytes.byte14$VH.get(seg.asSlice(index*sizeof())); + + private static final long byte15$OFFSET = $LAYOUT.byteOffset(groupElement("byte15")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte15 + * } + */ + public static final long byte15$offset() { + return byte15$OFFSET; } - public static void byte14$set(MemorySegment seg, long index, byte x) { - CFUUIDBytes.byte14$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte15 + * } + */ + public static byte byte15(MemorySegment struct) { + return struct.get(byte15$LAYOUT, byte15$OFFSET); } - static final VarHandle byte15$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("byte15")); - public static VarHandle byte15$VH() { - return CFUUIDBytes.byte15$VH; + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte15 + * } + */ + public static void byte15(MemorySegment struct, byte fieldValue) { + struct.set(byte15$LAYOUT, byte15$OFFSET, fieldValue); } - public static byte byte15$get(MemorySegment seg) { - return (byte)CFUUIDBytes.byte15$VH.get(seg); + + /** + * Obtains a slice of {@code arrayParam} which selects the array element at {@code index}. + * The returned segment has address {@code arrayParam.address() + index * layout().byteSize()} + */ + public static MemorySegment asSlice(MemorySegment array, long index) { + return array.asSlice(layout().byteSize() * index); } - public static void byte15$set( MemorySegment seg, byte x) { - CFUUIDBytes.byte15$VH.set(seg, x); + + /** + * The size (in bytes) of this struct + */ + public static long sizeof() { return layout().byteSize(); } + + /** + * Allocate a segment of size {@code layout().byteSize()} using {@code allocator} + */ + public static MemorySegment allocate(SegmentAllocator allocator) { + return allocator.allocate(layout()); } - public static byte byte15$get(MemorySegment seg, long index) { - return (byte)CFUUIDBytes.byte15$VH.get(seg.asSlice(index*sizeof())); + + /** + * Allocate an array of size {@code elementCount} using {@code allocator}. + * The returned segment has size {@code elementCount * layout().byteSize()}. + */ + public static MemorySegment allocateArray(long elementCount, SegmentAllocator allocator) { + return allocator.allocate(MemoryLayout.sequenceLayout(elementCount, layout())); } - public static void byte15$set(MemorySegment seg, long index, byte x) { - CFUUIDBytes.byte15$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, Arena arena, Consumer cleanup) { + return reinterpret(addr, 1, arena, cleanup); } - public static long sizeof() { return $LAYOUT().byteSize(); } - public static MemorySegment allocate(SegmentAllocator allocator) { return allocator.allocate($LAYOUT()); } - public static MemorySegment allocateArray(int len, SegmentAllocator allocator) { - return allocator.allocate(MemoryLayout.sequenceLayout(len, $LAYOUT())); + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code elementCount * layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, long elementCount, Arena arena, Consumer cleanup) { + return addr.reinterpret(layout().byteSize() * elementCount, arena, cleanup); } - public static MemorySegment ofAddress(MemoryAddress addr, MemorySession session) { return RuntimeHelper.asArray(addr, $LAYOUT(), 1, session); } } - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/Constants$root.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/Constants$root.java deleted file mode 100644 index 0755ccbe..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/Constants$root.java +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.macos.gen.corefoundation; - -import static java.lang.foreign.ValueLayout.*; -public class Constants$root { - - static final OfBoolean C_BOOL$LAYOUT = JAVA_BOOLEAN; - static final OfByte C_CHAR$LAYOUT = JAVA_BYTE; - static final OfShort C_SHORT$LAYOUT = JAVA_SHORT.withBitAlignment(16); - static final OfInt C_INT$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfLong C_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfLong C_LONG_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfFloat C_FLOAT$LAYOUT = JAVA_FLOAT.withBitAlignment(32); - static final OfDouble C_DOUBLE$LAYOUT = JAVA_DOUBLE.withBitAlignment(64); - static final OfAddress C_POINTER$LAYOUT = ADDRESS.withBitAlignment(64); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/CoreFoundation$shared.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/CoreFoundation$shared.java new file mode 100644 index 00000000..1994933a --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/CoreFoundation$shared.java @@ -0,0 +1,63 @@ +// Generated by jextract + +package net.codecrete.usb.macos.gen.corefoundation; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +public class CoreFoundation$shared { + + CoreFoundation$shared() { + // Should not be called directly + } + + public static final ValueLayout.OfBoolean C_BOOL = (ValueLayout.OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool"); + public static final ValueLayout.OfByte C_CHAR =(ValueLayout.OfByte)Linker.nativeLinker().canonicalLayouts().get("char"); + public static final ValueLayout.OfShort C_SHORT = (ValueLayout.OfShort) Linker.nativeLinker().canonicalLayouts().get("short"); + public static final ValueLayout.OfInt C_INT = (ValueLayout.OfInt) Linker.nativeLinker().canonicalLayouts().get("int"); + public static final ValueLayout.OfLong C_LONG_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long long"); + public static final ValueLayout.OfFloat C_FLOAT = (ValueLayout.OfFloat) Linker.nativeLinker().canonicalLayouts().get("float"); + public static final ValueLayout.OfDouble C_DOUBLE = (ValueLayout.OfDouble) Linker.nativeLinker().canonicalLayouts().get("double"); + public static final AddressLayout C_POINTER = ((AddressLayout) Linker.nativeLinker().canonicalLayouts().get("void*")) + .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, C_CHAR)); + public static final ValueLayout.OfLong C_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long"); + + static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + + static void traceDowncall(String name, Object... args) { + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("%s(%s)\n", name, traceArgs); + } + + static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { + try { + return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } + + static MemoryLayout align(MemoryLayout layout, long align) { + return switch (layout) { + case PaddingLayout p -> p; + case ValueLayout v -> v.withByteAlignment(align); + case GroupLayout g -> { + MemoryLayout[] alignedMembers = g.memberLayouts().stream() + .map(m -> align(m, align)).toArray(MemoryLayout[]::new); + yield g instanceof StructLayout ? + MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); + } + case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); + }; + } +} + diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/CoreFoundation.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/CoreFoundation.java index 2e501cd5..a1690ca3 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/CoreFoundation.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/CoreFoundation.java @@ -2,170 +2,1249 @@ package net.codecrete.usb.macos.gen.corefoundation; -import java.lang.foreign.Addressable; -import java.lang.foreign.MemoryAddress; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentAllocator; -import java.lang.invoke.MethodHandle; +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; import static java.lang.foreign.ValueLayout.*; -public class CoreFoundation { - - /* package-private */ CoreFoundation() {} - public static OfByte C_CHAR = Constants$root.C_CHAR$LAYOUT; - public static OfShort C_SHORT = Constants$root.C_SHORT$LAYOUT; - public static OfInt C_INT = Constants$root.C_INT$LAYOUT; - public static OfLong C_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfLong C_LONG_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfFloat C_FLOAT = Constants$root.C_FLOAT$LAYOUT; - public static OfDouble C_DOUBLE = Constants$root.C_DOUBLE$LAYOUT; - public static OfAddress C_POINTER = Constants$root.C_POINTER$LAYOUT; - public static MethodHandle CFGetTypeID$MH() { - return RuntimeHelper.requireNonNull(constants$0.CFGetTypeID$MH,"CFGetTypeID"); - } - public static long CFGetTypeID ( Addressable cf) { - var mh$ = CFGetTypeID$MH(); +import static java.lang.foreign.MemoryLayout.PathElement.*; + +public class CoreFoundation extends CoreFoundation$shared { + + CoreFoundation() { + // Should not be called directly + } + + static final Arena LIBRARY_ARENA = Arena.ofAuto(); + + static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.libraryLookup("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation", LIBRARY_ARENA) + .or(SymbolLookup.loaderLookup()) + .or(Linker.nativeLinker().defaultLookup()); + + + private static class CFGetTypeID { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + CoreFoundation.C_LONG, + CoreFoundation.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("CFGetTypeID"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * extern CFTypeID CFGetTypeID(CFTypeRef cf) + * } + */ + public static FunctionDescriptor CFGetTypeID$descriptor() { + return CFGetTypeID.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * extern CFTypeID CFGetTypeID(CFTypeRef cf) + * } + */ + public static MethodHandle CFGetTypeID$handle() { + return CFGetTypeID.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * extern CFTypeID CFGetTypeID(CFTypeRef cf) + * } + */ + public static MemorySegment CFGetTypeID$address() { + return CFGetTypeID.ADDR; + } + + /** + * {@snippet lang=c : + * extern CFTypeID CFGetTypeID(CFTypeRef cf) + * } + */ + public static long CFGetTypeID(MemorySegment cf) { + var mh$ = CFGetTypeID.HANDLE; try { + if (TRACE_DOWNCALLS) { + traceDowncall("CFGetTypeID", cf); + } return (long)mh$.invokeExact(cf); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle CFRelease$MH() { - return RuntimeHelper.requireNonNull(constants$0.CFRelease$MH,"CFRelease"); + + private static class CFRelease { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + CoreFoundation.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("CFRelease"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * extern void CFRelease(CFTypeRef cf) + * } + */ + public static FunctionDescriptor CFRelease$descriptor() { + return CFRelease.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * extern void CFRelease(CFTypeRef cf) + * } + */ + public static MethodHandle CFRelease$handle() { + return CFRelease.HANDLE; } - public static void CFRelease ( Addressable cf) { - var mh$ = CFRelease$MH(); + + /** + * Address for: + * {@snippet lang=c : + * extern void CFRelease(CFTypeRef cf) + * } + */ + public static MemorySegment CFRelease$address() { + return CFRelease.ADDR; + } + + /** + * {@snippet lang=c : + * extern void CFRelease(CFTypeRef cf) + * } + */ + public static void CFRelease(MemorySegment cf) { + var mh$ = CFRelease.HANDLE; try { + if (TRACE_DOWNCALLS) { + traceDowncall("CFRelease", cf); + } mh$.invokeExact(cf); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle CFStringGetTypeID$MH() { - return RuntimeHelper.requireNonNull(constants$0.CFStringGetTypeID$MH,"CFStringGetTypeID"); + + private static class CFDataCreate { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + CoreFoundation.C_POINTER, + CoreFoundation.C_POINTER, + CoreFoundation.C_POINTER, + CoreFoundation.C_LONG + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("CFDataCreate"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * extern CFDataRef CFDataCreate(CFAllocatorRef allocator, const UInt8 *bytes, CFIndex length) + * } + */ + public static FunctionDescriptor CFDataCreate$descriptor() { + return CFDataCreate.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * extern CFDataRef CFDataCreate(CFAllocatorRef allocator, const UInt8 *bytes, CFIndex length) + * } + */ + public static MethodHandle CFDataCreate$handle() { + return CFDataCreate.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * extern CFDataRef CFDataCreate(CFAllocatorRef allocator, const UInt8 *bytes, CFIndex length) + * } + */ + public static MemorySegment CFDataCreate$address() { + return CFDataCreate.ADDR; + } + + /** + * {@snippet lang=c : + * extern CFDataRef CFDataCreate(CFAllocatorRef allocator, const UInt8 *bytes, CFIndex length) + * } + */ + public static MemorySegment CFDataCreate(MemorySegment allocator, MemorySegment bytes, long length) { + var mh$ = CFDataCreate.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("CFDataCreate", allocator, bytes, length); + } + return (MemorySegment)mh$.invokeExact(allocator, bytes, length); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + private static class CFDataGetBytePtr { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + CoreFoundation.C_POINTER, + CoreFoundation.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("CFDataGetBytePtr"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * extern const UInt8 *CFDataGetBytePtr(CFDataRef theData) + * } + */ + public static FunctionDescriptor CFDataGetBytePtr$descriptor() { + return CFDataGetBytePtr.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * extern const UInt8 *CFDataGetBytePtr(CFDataRef theData) + * } + */ + public static MethodHandle CFDataGetBytePtr$handle() { + return CFDataGetBytePtr.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * extern const UInt8 *CFDataGetBytePtr(CFDataRef theData) + * } + */ + public static MemorySegment CFDataGetBytePtr$address() { + return CFDataGetBytePtr.ADDR; + } + + /** + * {@snippet lang=c : + * extern const UInt8 *CFDataGetBytePtr(CFDataRef theData) + * } + */ + public static MemorySegment CFDataGetBytePtr(MemorySegment theData) { + var mh$ = CFDataGetBytePtr.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("CFDataGetBytePtr", theData); + } + return (MemorySegment)mh$.invokeExact(theData); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + private static class CFStringGetTypeID { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + CoreFoundation.C_LONG ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("CFStringGetTypeID"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * extern CFTypeID CFStringGetTypeID(void) + * } + */ + public static FunctionDescriptor CFStringGetTypeID$descriptor() { + return CFStringGetTypeID.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * extern CFTypeID CFStringGetTypeID(void) + * } + */ + public static MethodHandle CFStringGetTypeID$handle() { + return CFStringGetTypeID.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * extern CFTypeID CFStringGetTypeID(void) + * } + */ + public static MemorySegment CFStringGetTypeID$address() { + return CFStringGetTypeID.ADDR; } - public static long CFStringGetTypeID () { - var mh$ = CFStringGetTypeID$MH(); + + /** + * {@snippet lang=c : + * extern CFTypeID CFStringGetTypeID(void) + * } + */ + public static long CFStringGetTypeID() { + var mh$ = CFStringGetTypeID.HANDLE; try { + if (TRACE_DOWNCALLS) { + traceDowncall("CFStringGetTypeID"); + } return (long)mh$.invokeExact(); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle CFStringCreateWithCharacters$MH() { - return RuntimeHelper.requireNonNull(constants$0.CFStringCreateWithCharacters$MH,"CFStringCreateWithCharacters"); + + private static class CFStringCreateWithCharacters { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + CoreFoundation.C_POINTER, + CoreFoundation.C_POINTER, + CoreFoundation.C_POINTER, + CoreFoundation.C_LONG + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("CFStringCreateWithCharacters"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * extern CFStringRef CFStringCreateWithCharacters(CFAllocatorRef alloc, const UniChar *chars, CFIndex numChars) + * } + */ + public static FunctionDescriptor CFStringCreateWithCharacters$descriptor() { + return CFStringCreateWithCharacters.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * extern CFStringRef CFStringCreateWithCharacters(CFAllocatorRef alloc, const UniChar *chars, CFIndex numChars) + * } + */ + public static MethodHandle CFStringCreateWithCharacters$handle() { + return CFStringCreateWithCharacters.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * extern CFStringRef CFStringCreateWithCharacters(CFAllocatorRef alloc, const UniChar *chars, CFIndex numChars) + * } + */ + public static MemorySegment CFStringCreateWithCharacters$address() { + return CFStringCreateWithCharacters.ADDR; } - public static MemoryAddress CFStringCreateWithCharacters ( Addressable alloc, Addressable chars, long numChars) { - var mh$ = CFStringCreateWithCharacters$MH(); + + /** + * {@snippet lang=c : + * extern CFStringRef CFStringCreateWithCharacters(CFAllocatorRef alloc, const UniChar *chars, CFIndex numChars) + * } + */ + public static MemorySegment CFStringCreateWithCharacters(MemorySegment alloc, MemorySegment chars, long numChars) { + var mh$ = CFStringCreateWithCharacters.HANDLE; try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(alloc, chars, numChars); + if (TRACE_DOWNCALLS) { + traceDowncall("CFStringCreateWithCharacters", alloc, chars, numChars); + } + return (MemorySegment)mh$.invokeExact(alloc, chars, numChars); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle CFStringGetLength$MH() { - return RuntimeHelper.requireNonNull(constants$0.CFStringGetLength$MH,"CFStringGetLength"); + + private static class CFStringGetLength { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + CoreFoundation.C_LONG, + CoreFoundation.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("CFStringGetLength"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * extern CFIndex CFStringGetLength(CFStringRef theString) + * } + */ + public static FunctionDescriptor CFStringGetLength$descriptor() { + return CFStringGetLength.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * extern CFIndex CFStringGetLength(CFStringRef theString) + * } + */ + public static MethodHandle CFStringGetLength$handle() { + return CFStringGetLength.HANDLE; } - public static long CFStringGetLength ( Addressable theString) { - var mh$ = CFStringGetLength$MH(); + + /** + * Address for: + * {@snippet lang=c : + * extern CFIndex CFStringGetLength(CFStringRef theString) + * } + */ + public static MemorySegment CFStringGetLength$address() { + return CFStringGetLength.ADDR; + } + + /** + * {@snippet lang=c : + * extern CFIndex CFStringGetLength(CFStringRef theString) + * } + */ + public static long CFStringGetLength(MemorySegment theString) { + var mh$ = CFStringGetLength.HANDLE; try { + if (TRACE_DOWNCALLS) { + traceDowncall("CFStringGetLength", theString); + } return (long)mh$.invokeExact(theString); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle CFStringGetCharacters$MH() { - return RuntimeHelper.requireNonNull(constants$0.CFStringGetCharacters$MH,"CFStringGetCharacters"); + + private static class CFStringGetCharacters { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + CoreFoundation.C_POINTER, + CFRange.layout(), + CoreFoundation.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("CFStringGetCharacters"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * extern void CFStringGetCharacters(CFStringRef theString, CFRange range, UniChar *buffer) + * } + */ + public static FunctionDescriptor CFStringGetCharacters$descriptor() { + return CFStringGetCharacters.DESC; } - public static void CFStringGetCharacters ( Addressable theString, MemorySegment range, Addressable buffer) { - var mh$ = CFStringGetCharacters$MH(); + + /** + * Downcall method handle for: + * {@snippet lang=c : + * extern void CFStringGetCharacters(CFStringRef theString, CFRange range, UniChar *buffer) + * } + */ + public static MethodHandle CFStringGetCharacters$handle() { + return CFStringGetCharacters.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * extern void CFStringGetCharacters(CFStringRef theString, CFRange range, UniChar *buffer) + * } + */ + public static MemorySegment CFStringGetCharacters$address() { + return CFStringGetCharacters.ADDR; + } + + /** + * {@snippet lang=c : + * extern void CFStringGetCharacters(CFStringRef theString, CFRange range, UniChar *buffer) + * } + */ + public static void CFStringGetCharacters(MemorySegment theString, MemorySegment range, MemorySegment buffer) { + var mh$ = CFStringGetCharacters.HANDLE; try { + if (TRACE_DOWNCALLS) { + traceDowncall("CFStringGetCharacters", theString, range, buffer); + } mh$.invokeExact(theString, range, buffer); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } + private static final int kCFNumberSInt32Type = (int)3L; + /** + * {@snippet lang=c : + * enum enum (unnamed at /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CFNumber.h:31:9).kCFNumberSInt32Type = 3 + * } + */ public static int kCFNumberSInt32Type() { - return (int)3L; + return kCFNumberSInt32Type; } - public static MethodHandle CFNumberGetTypeID$MH() { - return RuntimeHelper.requireNonNull(constants$1.CFNumberGetTypeID$MH,"CFNumberGetTypeID"); + + private static class CFNumberGetTypeID { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + CoreFoundation.C_LONG ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("CFNumberGetTypeID"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } - public static long CFNumberGetTypeID () { - var mh$ = CFNumberGetTypeID$MH(); + + /** + * Function descriptor for: + * {@snippet lang=c : + * extern CFTypeID CFNumberGetTypeID(void) + * } + */ + public static FunctionDescriptor CFNumberGetTypeID$descriptor() { + return CFNumberGetTypeID.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * extern CFTypeID CFNumberGetTypeID(void) + * } + */ + public static MethodHandle CFNumberGetTypeID$handle() { + return CFNumberGetTypeID.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * extern CFTypeID CFNumberGetTypeID(void) + * } + */ + public static MemorySegment CFNumberGetTypeID$address() { + return CFNumberGetTypeID.ADDR; + } + + /** + * {@snippet lang=c : + * extern CFTypeID CFNumberGetTypeID(void) + * } + */ + public static long CFNumberGetTypeID() { + var mh$ = CFNumberGetTypeID.HANDLE; try { + if (TRACE_DOWNCALLS) { + traceDowncall("CFNumberGetTypeID"); + } return (long)mh$.invokeExact(); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle CFNumberGetValue$MH() { - return RuntimeHelper.requireNonNull(constants$1.CFNumberGetValue$MH,"CFNumberGetValue"); + + private static class CFNumberGetValue { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + CoreFoundation.C_CHAR, + CoreFoundation.C_POINTER, + CoreFoundation.C_LONG, + CoreFoundation.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("CFNumberGetValue"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * extern Boolean CFNumberGetValue(CFNumberRef number, CFNumberType theType, void *valuePtr) + * } + */ + public static FunctionDescriptor CFNumberGetValue$descriptor() { + return CFNumberGetValue.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * extern Boolean CFNumberGetValue(CFNumberRef number, CFNumberType theType, void *valuePtr) + * } + */ + public static MethodHandle CFNumberGetValue$handle() { + return CFNumberGetValue.HANDLE; } - public static byte CFNumberGetValue ( Addressable number, long theType, Addressable valuePtr) { - var mh$ = CFNumberGetValue$MH(); + + /** + * Address for: + * {@snippet lang=c : + * extern Boolean CFNumberGetValue(CFNumberRef number, CFNumberType theType, void *valuePtr) + * } + */ + public static MemorySegment CFNumberGetValue$address() { + return CFNumberGetValue.ADDR; + } + + /** + * {@snippet lang=c : + * extern Boolean CFNumberGetValue(CFNumberRef number, CFNumberType theType, void *valuePtr) + * } + */ + public static byte CFNumberGetValue(MemorySegment number, long theType, MemorySegment valuePtr) { + var mh$ = CFNumberGetValue.HANDLE; try { + if (TRACE_DOWNCALLS) { + traceDowncall("CFNumberGetValue", number, theType, valuePtr); + } return (byte)mh$.invokeExact(number, theType, valuePtr); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle CFRunLoopGetCurrent$MH() { - return RuntimeHelper.requireNonNull(constants$1.CFRunLoopGetCurrent$MH,"CFRunLoopGetCurrent"); + + private static class CFRunLoopGetCurrent { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + CoreFoundation.C_POINTER ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("CFRunLoopGetCurrent"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * extern CFRunLoopRef CFRunLoopGetCurrent(void) + * } + */ + public static FunctionDescriptor CFRunLoopGetCurrent$descriptor() { + return CFRunLoopGetCurrent.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * extern CFRunLoopRef CFRunLoopGetCurrent(void) + * } + */ + public static MethodHandle CFRunLoopGetCurrent$handle() { + return CFRunLoopGetCurrent.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * extern CFRunLoopRef CFRunLoopGetCurrent(void) + * } + */ + public static MemorySegment CFRunLoopGetCurrent$address() { + return CFRunLoopGetCurrent.ADDR; } - public static MemoryAddress CFRunLoopGetCurrent () { - var mh$ = CFRunLoopGetCurrent$MH(); + + /** + * {@snippet lang=c : + * extern CFRunLoopRef CFRunLoopGetCurrent(void) + * } + */ + public static MemorySegment CFRunLoopGetCurrent() { + var mh$ = CFRunLoopGetCurrent.HANDLE; try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(); + if (TRACE_DOWNCALLS) { + traceDowncall("CFRunLoopGetCurrent"); + } + return (MemorySegment)mh$.invokeExact(); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle CFRunLoopRun$MH() { - return RuntimeHelper.requireNonNull(constants$1.CFRunLoopRun$MH,"CFRunLoopRun"); + + private static class CFRunLoopRun { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("CFRunLoopRun"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * extern void CFRunLoopRun(void) + * } + */ + public static FunctionDescriptor CFRunLoopRun$descriptor() { + return CFRunLoopRun.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * extern void CFRunLoopRun(void) + * } + */ + public static MethodHandle CFRunLoopRun$handle() { + return CFRunLoopRun.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * extern void CFRunLoopRun(void) + * } + */ + public static MemorySegment CFRunLoopRun$address() { + return CFRunLoopRun.ADDR; } - public static void CFRunLoopRun () { - var mh$ = CFRunLoopRun$MH(); + + /** + * {@snippet lang=c : + * extern void CFRunLoopRun(void) + * } + */ + public static void CFRunLoopRun() { + var mh$ = CFRunLoopRun.HANDLE; try { + if (TRACE_DOWNCALLS) { + traceDowncall("CFRunLoopRun"); + } mh$.invokeExact(); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle CFRunLoopAddSource$MH() { - return RuntimeHelper.requireNonNull(constants$1.CFRunLoopAddSource$MH,"CFRunLoopAddSource"); + + private static class CFRunLoopAddSource { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + CoreFoundation.C_POINTER, + CoreFoundation.C_POINTER, + CoreFoundation.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("CFRunLoopAddSource"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * extern void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFRunLoopMode mode) + * } + */ + public static FunctionDescriptor CFRunLoopAddSource$descriptor() { + return CFRunLoopAddSource.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * extern void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFRunLoopMode mode) + * } + */ + public static MethodHandle CFRunLoopAddSource$handle() { + return CFRunLoopAddSource.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * extern void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFRunLoopMode mode) + * } + */ + public static MemorySegment CFRunLoopAddSource$address() { + return CFRunLoopAddSource.ADDR; } - public static void CFRunLoopAddSource ( Addressable rl, Addressable source, Addressable mode) { - var mh$ = CFRunLoopAddSource$MH(); + + /** + * {@snippet lang=c : + * extern void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFRunLoopMode mode) + * } + */ + public static void CFRunLoopAddSource(MemorySegment rl, MemorySegment source, MemorySegment mode) { + var mh$ = CFRunLoopAddSource.HANDLE; try { + if (TRACE_DOWNCALLS) { + traceDowncall("CFRunLoopAddSource", rl, source, mode); + } mh$.invokeExact(rl, source, mode); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle CFUUIDGetUUIDBytes$MH() { - return RuntimeHelper.requireNonNull(constants$1.CFUUIDGetUUIDBytes$MH,"CFUUIDGetUUIDBytes"); + + private static class CFRunLoopRemoveSource { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + CoreFoundation.C_POINTER, + CoreFoundation.C_POINTER, + CoreFoundation.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("CFRunLoopRemoveSource"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } - public static MemorySegment CFUUIDGetUUIDBytes ( SegmentAllocator allocator, Addressable uuid) { - var mh$ = CFUUIDGetUUIDBytes$MH(); + + /** + * Function descriptor for: + * {@snippet lang=c : + * extern void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFRunLoopMode mode) + * } + */ + public static FunctionDescriptor CFRunLoopRemoveSource$descriptor() { + return CFRunLoopRemoveSource.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * extern void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFRunLoopMode mode) + * } + */ + public static MethodHandle CFRunLoopRemoveSource$handle() { + return CFRunLoopRemoveSource.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * extern void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFRunLoopMode mode) + * } + */ + public static MemorySegment CFRunLoopRemoveSource$address() { + return CFRunLoopRemoveSource.ADDR; + } + + /** + * {@snippet lang=c : + * extern void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFRunLoopMode mode) + * } + */ + public static void CFRunLoopRemoveSource(MemorySegment rl, MemorySegment source, MemorySegment mode) { + var mh$ = CFRunLoopRemoveSource.HANDLE; try { - return (java.lang.foreign.MemorySegment)mh$.invokeExact(allocator, uuid); + if (TRACE_DOWNCALLS) { + traceDowncall("CFRunLoopRemoveSource", rl, source, mode); + } + mh$.invokeExact(rl, source, mode); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle CFUUIDCreateFromUUIDBytes$MH() { - return RuntimeHelper.requireNonNull(constants$2.CFUUIDCreateFromUUIDBytes$MH,"CFUUIDCreateFromUUIDBytes"); + + private static class CFUUIDGetUUIDBytes { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + CFUUIDBytes.layout(), + CoreFoundation.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("CFUUIDGetUUIDBytes"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } - public static MemoryAddress CFUUIDCreateFromUUIDBytes ( Addressable alloc, MemorySegment bytes) { - var mh$ = CFUUIDCreateFromUUIDBytes$MH(); + + /** + * Function descriptor for: + * {@snippet lang=c : + * extern CFUUIDBytes CFUUIDGetUUIDBytes(CFUUIDRef uuid) + * } + */ + public static FunctionDescriptor CFUUIDGetUUIDBytes$descriptor() { + return CFUUIDGetUUIDBytes.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * extern CFUUIDBytes CFUUIDGetUUIDBytes(CFUUIDRef uuid) + * } + */ + public static MethodHandle CFUUIDGetUUIDBytes$handle() { + return CFUUIDGetUUIDBytes.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * extern CFUUIDBytes CFUUIDGetUUIDBytes(CFUUIDRef uuid) + * } + */ + public static MemorySegment CFUUIDGetUUIDBytes$address() { + return CFUUIDGetUUIDBytes.ADDR; + } + + /** + * {@snippet lang=c : + * extern CFUUIDBytes CFUUIDGetUUIDBytes(CFUUIDRef uuid) + * } + */ + public static MemorySegment CFUUIDGetUUIDBytes(SegmentAllocator allocator, MemorySegment uuid) { + var mh$ = CFUUIDGetUUIDBytes.HANDLE; try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(alloc, bytes); + if (TRACE_DOWNCALLS) { + traceDowncall("CFUUIDGetUUIDBytes", allocator, uuid); + } + return (MemorySegment)mh$.invokeExact(allocator, uuid); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); + } + } + + private static class CFUUIDCreateFromUUIDBytes { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + CoreFoundation.C_POINTER, + CoreFoundation.C_POINTER, + CFUUIDBytes.layout() + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("CFUUIDCreateFromUUIDBytes"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * extern CFUUIDRef CFUUIDCreateFromUUIDBytes(CFAllocatorRef alloc, CFUUIDBytes bytes) + * } + */ + public static FunctionDescriptor CFUUIDCreateFromUUIDBytes$descriptor() { + return CFUUIDCreateFromUUIDBytes.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * extern CFUUIDRef CFUUIDCreateFromUUIDBytes(CFAllocatorRef alloc, CFUUIDBytes bytes) + * } + */ + public static MethodHandle CFUUIDCreateFromUUIDBytes$handle() { + return CFUUIDCreateFromUUIDBytes.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * extern CFUUIDRef CFUUIDCreateFromUUIDBytes(CFAllocatorRef alloc, CFUUIDBytes bytes) + * } + */ + public static MemorySegment CFUUIDCreateFromUUIDBytes$address() { + return CFUUIDCreateFromUUIDBytes.ADDR; + } + + /** + * {@snippet lang=c : + * extern CFUUIDRef CFUUIDCreateFromUUIDBytes(CFAllocatorRef alloc, CFUUIDBytes bytes) + * } + */ + public static MemorySegment CFUUIDCreateFromUUIDBytes(MemorySegment alloc, MemorySegment bytes) { + var mh$ = CFUUIDCreateFromUUIDBytes.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("CFUUIDCreateFromUUIDBytes", alloc, bytes); + } + return (MemorySegment)mh$.invokeExact(alloc, bytes); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + private static class CFMessagePortCreateLocal { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + CoreFoundation.C_POINTER, + CoreFoundation.C_POINTER, + CoreFoundation.C_POINTER, + CoreFoundation.C_POINTER, + CoreFoundation.C_POINTER, + CoreFoundation.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("CFMessagePortCreateLocal"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * extern CFMessagePortRef CFMessagePortCreateLocal(CFAllocatorRef allocator, CFStringRef name, CFMessagePortCallBack callout, CFMessagePortContext *context, Boolean *shouldFreeInfo) + * } + */ + public static FunctionDescriptor CFMessagePortCreateLocal$descriptor() { + return CFMessagePortCreateLocal.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * extern CFMessagePortRef CFMessagePortCreateLocal(CFAllocatorRef allocator, CFStringRef name, CFMessagePortCallBack callout, CFMessagePortContext *context, Boolean *shouldFreeInfo) + * } + */ + public static MethodHandle CFMessagePortCreateLocal$handle() { + return CFMessagePortCreateLocal.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * extern CFMessagePortRef CFMessagePortCreateLocal(CFAllocatorRef allocator, CFStringRef name, CFMessagePortCallBack callout, CFMessagePortContext *context, Boolean *shouldFreeInfo) + * } + */ + public static MemorySegment CFMessagePortCreateLocal$address() { + return CFMessagePortCreateLocal.ADDR; + } + + /** + * {@snippet lang=c : + * extern CFMessagePortRef CFMessagePortCreateLocal(CFAllocatorRef allocator, CFStringRef name, CFMessagePortCallBack callout, CFMessagePortContext *context, Boolean *shouldFreeInfo) + * } + */ + public static MemorySegment CFMessagePortCreateLocal(MemorySegment allocator, MemorySegment name, MemorySegment callout, MemorySegment context, MemorySegment shouldFreeInfo) { + var mh$ = CFMessagePortCreateLocal.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("CFMessagePortCreateLocal", allocator, name, callout, context, shouldFreeInfo); + } + return (MemorySegment)mh$.invokeExact(allocator, name, callout, context, shouldFreeInfo); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + private static class CFMessagePortCreateRemote { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + CoreFoundation.C_POINTER, + CoreFoundation.C_POINTER, + CoreFoundation.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("CFMessagePortCreateRemote"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * extern CFMessagePortRef CFMessagePortCreateRemote(CFAllocatorRef allocator, CFStringRef name) + * } + */ + public static FunctionDescriptor CFMessagePortCreateRemote$descriptor() { + return CFMessagePortCreateRemote.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * extern CFMessagePortRef CFMessagePortCreateRemote(CFAllocatorRef allocator, CFStringRef name) + * } + */ + public static MethodHandle CFMessagePortCreateRemote$handle() { + return CFMessagePortCreateRemote.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * extern CFMessagePortRef CFMessagePortCreateRemote(CFAllocatorRef allocator, CFStringRef name) + * } + */ + public static MemorySegment CFMessagePortCreateRemote$address() { + return CFMessagePortCreateRemote.ADDR; + } + + /** + * {@snippet lang=c : + * extern CFMessagePortRef CFMessagePortCreateRemote(CFAllocatorRef allocator, CFStringRef name) + * } + */ + public static MemorySegment CFMessagePortCreateRemote(MemorySegment allocator, MemorySegment name) { + var mh$ = CFMessagePortCreateRemote.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("CFMessagePortCreateRemote", allocator, name); + } + return (MemorySegment)mh$.invokeExact(allocator, name); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); } } -} + private static class CFMessagePortSendRequest { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + CoreFoundation.C_INT, + CoreFoundation.C_POINTER, + CoreFoundation.C_INT, + CoreFoundation.C_POINTER, + CoreFoundation.C_DOUBLE, + CoreFoundation.C_DOUBLE, + CoreFoundation.C_POINTER, + CoreFoundation.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("CFMessagePortSendRequest"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * extern SInt32 CFMessagePortSendRequest(CFMessagePortRef remote, SInt32 msgid, CFDataRef data, CFTimeInterval sendTimeout, CFTimeInterval rcvTimeout, CFStringRef replyMode, CFDataRef *returnData) + * } + */ + public static FunctionDescriptor CFMessagePortSendRequest$descriptor() { + return CFMessagePortSendRequest.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * extern SInt32 CFMessagePortSendRequest(CFMessagePortRef remote, SInt32 msgid, CFDataRef data, CFTimeInterval sendTimeout, CFTimeInterval rcvTimeout, CFStringRef replyMode, CFDataRef *returnData) + * } + */ + public static MethodHandle CFMessagePortSendRequest$handle() { + return CFMessagePortSendRequest.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * extern SInt32 CFMessagePortSendRequest(CFMessagePortRef remote, SInt32 msgid, CFDataRef data, CFTimeInterval sendTimeout, CFTimeInterval rcvTimeout, CFStringRef replyMode, CFDataRef *returnData) + * } + */ + public static MemorySegment CFMessagePortSendRequest$address() { + return CFMessagePortSendRequest.ADDR; + } + + /** + * {@snippet lang=c : + * extern SInt32 CFMessagePortSendRequest(CFMessagePortRef remote, SInt32 msgid, CFDataRef data, CFTimeInterval sendTimeout, CFTimeInterval rcvTimeout, CFStringRef replyMode, CFDataRef *returnData) + * } + */ + public static int CFMessagePortSendRequest(MemorySegment remote, int msgid, MemorySegment data, double sendTimeout, double rcvTimeout, MemorySegment replyMode, MemorySegment returnData) { + var mh$ = CFMessagePortSendRequest.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("CFMessagePortSendRequest", remote, msgid, data, sendTimeout, rcvTimeout, replyMode, returnData); + } + return (int)mh$.invokeExact(remote, msgid, data, sendTimeout, rcvTimeout, replyMode, returnData); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + private static class CFMessagePortCreateRunLoopSource { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + CoreFoundation.C_POINTER, + CoreFoundation.C_POINTER, + CoreFoundation.C_POINTER, + CoreFoundation.C_LONG + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("CFMessagePortCreateRunLoopSource"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * extern CFRunLoopSourceRef CFMessagePortCreateRunLoopSource(CFAllocatorRef allocator, CFMessagePortRef local, CFIndex order) + * } + */ + public static FunctionDescriptor CFMessagePortCreateRunLoopSource$descriptor() { + return CFMessagePortCreateRunLoopSource.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * extern CFRunLoopSourceRef CFMessagePortCreateRunLoopSource(CFAllocatorRef allocator, CFMessagePortRef local, CFIndex order) + * } + */ + public static MethodHandle CFMessagePortCreateRunLoopSource$handle() { + return CFMessagePortCreateRunLoopSource.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * extern CFRunLoopSourceRef CFMessagePortCreateRunLoopSource(CFAllocatorRef allocator, CFMessagePortRef local, CFIndex order) + * } + */ + public static MemorySegment CFMessagePortCreateRunLoopSource$address() { + return CFMessagePortCreateRunLoopSource.ADDR; + } + + /** + * {@snippet lang=c : + * extern CFRunLoopSourceRef CFMessagePortCreateRunLoopSource(CFAllocatorRef allocator, CFMessagePortRef local, CFIndex order) + * } + */ + public static MemorySegment CFMessagePortCreateRunLoopSource(MemorySegment allocator, MemorySegment local, long order) { + var mh$ = CFMessagePortCreateRunLoopSource.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("CFMessagePortCreateRunLoopSource", allocator, local, order); + } + return (MemorySegment)mh$.invokeExact(allocator, local, order); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/RuntimeHelper.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/RuntimeHelper.java deleted file mode 100644 index 868a5980..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/RuntimeHelper.java +++ /dev/null @@ -1,217 +0,0 @@ -package net.codecrete.usb.macos.gen.corefoundation; -// Generated by jextract - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import static java.lang.foreign.ValueLayout.*; - -final class RuntimeHelper { - - private RuntimeHelper() {} - private final static Linker LINKER = Linker.nativeLinker(); - private final static ClassLoader LOADER = RuntimeHelper.class.getClassLoader(); - private final static MethodHandles.Lookup MH_LOOKUP = MethodHandles.lookup(); - private final static SymbolLookup SYMBOL_LOOKUP; - - final static SegmentAllocator CONSTANT_ALLOCATOR = - (size, align) -> MemorySegment.allocateNative(size, align, MemorySession.openImplicit()); - - static { -// System.loadLibrary("CoreFoundation.framework"); -// SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); - SymbolLookup loaderLookup = SymbolLookup.libraryLookup("CoreFoundation.framework/CoreFoundation", MemorySession.openImplicit()); - SYMBOL_LOOKUP = name -> loaderLookup.lookup(name).or(() -> LINKER.defaultLookup().lookup(name)); - } - - static T requireNonNull(T obj, String symbolName) { - if (obj == null) { - throw new UnsatisfiedLinkError("unresolved symbol: " + symbolName); - } - return obj; - } - - private final static SegmentAllocator THROWING_ALLOCATOR = (x, y) -> { throw new AssertionError("should not reach here"); }; - - static final MemorySegment lookupGlobalVariable(String name, MemoryLayout layout) { - return SYMBOL_LOOKUP.lookup(name).map(symbol -> MemorySegment.ofAddress(symbol.address(), layout.byteSize(), MemorySession.openShared())).orElse(null); - } - - static final MethodHandle downcallHandle(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> LINKER.downcallHandle(addr, fdesc)). - orElse(null); - } - - static final MethodHandle downcallHandle(FunctionDescriptor fdesc) { - return LINKER.downcallHandle(fdesc); - } - - static final MethodHandle downcallHandleVariadic(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> VarargsInvoker.make(addr, fdesc)). - orElse(null); - } - - static final MemorySegment upcallStub(Class fi, Z z, FunctionDescriptor fdesc, MemorySession session) { - try { - MethodHandle handle = MH_LOOKUP.findVirtual(fi, "apply", Linker.upcallType(fdesc)); - handle = handle.bindTo(z); - return LINKER.upcallStub(handle, fdesc, session); - } catch (Throwable ex) { - throw new AssertionError(ex); - } - } - - static MemorySegment asArray(MemoryAddress addr, MemoryLayout layout, int numElements, MemorySession session) { - return MemorySegment.ofAddress(addr, numElements * layout.byteSize(), session); - } - - // Internals only below this point - - private static class VarargsInvoker { - private static final MethodHandle INVOKE_MH; - private final MemorySegment symbol; - private final FunctionDescriptor function; - - private VarargsInvoker(MemorySegment symbol, FunctionDescriptor function) { - this.symbol = symbol; - this.function = function; - } - - static { - try { - INVOKE_MH = MethodHandles.lookup().findVirtual(VarargsInvoker.class, "invoke", MethodType.methodType(Object.class, SegmentAllocator.class, Object[].class)); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - static MethodHandle make(MemorySegment symbol, FunctionDescriptor function) { - VarargsInvoker invoker = new VarargsInvoker(symbol, function); - MethodHandle handle = INVOKE_MH.bindTo(invoker).asCollector(Object[].class, function.argumentLayouts().size() + 1); - MethodType mtype = MethodType.methodType(function.returnLayout().isPresent() ? carrier(function.returnLayout().get(), true) : void.class); - for (MemoryLayout layout : function.argumentLayouts()) { - mtype = mtype.appendParameterTypes(carrier(layout, false)); - } - mtype = mtype.appendParameterTypes(Object[].class); - if (mtype.returnType().equals(MemorySegment.class)) { - mtype = mtype.insertParameterTypes(0, SegmentAllocator.class); - } else { - handle = MethodHandles.insertArguments(handle, 0, THROWING_ALLOCATOR); - } - return handle.asType(mtype); - } - - static Class carrier(MemoryLayout layout, boolean ret) { - if (layout instanceof ValueLayout valueLayout) { - return (ret || valueLayout.carrier() != MemoryAddress.class) ? - valueLayout.carrier() : Addressable.class; - } else if (layout instanceof GroupLayout) { - return MemorySegment.class; - } else { - throw new AssertionError("Cannot get here!"); - } - } - - private Object invoke(SegmentAllocator allocator, Object[] args) throws Throwable { - // one trailing Object[] - int nNamedArgs = function.argumentLayouts().size(); - assert(args.length == nNamedArgs + 1); - // The last argument is the array of vararg collector - Object[] unnamedArgs = (Object[]) args[args.length - 1]; - - int argsCount = nNamedArgs + unnamedArgs.length; - Class[] argTypes = new Class[argsCount]; - MemoryLayout[] argLayouts = new MemoryLayout[nNamedArgs + unnamedArgs.length]; - - int pos = 0; - for (pos = 0; pos < nNamedArgs; pos++) { - argLayouts[pos] = function.argumentLayouts().get(pos); - } - - assert pos == nNamedArgs; - for (Object o: unnamedArgs) { - argLayouts[pos] = variadicLayout(normalize(o.getClass())); - pos++; - } - assert pos == argsCount; - - FunctionDescriptor f = (function.returnLayout().isEmpty()) ? - FunctionDescriptor.ofVoid(argLayouts) : - FunctionDescriptor.of(function.returnLayout().get(), argLayouts); - MethodHandle mh = LINKER.downcallHandle(symbol, f); - if (mh.type().returnType() == MemorySegment.class) { - mh = mh.bindTo(allocator); - } - // flatten argument list so that it can be passed to an asSpreader MH - Object[] allArgs = new Object[nNamedArgs + unnamedArgs.length]; - System.arraycopy(args, 0, allArgs, 0, nNamedArgs); - System.arraycopy(unnamedArgs, 0, allArgs, nNamedArgs, unnamedArgs.length); - - return mh.asSpreader(Object[].class, argsCount).invoke(allArgs); - } - - private static Class unboxIfNeeded(Class clazz) { - if (clazz == Boolean.class) { - return boolean.class; - } else if (clazz == Void.class) { - return void.class; - } else if (clazz == Byte.class) { - return byte.class; - } else if (clazz == Character.class) { - return char.class; - } else if (clazz == Short.class) { - return short.class; - } else if (clazz == Integer.class) { - return int.class; - } else if (clazz == Long.class) { - return long.class; - } else if (clazz == Float.class) { - return float.class; - } else if (clazz == Double.class) { - return double.class; - } else { - return clazz; - } - } - - private Class promote(Class c) { - if (c == byte.class || c == char.class || c == short.class || c == int.class) { - return long.class; - } else if (c == float.class) { - return double.class; - } else { - return c; - } - } - - private Class normalize(Class c) { - c = unboxIfNeeded(c); - if (c.isPrimitive()) { - return promote(c); - } - if (MemoryAddress.class.isAssignableFrom(c)) { - return MemoryAddress.class; - } - if (MemorySegment.class.isAssignableFrom(c)) { - return MemorySegment.class; - } - throw new IllegalArgumentException("Invalid type for ABI: " + c.getTypeName()); - } - - private MemoryLayout variadicLayout(Class c) { - if (c == long.class) { - return JAVA_LONG; - } else if (c == double.class) { - return JAVA_DOUBLE; - } else if (MemoryAddress.class.isAssignableFrom(c)) { - return ADDRESS; - } else { - throw new IllegalArgumentException("Unhandled variadic argument class: " + c); - } - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/constants$0.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/constants$0.java deleted file mode 100644 index 08d7d3ea..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/constants$0.java +++ /dev/null @@ -1,59 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.macos.gen.corefoundation; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryLayout; -import java.lang.invoke.MethodHandle; -class constants$0 { - - static final FunctionDescriptor CFGetTypeID$FUNC = FunctionDescriptor.of(Constants$root.C_LONG_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle CFGetTypeID$MH = RuntimeHelper.downcallHandle( - "CFGetTypeID", - constants$0.CFGetTypeID$FUNC - ); - static final FunctionDescriptor CFRelease$FUNC = FunctionDescriptor.ofVoid( - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle CFRelease$MH = RuntimeHelper.downcallHandle( - "CFRelease", - constants$0.CFRelease$FUNC - ); - static final FunctionDescriptor CFStringGetTypeID$FUNC = FunctionDescriptor.of(Constants$root.C_LONG_LONG$LAYOUT); - static final MethodHandle CFStringGetTypeID$MH = RuntimeHelper.downcallHandle( - "CFStringGetTypeID", - constants$0.CFStringGetTypeID$FUNC - ); - static final FunctionDescriptor CFStringCreateWithCharacters$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG_LONG$LAYOUT - ); - static final MethodHandle CFStringCreateWithCharacters$MH = RuntimeHelper.downcallHandle( - "CFStringCreateWithCharacters", - constants$0.CFStringCreateWithCharacters$FUNC - ); - static final FunctionDescriptor CFStringGetLength$FUNC = FunctionDescriptor.of(Constants$root.C_LONG_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle CFStringGetLength$MH = RuntimeHelper.downcallHandle( - "CFStringGetLength", - constants$0.CFStringGetLength$FUNC - ); - static final FunctionDescriptor CFStringGetCharacters$FUNC = FunctionDescriptor.ofVoid( - Constants$root.C_POINTER$LAYOUT, - MemoryLayout.structLayout( - Constants$root.C_LONG_LONG$LAYOUT.withName("location"), - Constants$root.C_LONG_LONG$LAYOUT.withName("length") - ), - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle CFStringGetCharacters$MH = RuntimeHelper.downcallHandle( - "CFStringGetCharacters", - constants$0.CFStringGetCharacters$FUNC - ); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/constants$1.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/constants$1.java deleted file mode 100644 index 16dac46b..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/constants$1.java +++ /dev/null @@ -1,69 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.macos.gen.corefoundation; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryLayout; -import java.lang.invoke.MethodHandle; -class constants$1 { - - static final FunctionDescriptor CFNumberGetTypeID$FUNC = FunctionDescriptor.of(Constants$root.C_LONG_LONG$LAYOUT); - static final MethodHandle CFNumberGetTypeID$MH = RuntimeHelper.downcallHandle( - "CFNumberGetTypeID", - constants$1.CFNumberGetTypeID$FUNC - ); - static final FunctionDescriptor CFNumberGetValue$FUNC = FunctionDescriptor.of(Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle CFNumberGetValue$MH = RuntimeHelper.downcallHandle( - "CFNumberGetValue", - constants$1.CFNumberGetValue$FUNC - ); - static final FunctionDescriptor CFRunLoopGetCurrent$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT); - static final MethodHandle CFRunLoopGetCurrent$MH = RuntimeHelper.downcallHandle( - "CFRunLoopGetCurrent", - constants$1.CFRunLoopGetCurrent$FUNC - ); - static final FunctionDescriptor CFRunLoopRun$FUNC = FunctionDescriptor.ofVoid(); - static final MethodHandle CFRunLoopRun$MH = RuntimeHelper.downcallHandle( - "CFRunLoopRun", - constants$1.CFRunLoopRun$FUNC - ); - static final FunctionDescriptor CFRunLoopAddSource$FUNC = FunctionDescriptor.ofVoid( - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle CFRunLoopAddSource$MH = RuntimeHelper.downcallHandle( - "CFRunLoopAddSource", - constants$1.CFRunLoopAddSource$FUNC - ); - static final FunctionDescriptor CFUUIDGetUUIDBytes$FUNC = FunctionDescriptor.of(MemoryLayout.structLayout( - Constants$root.C_CHAR$LAYOUT.withName("byte0"), - Constants$root.C_CHAR$LAYOUT.withName("byte1"), - Constants$root.C_CHAR$LAYOUT.withName("byte2"), - Constants$root.C_CHAR$LAYOUT.withName("byte3"), - Constants$root.C_CHAR$LAYOUT.withName("byte4"), - Constants$root.C_CHAR$LAYOUT.withName("byte5"), - Constants$root.C_CHAR$LAYOUT.withName("byte6"), - Constants$root.C_CHAR$LAYOUT.withName("byte7"), - Constants$root.C_CHAR$LAYOUT.withName("byte8"), - Constants$root.C_CHAR$LAYOUT.withName("byte9"), - Constants$root.C_CHAR$LAYOUT.withName("byte10"), - Constants$root.C_CHAR$LAYOUT.withName("byte11"), - Constants$root.C_CHAR$LAYOUT.withName("byte12"), - Constants$root.C_CHAR$LAYOUT.withName("byte13"), - Constants$root.C_CHAR$LAYOUT.withName("byte14"), - Constants$root.C_CHAR$LAYOUT.withName("byte15") - ), - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle CFUUIDGetUUIDBytes$MH = RuntimeHelper.downcallHandle( - "CFUUIDGetUUIDBytes", - constants$1.CFUUIDGetUUIDBytes$FUNC - ); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/constants$2.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/constants$2.java deleted file mode 100644 index 9558038b..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/corefoundation/constants$2.java +++ /dev/null @@ -1,37 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.macos.gen.corefoundation; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryLayout; -import java.lang.invoke.MethodHandle; -class constants$2 { - - static final FunctionDescriptor CFUUIDCreateFromUUIDBytes$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - MemoryLayout.structLayout( - Constants$root.C_CHAR$LAYOUT.withName("byte0"), - Constants$root.C_CHAR$LAYOUT.withName("byte1"), - Constants$root.C_CHAR$LAYOUT.withName("byte2"), - Constants$root.C_CHAR$LAYOUT.withName("byte3"), - Constants$root.C_CHAR$LAYOUT.withName("byte4"), - Constants$root.C_CHAR$LAYOUT.withName("byte5"), - Constants$root.C_CHAR$LAYOUT.withName("byte6"), - Constants$root.C_CHAR$LAYOUT.withName("byte7"), - Constants$root.C_CHAR$LAYOUT.withName("byte8"), - Constants$root.C_CHAR$LAYOUT.withName("byte9"), - Constants$root.C_CHAR$LAYOUT.withName("byte10"), - Constants$root.C_CHAR$LAYOUT.withName("byte11"), - Constants$root.C_CHAR$LAYOUT.withName("byte12"), - Constants$root.C_CHAR$LAYOUT.withName("byte13"), - Constants$root.C_CHAR$LAYOUT.withName("byte14"), - Constants$root.C_CHAR$LAYOUT.withName("byte15") - ) - ); - static final MethodHandle CFUUIDCreateFromUUIDBytes$MH = RuntimeHelper.downcallHandle( - "CFUUIDCreateFromUUIDBytes", - constants$2.CFUUIDCreateFromUUIDBytes$FUNC - ); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/CFUUIDBytes.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/CFUUIDBytes.java new file mode 100644 index 00000000..b1ab37eb --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/CFUUIDBytes.java @@ -0,0 +1,817 @@ +// Generated by jextract + +package net.codecrete.usb.macos.gen.iokit; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +/** + * {@snippet lang=c : + * struct { + * UInt8 byte0; + * UInt8 byte1; + * UInt8 byte2; + * UInt8 byte3; + * UInt8 byte4; + * UInt8 byte5; + * UInt8 byte6; + * UInt8 byte7; + * UInt8 byte8; + * UInt8 byte9; + * UInt8 byte10; + * UInt8 byte11; + * UInt8 byte12; + * UInt8 byte13; + * UInt8 byte14; + * UInt8 byte15; + * } + * } + */ +public class CFUUIDBytes { + + CFUUIDBytes() { + // Should not be called directly + } + + private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( + IOKit.C_CHAR.withName("byte0"), + IOKit.C_CHAR.withName("byte1"), + IOKit.C_CHAR.withName("byte2"), + IOKit.C_CHAR.withName("byte3"), + IOKit.C_CHAR.withName("byte4"), + IOKit.C_CHAR.withName("byte5"), + IOKit.C_CHAR.withName("byte6"), + IOKit.C_CHAR.withName("byte7"), + IOKit.C_CHAR.withName("byte8"), + IOKit.C_CHAR.withName("byte9"), + IOKit.C_CHAR.withName("byte10"), + IOKit.C_CHAR.withName("byte11"), + IOKit.C_CHAR.withName("byte12"), + IOKit.C_CHAR.withName("byte13"), + IOKit.C_CHAR.withName("byte14"), + IOKit.C_CHAR.withName("byte15") + ).withName("CFUUIDBytes"); + + /** + * The layout of this struct + */ + public static final GroupLayout layout() { + return $LAYOUT; + } + + private static final OfByte byte0$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte0")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte0 + * } + */ + public static final OfByte byte0$layout() { + return byte0$LAYOUT; + } + + private static final long byte0$OFFSET = $LAYOUT.byteOffset(groupElement("byte0")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte0 + * } + */ + public static final long byte0$offset() { + return byte0$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte0 + * } + */ + public static byte byte0(MemorySegment struct) { + return struct.get(byte0$LAYOUT, byte0$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte0 + * } + */ + public static void byte0(MemorySegment struct, byte fieldValue) { + struct.set(byte0$LAYOUT, byte0$OFFSET, fieldValue); + } + + private static final OfByte byte1$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte1")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte1 + * } + */ + public static final OfByte byte1$layout() { + return byte1$LAYOUT; + } + + private static final long byte1$OFFSET = $LAYOUT.byteOffset(groupElement("byte1")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte1 + * } + */ + public static final long byte1$offset() { + return byte1$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte1 + * } + */ + public static byte byte1(MemorySegment struct) { + return struct.get(byte1$LAYOUT, byte1$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte1 + * } + */ + public static void byte1(MemorySegment struct, byte fieldValue) { + struct.set(byte1$LAYOUT, byte1$OFFSET, fieldValue); + } + + private static final OfByte byte2$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte2")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte2 + * } + */ + public static final OfByte byte2$layout() { + return byte2$LAYOUT; + } + + private static final long byte2$OFFSET = $LAYOUT.byteOffset(groupElement("byte2")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte2 + * } + */ + public static final long byte2$offset() { + return byte2$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte2 + * } + */ + public static byte byte2(MemorySegment struct) { + return struct.get(byte2$LAYOUT, byte2$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte2 + * } + */ + public static void byte2(MemorySegment struct, byte fieldValue) { + struct.set(byte2$LAYOUT, byte2$OFFSET, fieldValue); + } + + private static final OfByte byte3$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte3")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte3 + * } + */ + public static final OfByte byte3$layout() { + return byte3$LAYOUT; + } + + private static final long byte3$OFFSET = $LAYOUT.byteOffset(groupElement("byte3")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte3 + * } + */ + public static final long byte3$offset() { + return byte3$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte3 + * } + */ + public static byte byte3(MemorySegment struct) { + return struct.get(byte3$LAYOUT, byte3$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte3 + * } + */ + public static void byte3(MemorySegment struct, byte fieldValue) { + struct.set(byte3$LAYOUT, byte3$OFFSET, fieldValue); + } + + private static final OfByte byte4$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte4")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte4 + * } + */ + public static final OfByte byte4$layout() { + return byte4$LAYOUT; + } + + private static final long byte4$OFFSET = $LAYOUT.byteOffset(groupElement("byte4")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte4 + * } + */ + public static final long byte4$offset() { + return byte4$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte4 + * } + */ + public static byte byte4(MemorySegment struct) { + return struct.get(byte4$LAYOUT, byte4$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte4 + * } + */ + public static void byte4(MemorySegment struct, byte fieldValue) { + struct.set(byte4$LAYOUT, byte4$OFFSET, fieldValue); + } + + private static final OfByte byte5$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte5")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte5 + * } + */ + public static final OfByte byte5$layout() { + return byte5$LAYOUT; + } + + private static final long byte5$OFFSET = $LAYOUT.byteOffset(groupElement("byte5")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte5 + * } + */ + public static final long byte5$offset() { + return byte5$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte5 + * } + */ + public static byte byte5(MemorySegment struct) { + return struct.get(byte5$LAYOUT, byte5$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte5 + * } + */ + public static void byte5(MemorySegment struct, byte fieldValue) { + struct.set(byte5$LAYOUT, byte5$OFFSET, fieldValue); + } + + private static final OfByte byte6$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte6")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte6 + * } + */ + public static final OfByte byte6$layout() { + return byte6$LAYOUT; + } + + private static final long byte6$OFFSET = $LAYOUT.byteOffset(groupElement("byte6")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte6 + * } + */ + public static final long byte6$offset() { + return byte6$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte6 + * } + */ + public static byte byte6(MemorySegment struct) { + return struct.get(byte6$LAYOUT, byte6$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte6 + * } + */ + public static void byte6(MemorySegment struct, byte fieldValue) { + struct.set(byte6$LAYOUT, byte6$OFFSET, fieldValue); + } + + private static final OfByte byte7$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte7")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte7 + * } + */ + public static final OfByte byte7$layout() { + return byte7$LAYOUT; + } + + private static final long byte7$OFFSET = $LAYOUT.byteOffset(groupElement("byte7")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte7 + * } + */ + public static final long byte7$offset() { + return byte7$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte7 + * } + */ + public static byte byte7(MemorySegment struct) { + return struct.get(byte7$LAYOUT, byte7$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte7 + * } + */ + public static void byte7(MemorySegment struct, byte fieldValue) { + struct.set(byte7$LAYOUT, byte7$OFFSET, fieldValue); + } + + private static final OfByte byte8$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte8")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte8 + * } + */ + public static final OfByte byte8$layout() { + return byte8$LAYOUT; + } + + private static final long byte8$OFFSET = $LAYOUT.byteOffset(groupElement("byte8")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte8 + * } + */ + public static final long byte8$offset() { + return byte8$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte8 + * } + */ + public static byte byte8(MemorySegment struct) { + return struct.get(byte8$LAYOUT, byte8$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte8 + * } + */ + public static void byte8(MemorySegment struct, byte fieldValue) { + struct.set(byte8$LAYOUT, byte8$OFFSET, fieldValue); + } + + private static final OfByte byte9$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte9")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte9 + * } + */ + public static final OfByte byte9$layout() { + return byte9$LAYOUT; + } + + private static final long byte9$OFFSET = $LAYOUT.byteOffset(groupElement("byte9")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte9 + * } + */ + public static final long byte9$offset() { + return byte9$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte9 + * } + */ + public static byte byte9(MemorySegment struct) { + return struct.get(byte9$LAYOUT, byte9$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte9 + * } + */ + public static void byte9(MemorySegment struct, byte fieldValue) { + struct.set(byte9$LAYOUT, byte9$OFFSET, fieldValue); + } + + private static final OfByte byte10$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte10")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte10 + * } + */ + public static final OfByte byte10$layout() { + return byte10$LAYOUT; + } + + private static final long byte10$OFFSET = $LAYOUT.byteOffset(groupElement("byte10")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte10 + * } + */ + public static final long byte10$offset() { + return byte10$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte10 + * } + */ + public static byte byte10(MemorySegment struct) { + return struct.get(byte10$LAYOUT, byte10$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte10 + * } + */ + public static void byte10(MemorySegment struct, byte fieldValue) { + struct.set(byte10$LAYOUT, byte10$OFFSET, fieldValue); + } + + private static final OfByte byte11$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte11")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte11 + * } + */ + public static final OfByte byte11$layout() { + return byte11$LAYOUT; + } + + private static final long byte11$OFFSET = $LAYOUT.byteOffset(groupElement("byte11")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte11 + * } + */ + public static final long byte11$offset() { + return byte11$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte11 + * } + */ + public static byte byte11(MemorySegment struct) { + return struct.get(byte11$LAYOUT, byte11$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte11 + * } + */ + public static void byte11(MemorySegment struct, byte fieldValue) { + struct.set(byte11$LAYOUT, byte11$OFFSET, fieldValue); + } + + private static final OfByte byte12$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte12")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte12 + * } + */ + public static final OfByte byte12$layout() { + return byte12$LAYOUT; + } + + private static final long byte12$OFFSET = $LAYOUT.byteOffset(groupElement("byte12")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte12 + * } + */ + public static final long byte12$offset() { + return byte12$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte12 + * } + */ + public static byte byte12(MemorySegment struct) { + return struct.get(byte12$LAYOUT, byte12$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte12 + * } + */ + public static void byte12(MemorySegment struct, byte fieldValue) { + struct.set(byte12$LAYOUT, byte12$OFFSET, fieldValue); + } + + private static final OfByte byte13$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte13")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte13 + * } + */ + public static final OfByte byte13$layout() { + return byte13$LAYOUT; + } + + private static final long byte13$OFFSET = $LAYOUT.byteOffset(groupElement("byte13")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte13 + * } + */ + public static final long byte13$offset() { + return byte13$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte13 + * } + */ + public static byte byte13(MemorySegment struct) { + return struct.get(byte13$LAYOUT, byte13$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte13 + * } + */ + public static void byte13(MemorySegment struct, byte fieldValue) { + struct.set(byte13$LAYOUT, byte13$OFFSET, fieldValue); + } + + private static final OfByte byte14$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte14")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte14 + * } + */ + public static final OfByte byte14$layout() { + return byte14$LAYOUT; + } + + private static final long byte14$OFFSET = $LAYOUT.byteOffset(groupElement("byte14")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte14 + * } + */ + public static final long byte14$offset() { + return byte14$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte14 + * } + */ + public static byte byte14(MemorySegment struct) { + return struct.get(byte14$LAYOUT, byte14$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte14 + * } + */ + public static void byte14(MemorySegment struct, byte fieldValue) { + struct.set(byte14$LAYOUT, byte14$OFFSET, fieldValue); + } + + private static final OfByte byte15$LAYOUT = (OfByte)$LAYOUT.select(groupElement("byte15")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 byte15 + * } + */ + public static final OfByte byte15$layout() { + return byte15$LAYOUT; + } + + private static final long byte15$OFFSET = $LAYOUT.byteOffset(groupElement("byte15")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 byte15 + * } + */ + public static final long byte15$offset() { + return byte15$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 byte15 + * } + */ + public static byte byte15(MemorySegment struct) { + return struct.get(byte15$LAYOUT, byte15$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 byte15 + * } + */ + public static void byte15(MemorySegment struct, byte fieldValue) { + struct.set(byte15$LAYOUT, byte15$OFFSET, fieldValue); + } + + /** + * Obtains a slice of {@code arrayParam} which selects the array element at {@code index}. + * The returned segment has address {@code arrayParam.address() + index * layout().byteSize()} + */ + public static MemorySegment asSlice(MemorySegment array, long index) { + return array.asSlice(layout().byteSize() * index); + } + + /** + * The size (in bytes) of this struct + */ + public static long sizeof() { return layout().byteSize(); } + + /** + * Allocate a segment of size {@code layout().byteSize()} using {@code allocator} + */ + public static MemorySegment allocate(SegmentAllocator allocator) { + return allocator.allocate(layout()); + } + + /** + * Allocate an array of size {@code elementCount} using {@code allocator}. + * The returned segment has size {@code elementCount * layout().byteSize()}. + */ + public static MemorySegment allocateArray(long elementCount, SegmentAllocator allocator) { + return allocator.allocate(MemoryLayout.sequenceLayout(elementCount, layout())); + } + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, Arena arena, Consumer cleanup) { + return reinterpret(addr, 1, arena, cleanup); + } + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code elementCount * layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, long elementCount, Arena arena, Consumer cleanup) { + return addr.reinterpret(layout().byteSize() * elementCount, arena, cleanup); + } +} + diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/Constants$root.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/Constants$root.java deleted file mode 100644 index baa3d26a..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/Constants$root.java +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.macos.gen.iokit; - -import static java.lang.foreign.ValueLayout.*; -public class Constants$root { - - static final OfBoolean C_BOOL$LAYOUT = JAVA_BOOLEAN; - static final OfByte C_CHAR$LAYOUT = JAVA_BYTE; - static final OfShort C_SHORT$LAYOUT = JAVA_SHORT.withBitAlignment(16); - static final OfInt C_INT$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfLong C_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfLong C_LONG_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfFloat C_FLOAT$LAYOUT = JAVA_FLOAT.withBitAlignment(32); - static final OfDouble C_DOUBLE$LAYOUT = JAVA_DOUBLE.withBitAlignment(64); - static final OfAddress C_POINTER$LAYOUT = ADDRESS.withBitAlignment(64); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOCFPlugInInterface.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOCFPlugInInterface.java deleted file mode 100644 index 401ce8fc..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOCFPlugInInterface.java +++ /dev/null @@ -1,9 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.macos.gen.iokit; - -public class IOCFPlugInInterface extends IOCFPlugInInterfaceStruct { - -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOCFPlugInInterfaceStruct.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOCFPlugInInterfaceStruct.java index 5f9e0c79..74f3414d 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOCFPlugInInterfaceStruct.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOCFPlugInInterfaceStruct.java @@ -2,362 +2,760 @@ package net.codecrete.usb.macos.gen.iokit; +import java.lang.invoke.*; import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +/** + * {@snippet lang=c : + * struct IOCFPlugInInterfaceStruct { + * void *_reserved; + * HRESULT (*QueryInterface)(void *, REFIID, LPVOID *); + * ULONG (*AddRef)(void *); + * ULONG (*Release)(void *); + * UInt16 version; + * UInt16 revision; + * IOReturn (*Probe)(void *, CFDictionaryRef, io_service_t, SInt32 *); + * IOReturn (*Start)(void *, CFDictionaryRef, io_service_t); + * IOReturn (*Stop)(void *); + * } + * } + */ public class IOCFPlugInInterfaceStruct { - static final GroupLayout $struct$LAYOUT = MemoryLayout.structLayout( - Constants$root.C_POINTER$LAYOUT.withName("_reserved"), - Constants$root.C_POINTER$LAYOUT.withName("QueryInterface"), - Constants$root.C_POINTER$LAYOUT.withName("AddRef"), - Constants$root.C_POINTER$LAYOUT.withName("Release"), - Constants$root.C_SHORT$LAYOUT.withName("version"), - Constants$root.C_SHORT$LAYOUT.withName("revision"), - MemoryLayout.paddingLayout(32), - Constants$root.C_POINTER$LAYOUT.withName("Probe"), - Constants$root.C_POINTER$LAYOUT.withName("Start"), - Constants$root.C_POINTER$LAYOUT.withName("Stop") - ).withName("IOCFPlugInInterfaceStruct"); - public static MemoryLayout $LAYOUT() { - return IOCFPlugInInterfaceStruct.$struct$LAYOUT; - } - static final VarHandle _reserved$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("_reserved")); - public static VarHandle _reserved$VH() { - return IOCFPlugInInterfaceStruct._reserved$VH; - } - public static MemoryAddress _reserved$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOCFPlugInInterfaceStruct._reserved$VH.get(seg); - } - public static void _reserved$set( MemorySegment seg, MemoryAddress x) { - IOCFPlugInInterfaceStruct._reserved$VH.set(seg, x); - } - public static MemoryAddress _reserved$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOCFPlugInInterfaceStruct._reserved$VH.get(seg.asSlice(index*sizeof())); - } - public static void _reserved$set(MemorySegment seg, long index, MemoryAddress x) { - IOCFPlugInInterfaceStruct._reserved$VH.set(seg.asSlice(index*sizeof()), x); - } - static final FunctionDescriptor QueryInterface$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - MemoryLayout.structLayout( - Constants$root.C_CHAR$LAYOUT.withName("byte0"), - Constants$root.C_CHAR$LAYOUT.withName("byte1"), - Constants$root.C_CHAR$LAYOUT.withName("byte2"), - Constants$root.C_CHAR$LAYOUT.withName("byte3"), - Constants$root.C_CHAR$LAYOUT.withName("byte4"), - Constants$root.C_CHAR$LAYOUT.withName("byte5"), - Constants$root.C_CHAR$LAYOUT.withName("byte6"), - Constants$root.C_CHAR$LAYOUT.withName("byte7"), - Constants$root.C_CHAR$LAYOUT.withName("byte8"), - Constants$root.C_CHAR$LAYOUT.withName("byte9"), - Constants$root.C_CHAR$LAYOUT.withName("byte10"), - Constants$root.C_CHAR$LAYOUT.withName("byte11"), - Constants$root.C_CHAR$LAYOUT.withName("byte12"), - Constants$root.C_CHAR$LAYOUT.withName("byte13"), - Constants$root.C_CHAR$LAYOUT.withName("byte14"), - Constants$root.C_CHAR$LAYOUT.withName("byte15") - ), - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle QueryInterface$MH = RuntimeHelper.downcallHandle( - IOCFPlugInInterfaceStruct.QueryInterface$FUNC - ); - public interface QueryInterface { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemorySegment _x1, java.lang.foreign.MemoryAddress _x2); - static MemorySegment allocate(QueryInterface fi, MemorySession session) { - return RuntimeHelper.upcallStub(QueryInterface.class, fi, IOCFPlugInInterfaceStruct.QueryInterface$FUNC, session); - } - static QueryInterface ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemorySegment __x1, java.lang.foreign.MemoryAddress __x2) -> { - try { - return (int)IOCFPlugInInterfaceStruct.QueryInterface$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } + IOCFPlugInInterfaceStruct() { + // Should not be called directly } - static final VarHandle QueryInterface$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("QueryInterface")); - public static VarHandle QueryInterface$VH() { - return IOCFPlugInInterfaceStruct.QueryInterface$VH; - } - public static MemoryAddress QueryInterface$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOCFPlugInInterfaceStruct.QueryInterface$VH.get(seg); + private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( + IOKit.C_POINTER.withName("_reserved"), + IOKit.C_POINTER.withName("QueryInterface"), + IOKit.C_POINTER.withName("AddRef"), + IOKit.C_POINTER.withName("Release"), + IOKit.C_SHORT.withName("version"), + IOKit.C_SHORT.withName("revision"), + MemoryLayout.paddingLayout(4), + IOKit.C_POINTER.withName("Probe"), + IOKit.C_POINTER.withName("Start"), + IOKit.C_POINTER.withName("Stop") + ).withName("IOCFPlugInInterfaceStruct"); + + /** + * The layout of this struct + */ + public static final GroupLayout layout() { + return $LAYOUT; } - public static void QueryInterface$set( MemorySegment seg, MemoryAddress x) { - IOCFPlugInInterfaceStruct.QueryInterface$VH.set(seg, x); + + private static final AddressLayout _reserved$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("_reserved")); + + /** + * Layout for field: + * {@snippet lang=c : + * void *_reserved + * } + */ + public static final AddressLayout _reserved$layout() { + return _reserved$LAYOUT; } - public static MemoryAddress QueryInterface$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOCFPlugInInterfaceStruct.QueryInterface$VH.get(seg.asSlice(index*sizeof())); + + private static final long _reserved$OFFSET = $LAYOUT.byteOffset(groupElement("_reserved")); + + /** + * Offset for field: + * {@snippet lang=c : + * void *_reserved + * } + */ + public static final long _reserved$offset() { + return _reserved$OFFSET; } - public static void QueryInterface$set(MemorySegment seg, long index, MemoryAddress x) { - IOCFPlugInInterfaceStruct.QueryInterface$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Getter for field: + * {@snippet lang=c : + * void *_reserved + * } + */ + public static MemorySegment _reserved(MemorySegment struct) { + return struct.get(_reserved$LAYOUT, _reserved$OFFSET); } - public static QueryInterface QueryInterface (MemorySegment segment, MemorySession session) { - return QueryInterface.ofAddress(QueryInterface$get(segment), session); + + /** + * Setter for field: + * {@snippet lang=c : + * void *_reserved + * } + */ + public static void _reserved(MemorySegment struct, MemorySegment fieldValue) { + struct.set(_reserved$LAYOUT, _reserved$OFFSET, fieldValue); } - static final FunctionDescriptor AddRef$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle AddRef$MH = RuntimeHelper.downcallHandle( - IOCFPlugInInterfaceStruct.AddRef$FUNC - ); - public interface AddRef { - int apply(java.lang.foreign.MemoryAddress _x0); - static MemorySegment allocate(AddRef fi, MemorySession session) { - return RuntimeHelper.upcallStub(AddRef.class, fi, IOCFPlugInInterfaceStruct.AddRef$FUNC, session); + /** + * {@snippet lang=c : + * HRESULT (*QueryInterface)(void *, REFIID, LPVOID *) + * } + */ + public final static class QueryInterface { + + private QueryInterface() { + // Should not be called directly } - static AddRef ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0) -> { - try { - return (int)IOCFPlugInInterfaceStruct.AddRef$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + CFUUIDBytes.layout(), + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; } - } - static final VarHandle AddRef$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("AddRef")); - public static VarHandle AddRef$VH() { - return IOCFPlugInInterfaceStruct.AddRef$VH; - } - public static MemoryAddress AddRef$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOCFPlugInInterfaceStruct.AddRef$VH.get(seg); + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1, MemorySegment _x2) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } } - public static void AddRef$set( MemorySegment seg, MemoryAddress x) { - IOCFPlugInInterfaceStruct.AddRef$VH.set(seg, x); + + private static final AddressLayout QueryInterface$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("QueryInterface")); + + /** + * Layout for field: + * {@snippet lang=c : + * HRESULT (*QueryInterface)(void *, REFIID, LPVOID *) + * } + */ + public static final AddressLayout QueryInterface$layout() { + return QueryInterface$LAYOUT; } - public static MemoryAddress AddRef$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOCFPlugInInterfaceStruct.AddRef$VH.get(seg.asSlice(index*sizeof())); + + private static final long QueryInterface$OFFSET = $LAYOUT.byteOffset(groupElement("QueryInterface")); + + /** + * Offset for field: + * {@snippet lang=c : + * HRESULT (*QueryInterface)(void *, REFIID, LPVOID *) + * } + */ + public static final long QueryInterface$offset() { + return QueryInterface$OFFSET; } - public static void AddRef$set(MemorySegment seg, long index, MemoryAddress x) { - IOCFPlugInInterfaceStruct.AddRef$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Getter for field: + * {@snippet lang=c : + * HRESULT (*QueryInterface)(void *, REFIID, LPVOID *) + * } + */ + public static MemorySegment QueryInterface(MemorySegment struct) { + return struct.get(QueryInterface$LAYOUT, QueryInterface$OFFSET); } - public static AddRef AddRef (MemorySegment segment, MemorySession session) { - return AddRef.ofAddress(AddRef$get(segment), session); + + /** + * Setter for field: + * {@snippet lang=c : + * HRESULT (*QueryInterface)(void *, REFIID, LPVOID *) + * } + */ + public static void QueryInterface(MemorySegment struct, MemorySegment fieldValue) { + struct.set(QueryInterface$LAYOUT, QueryInterface$OFFSET, fieldValue); } - static final FunctionDescriptor Release$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle Release$MH = RuntimeHelper.downcallHandle( - IOCFPlugInInterfaceStruct.Release$FUNC - ); - public interface Release { - int apply(java.lang.foreign.MemoryAddress _x0); - static MemorySegment allocate(Release fi, MemorySession session) { - return RuntimeHelper.upcallStub(Release.class, fi, IOCFPlugInInterfaceStruct.Release$FUNC, session); + /** + * {@snippet lang=c : + * ULONG (*AddRef)(void *) + * } + */ + public final static class AddRef { + + private AddRef() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; } - static Release ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0) -> { - try { - return (int)IOCFPlugInInterfaceStruct.Release$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } } } - static final VarHandle Release$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("Release")); - public static VarHandle Release$VH() { - return IOCFPlugInInterfaceStruct.Release$VH; + private static final AddressLayout AddRef$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("AddRef")); + + /** + * Layout for field: + * {@snippet lang=c : + * ULONG (*AddRef)(void *) + * } + */ + public static final AddressLayout AddRef$layout() { + return AddRef$LAYOUT; } - public static MemoryAddress Release$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOCFPlugInInterfaceStruct.Release$VH.get(seg); + + private static final long AddRef$OFFSET = $LAYOUT.byteOffset(groupElement("AddRef")); + + /** + * Offset for field: + * {@snippet lang=c : + * ULONG (*AddRef)(void *) + * } + */ + public static final long AddRef$offset() { + return AddRef$OFFSET; } - public static void Release$set( MemorySegment seg, MemoryAddress x) { - IOCFPlugInInterfaceStruct.Release$VH.set(seg, x); + + /** + * Getter for field: + * {@snippet lang=c : + * ULONG (*AddRef)(void *) + * } + */ + public static MemorySegment AddRef(MemorySegment struct) { + return struct.get(AddRef$LAYOUT, AddRef$OFFSET); } - public static MemoryAddress Release$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOCFPlugInInterfaceStruct.Release$VH.get(seg.asSlice(index*sizeof())); + + /** + * Setter for field: + * {@snippet lang=c : + * ULONG (*AddRef)(void *) + * } + */ + public static void AddRef(MemorySegment struct, MemorySegment fieldValue) { + struct.set(AddRef$LAYOUT, AddRef$OFFSET, fieldValue); } - public static void Release$set(MemorySegment seg, long index, MemoryAddress x) { - IOCFPlugInInterfaceStruct.Release$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * {@snippet lang=c : + * ULONG (*Release)(void *) + * } + */ + public final static class Release { + + private Release() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } } - public static Release Release (MemorySegment segment, MemorySession session) { - return Release.ofAddress(Release$get(segment), session); + + private static final AddressLayout Release$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("Release")); + + /** + * Layout for field: + * {@snippet lang=c : + * ULONG (*Release)(void *) + * } + */ + public static final AddressLayout Release$layout() { + return Release$LAYOUT; } - static final VarHandle version$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("version")); - public static VarHandle version$VH() { - return IOCFPlugInInterfaceStruct.version$VH; + + private static final long Release$OFFSET = $LAYOUT.byteOffset(groupElement("Release")); + + /** + * Offset for field: + * {@snippet lang=c : + * ULONG (*Release)(void *) + * } + */ + public static final long Release$offset() { + return Release$OFFSET; } - public static short version$get(MemorySegment seg) { - return (short)IOCFPlugInInterfaceStruct.version$VH.get(seg); + + /** + * Getter for field: + * {@snippet lang=c : + * ULONG (*Release)(void *) + * } + */ + public static MemorySegment Release(MemorySegment struct) { + return struct.get(Release$LAYOUT, Release$OFFSET); } - public static void version$set( MemorySegment seg, short x) { - IOCFPlugInInterfaceStruct.version$VH.set(seg, x); + + /** + * Setter for field: + * {@snippet lang=c : + * ULONG (*Release)(void *) + * } + */ + public static void Release(MemorySegment struct, MemorySegment fieldValue) { + struct.set(Release$LAYOUT, Release$OFFSET, fieldValue); + } + + private static final OfShort version$LAYOUT = (OfShort)$LAYOUT.select(groupElement("version")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt16 version + * } + */ + public static final OfShort version$layout() { + return version$LAYOUT; } - public static short version$get(MemorySegment seg, long index) { - return (short)IOCFPlugInInterfaceStruct.version$VH.get(seg.asSlice(index*sizeof())); + + private static final long version$OFFSET = $LAYOUT.byteOffset(groupElement("version")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt16 version + * } + */ + public static final long version$offset() { + return version$OFFSET; } - public static void version$set(MemorySegment seg, long index, short x) { - IOCFPlugInInterfaceStruct.version$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Getter for field: + * {@snippet lang=c : + * UInt16 version + * } + */ + public static short version(MemorySegment struct) { + return struct.get(version$LAYOUT, version$OFFSET); } - static final VarHandle revision$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("revision")); - public static VarHandle revision$VH() { - return IOCFPlugInInterfaceStruct.revision$VH; + + /** + * Setter for field: + * {@snippet lang=c : + * UInt16 version + * } + */ + public static void version(MemorySegment struct, short fieldValue) { + struct.set(version$LAYOUT, version$OFFSET, fieldValue); } - public static short revision$get(MemorySegment seg) { - return (short)IOCFPlugInInterfaceStruct.revision$VH.get(seg); + + private static final OfShort revision$LAYOUT = (OfShort)$LAYOUT.select(groupElement("revision")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt16 revision + * } + */ + public static final OfShort revision$layout() { + return revision$LAYOUT; } - public static void revision$set( MemorySegment seg, short x) { - IOCFPlugInInterfaceStruct.revision$VH.set(seg, x); + + private static final long revision$OFFSET = $LAYOUT.byteOffset(groupElement("revision")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt16 revision + * } + */ + public static final long revision$offset() { + return revision$OFFSET; } - public static short revision$get(MemorySegment seg, long index) { - return (short)IOCFPlugInInterfaceStruct.revision$VH.get(seg.asSlice(index*sizeof())); + + /** + * Getter for field: + * {@snippet lang=c : + * UInt16 revision + * } + */ + public static short revision(MemorySegment struct) { + return struct.get(revision$LAYOUT, revision$OFFSET); } - public static void revision$set(MemorySegment seg, long index, short x) { - IOCFPlugInInterfaceStruct.revision$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Setter for field: + * {@snippet lang=c : + * UInt16 revision + * } + */ + public static void revision(MemorySegment struct, short fieldValue) { + struct.set(revision$LAYOUT, revision$OFFSET, fieldValue); } - static final FunctionDescriptor Probe$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle Probe$MH = RuntimeHelper.downcallHandle( - IOCFPlugInInterfaceStruct.Probe$FUNC - ); - public interface Probe { - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1, int _x2, java.lang.foreign.MemoryAddress _x3); - static MemorySegment allocate(Probe fi, MemorySession session) { - return RuntimeHelper.upcallStub(Probe.class, fi, IOCFPlugInInterfaceStruct.Probe$FUNC, session); + /** + * {@snippet lang=c : + * IOReturn (*Probe)(void *, CFDictionaryRef, io_service_t, SInt32 *) + * } + */ + public final static class Probe { + + private Probe() { + // Should not be called directly } - static Probe ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1, int __x2, java.lang.foreign.MemoryAddress __x3) -> { - try { - return (int)IOCFPlugInInterfaceStruct.Probe$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1, __x2, (java.lang.foreign.Addressable)__x3); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_INT, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; } - } - static final VarHandle Probe$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("Probe")); - public static VarHandle Probe$VH() { - return IOCFPlugInInterfaceStruct.Probe$VH; - } - public static MemoryAddress Probe$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOCFPlugInInterfaceStruct.Probe$VH.get(seg); + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1, int _x2, MemorySegment _x3) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2, _x3); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } } - public static void Probe$set( MemorySegment seg, MemoryAddress x) { - IOCFPlugInInterfaceStruct.Probe$VH.set(seg, x); + + private static final AddressLayout Probe$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("Probe")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*Probe)(void *, CFDictionaryRef, io_service_t, SInt32 *) + * } + */ + public static final AddressLayout Probe$layout() { + return Probe$LAYOUT; } - public static MemoryAddress Probe$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOCFPlugInInterfaceStruct.Probe$VH.get(seg.asSlice(index*sizeof())); + + private static final long Probe$OFFSET = $LAYOUT.byteOffset(groupElement("Probe")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*Probe)(void *, CFDictionaryRef, io_service_t, SInt32 *) + * } + */ + public static final long Probe$offset() { + return Probe$OFFSET; } - public static void Probe$set(MemorySegment seg, long index, MemoryAddress x) { - IOCFPlugInInterfaceStruct.Probe$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*Probe)(void *, CFDictionaryRef, io_service_t, SInt32 *) + * } + */ + public static MemorySegment Probe(MemorySegment struct) { + return struct.get(Probe$LAYOUT, Probe$OFFSET); } - public static Probe Probe (MemorySegment segment, MemorySession session) { - return Probe.ofAddress(Probe$get(segment), session); + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*Probe)(void *, CFDictionaryRef, io_service_t, SInt32 *) + * } + */ + public static void Probe(MemorySegment struct, MemorySegment fieldValue) { + struct.set(Probe$LAYOUT, Probe$OFFSET, fieldValue); } - static final FunctionDescriptor Start$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT - ); - static final MethodHandle Start$MH = RuntimeHelper.downcallHandle( - IOCFPlugInInterfaceStruct.Start$FUNC - ); - public interface Start { - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1, int _x2); - static MemorySegment allocate(Start fi, MemorySession session) { - return RuntimeHelper.upcallStub(Start.class, fi, IOCFPlugInInterfaceStruct.Start$FUNC, session); + /** + * {@snippet lang=c : + * IOReturn (*Start)(void *, CFDictionaryRef, io_service_t) + * } + */ + public final static class Start { + + private Start() { + // Should not be called directly } - static Start ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1, int __x2) -> { - try { - return (int)IOCFPlugInInterfaceStruct.Start$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1, __x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_INT + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; } - } - static final VarHandle Start$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("Start")); - public static VarHandle Start$VH() { - return IOCFPlugInInterfaceStruct.Start$VH; - } - public static MemoryAddress Start$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOCFPlugInInterfaceStruct.Start$VH.get(seg); + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1, int _x2) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } } - public static void Start$set( MemorySegment seg, MemoryAddress x) { - IOCFPlugInInterfaceStruct.Start$VH.set(seg, x); + + private static final AddressLayout Start$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("Start")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*Start)(void *, CFDictionaryRef, io_service_t) + * } + */ + public static final AddressLayout Start$layout() { + return Start$LAYOUT; } - public static MemoryAddress Start$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOCFPlugInInterfaceStruct.Start$VH.get(seg.asSlice(index*sizeof())); + + private static final long Start$OFFSET = $LAYOUT.byteOffset(groupElement("Start")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*Start)(void *, CFDictionaryRef, io_service_t) + * } + */ + public static final long Start$offset() { + return Start$OFFSET; } - public static void Start$set(MemorySegment seg, long index, MemoryAddress x) { - IOCFPlugInInterfaceStruct.Start$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*Start)(void *, CFDictionaryRef, io_service_t) + * } + */ + public static MemorySegment Start(MemorySegment struct) { + return struct.get(Start$LAYOUT, Start$OFFSET); } - public static Start Start (MemorySegment segment, MemorySession session) { - return Start.ofAddress(Start$get(segment), session); + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*Start)(void *, CFDictionaryRef, io_service_t) + * } + */ + public static void Start(MemorySegment struct, MemorySegment fieldValue) { + struct.set(Start$LAYOUT, Start$OFFSET, fieldValue); } - static final FunctionDescriptor Stop$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle Stop$MH = RuntimeHelper.downcallHandle( - IOCFPlugInInterfaceStruct.Stop$FUNC - ); - public interface Stop { - int apply(java.lang.foreign.MemoryAddress _x0); - static MemorySegment allocate(Stop fi, MemorySession session) { - return RuntimeHelper.upcallStub(Stop.class, fi, IOCFPlugInInterfaceStruct.Stop$FUNC, session); + /** + * {@snippet lang=c : + * IOReturn (*Stop)(void *) + * } + */ + public final static class Stop { + + private Stop() { + // Should not be called directly } - static Stop ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0) -> { - try { - return (int)IOCFPlugInInterfaceStruct.Stop$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } } } - static final VarHandle Stop$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("Stop")); - public static VarHandle Stop$VH() { - return IOCFPlugInInterfaceStruct.Stop$VH; + private static final AddressLayout Stop$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("Stop")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*Stop)(void *) + * } + */ + public static final AddressLayout Stop$layout() { + return Stop$LAYOUT; } - public static MemoryAddress Stop$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOCFPlugInInterfaceStruct.Stop$VH.get(seg); + + private static final long Stop$OFFSET = $LAYOUT.byteOffset(groupElement("Stop")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*Stop)(void *) + * } + */ + public static final long Stop$offset() { + return Stop$OFFSET; } - public static void Stop$set( MemorySegment seg, MemoryAddress x) { - IOCFPlugInInterfaceStruct.Stop$VH.set(seg, x); + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*Stop)(void *) + * } + */ + public static MemorySegment Stop(MemorySegment struct) { + return struct.get(Stop$LAYOUT, Stop$OFFSET); } - public static MemoryAddress Stop$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOCFPlugInInterfaceStruct.Stop$VH.get(seg.asSlice(index*sizeof())); + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*Stop)(void *) + * } + */ + public static void Stop(MemorySegment struct, MemorySegment fieldValue) { + struct.set(Stop$LAYOUT, Stop$OFFSET, fieldValue); } - public static void Stop$set(MemorySegment seg, long index, MemoryAddress x) { - IOCFPlugInInterfaceStruct.Stop$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Obtains a slice of {@code arrayParam} which selects the array element at {@code index}. + * The returned segment has address {@code arrayParam.address() + index * layout().byteSize()} + */ + public static MemorySegment asSlice(MemorySegment array, long index) { + return array.asSlice(layout().byteSize() * index); } - public static Stop Stop (MemorySegment segment, MemorySession session) { - return Stop.ofAddress(Stop$get(segment), session); + + /** + * The size (in bytes) of this struct + */ + public static long sizeof() { return layout().byteSize(); } + + /** + * Allocate a segment of size {@code layout().byteSize()} using {@code allocator} + */ + public static MemorySegment allocate(SegmentAllocator allocator) { + return allocator.allocate(layout()); } - public static long sizeof() { return $LAYOUT().byteSize(); } - public static MemorySegment allocate(SegmentAllocator allocator) { return allocator.allocate($LAYOUT()); } - public static MemorySegment allocateArray(int len, SegmentAllocator allocator) { - return allocator.allocate(MemoryLayout.sequenceLayout(len, $LAYOUT())); + + /** + * Allocate an array of size {@code elementCount} using {@code allocator}. + * The returned segment has size {@code elementCount * layout().byteSize()}. + */ + public static MemorySegment allocateArray(long elementCount, SegmentAllocator allocator) { + return allocator.allocate(MemoryLayout.sequenceLayout(elementCount, layout())); } - public static MemorySegment ofAddress(MemoryAddress addr, MemorySession session) { return RuntimeHelper.asArray(addr, $LAYOUT(), 1, session); } -} + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, Arena arena, Consumer cleanup) { + return reinterpret(addr, 1, arena, cleanup); + } + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code elementCount * layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, long elementCount, Arena arena, Consumer cleanup) { + return addr.reinterpret(layout().byteSize() * elementCount, arena, cleanup); + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOKit$shared.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOKit$shared.java new file mode 100644 index 00000000..c3328897 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOKit$shared.java @@ -0,0 +1,63 @@ +// Generated by jextract + +package net.codecrete.usb.macos.gen.iokit; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +public class IOKit$shared { + + IOKit$shared() { + // Should not be called directly + } + + public static final ValueLayout.OfBoolean C_BOOL = (ValueLayout.OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool"); + public static final ValueLayout.OfByte C_CHAR =(ValueLayout.OfByte)Linker.nativeLinker().canonicalLayouts().get("char"); + public static final ValueLayout.OfShort C_SHORT = (ValueLayout.OfShort) Linker.nativeLinker().canonicalLayouts().get("short"); + public static final ValueLayout.OfInt C_INT = (ValueLayout.OfInt) Linker.nativeLinker().canonicalLayouts().get("int"); + public static final ValueLayout.OfLong C_LONG_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long long"); + public static final ValueLayout.OfFloat C_FLOAT = (ValueLayout.OfFloat) Linker.nativeLinker().canonicalLayouts().get("float"); + public static final ValueLayout.OfDouble C_DOUBLE = (ValueLayout.OfDouble) Linker.nativeLinker().canonicalLayouts().get("double"); + public static final AddressLayout C_POINTER = ((AddressLayout) Linker.nativeLinker().canonicalLayouts().get("void*")) + .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, C_CHAR)); + public static final ValueLayout.OfLong C_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long"); + + static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + + static void traceDowncall(String name, Object... args) { + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("%s(%s)\n", name, traceArgs); + } + + static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { + try { + return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } + + static MemoryLayout align(MemoryLayout layout, long align) { + return switch (layout) { + case PaddingLayout p -> p; + case ValueLayout v -> v.withByteAlignment(align); + case GroupLayout g -> { + MemoryLayout[] alignedMembers = g.memberLayouts().stream() + .map(m -> align(m, align)).toArray(MemoryLayout[]::new); + yield g instanceof StructLayout ? + MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); + } + case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); + }; + } +} + diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOKit.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOKit.java index 59ebcf8b..0eb5e6dc 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOKit.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOKit.java @@ -2,166 +2,769 @@ package net.codecrete.usb.macos.gen.iokit; -import java.lang.foreign.Addressable; -import java.lang.foreign.MemoryAddress; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemorySegment; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.VarHandle; +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; import static java.lang.foreign.ValueLayout.*; -public class IOKit { - - /* package-private */ IOKit() {} - public static OfByte C_CHAR = Constants$root.C_CHAR$LAYOUT; - public static OfShort C_SHORT = Constants$root.C_SHORT$LAYOUT; - public static OfInt C_INT = Constants$root.C_INT$LAYOUT; - public static OfLong C_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfLong C_LONG_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfFloat C_FLOAT = Constants$root.C_FLOAT$LAYOUT; - public static OfDouble C_DOUBLE = Constants$root.C_DOUBLE$LAYOUT; - public static OfAddress C_POINTER = Constants$root.C_POINTER$LAYOUT; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +public class IOKit extends IOKit$shared { + + IOKit() { + // Should not be called directly + } + + static final Arena LIBRARY_ARENA = Arena.ofAuto(); + + static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.libraryLookup("/System/Library/Frameworks/IOKit.framework/IOKit", LIBRARY_ARENA) + .or(SymbolLookup.loaderLookup()) + .or(Linker.nativeLinker().defaultLookup()); + + private static final int kIOUSBFindInterfaceDontCare = (int)65535L; + /** + * {@snippet lang=c : + * enum enum (unnamed at /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/IOKit.framework/Headers/usb/USB.h:898:1).kIOUSBFindInterfaceDontCare = 65535 + * } + */ public static int kIOUSBFindInterfaceDontCare() { - return (int)65535L; + return kIOUSBFindInterfaceDontCare; + } + private static final int kUSBReEnumerateCaptureDeviceMask = (int)1073741824L; + /** + * {@snippet lang=c : + * enum USBReEnumerateOptions.kUSBReEnumerateCaptureDeviceMask = 1073741824 + * } + */ + public static int kUSBReEnumerateCaptureDeviceMask() { + return kUSBReEnumerateCaptureDeviceMask; + } + private static final int kUSBReEnumerateReleaseDeviceMask = (int)536870912L; + /** + * {@snippet lang=c : + * enum USBReEnumerateOptions.kUSBReEnumerateReleaseDeviceMask = 536870912 + * } + */ + public static int kUSBReEnumerateReleaseDeviceMask() { + return kUSBReEnumerateReleaseDeviceMask; + } + + private static class kCFRunLoopDefaultMode$constants { + public static final AddressLayout LAYOUT = IOKit.C_POINTER; + public static final MemorySegment SEGMENT = SYMBOL_LOOKUP.findOrThrow("kCFRunLoopDefaultMode").reinterpret(LAYOUT.byteSize()); + } + + /** + * Layout for variable: + * {@snippet lang=c : + * extern const CFRunLoopMode kCFRunLoopDefaultMode + * } + */ + public static AddressLayout kCFRunLoopDefaultMode$layout() { + return kCFRunLoopDefaultMode$constants.LAYOUT; } - public static MemoryLayout kCFRunLoopDefaultMode$LAYOUT() { - return constants$0.kCFRunLoopDefaultMode$LAYOUT; + + /** + * Segment for variable: + * {@snippet lang=c : + * extern const CFRunLoopMode kCFRunLoopDefaultMode + * } + */ + public static MemorySegment kCFRunLoopDefaultMode$segment() { + return kCFRunLoopDefaultMode$constants.SEGMENT; } - public static VarHandle kCFRunLoopDefaultMode$VH() { - return constants$0.kCFRunLoopDefaultMode$VH; + + /** + * Getter for variable: + * {@snippet lang=c : + * extern const CFRunLoopMode kCFRunLoopDefaultMode + * } + */ + public static MemorySegment kCFRunLoopDefaultMode() { + return kCFRunLoopDefaultMode$constants.SEGMENT.get(kCFRunLoopDefaultMode$constants.LAYOUT, 0L); + } + + /** + * Setter for variable: + * {@snippet lang=c : + * extern const CFRunLoopMode kCFRunLoopDefaultMode + * } + */ + public static void kCFRunLoopDefaultMode(MemorySegment varValue) { + kCFRunLoopDefaultMode$constants.SEGMENT.set(kCFRunLoopDefaultMode$constants.LAYOUT, 0L, varValue); } - public static MemorySegment kCFRunLoopDefaultMode$SEGMENT() { - return RuntimeHelper.requireNonNull(constants$0.kCFRunLoopDefaultMode$SEGMENT,"kCFRunLoopDefaultMode"); + + private static class kIOMasterPortDefault$constants { + public static final OfInt LAYOUT = IOKit.C_INT; + public static final MemorySegment SEGMENT = SYMBOL_LOOKUP.findOrThrow("kIOMasterPortDefault").reinterpret(LAYOUT.byteSize()); } - public static MemoryAddress kCFRunLoopDefaultMode$get() { - return (java.lang.foreign.MemoryAddress) constants$0.kCFRunLoopDefaultMode$VH.get(RuntimeHelper.requireNonNull(constants$0.kCFRunLoopDefaultMode$SEGMENT, "kCFRunLoopDefaultMode")); + + /** + * Layout for variable: + * {@snippet lang=c : + * extern const mach_port_t kIOMasterPortDefault + * } + */ + public static OfInt kIOMasterPortDefault$layout() { + return kIOMasterPortDefault$constants.LAYOUT; } - public static void kCFRunLoopDefaultMode$set( MemoryAddress x) { - constants$0.kCFRunLoopDefaultMode$VH.set(RuntimeHelper.requireNonNull(constants$0.kCFRunLoopDefaultMode$SEGMENT, "kCFRunLoopDefaultMode"), x); + + /** + * Segment for variable: + * {@snippet lang=c : + * extern const mach_port_t kIOMasterPortDefault + * } + */ + public static MemorySegment kIOMasterPortDefault$segment() { + return kIOMasterPortDefault$constants.SEGMENT; } - public static MemoryLayout kIOMasterPortDefault$LAYOUT() { - return constants$0.kIOMasterPortDefault$LAYOUT; + + /** + * Getter for variable: + * {@snippet lang=c : + * extern const mach_port_t kIOMasterPortDefault + * } + */ + public static int kIOMasterPortDefault() { + return kIOMasterPortDefault$constants.SEGMENT.get(kIOMasterPortDefault$constants.LAYOUT, 0L); } - public static VarHandle kIOMasterPortDefault$VH() { - return constants$0.kIOMasterPortDefault$VH; + + /** + * Setter for variable: + * {@snippet lang=c : + * extern const mach_port_t kIOMasterPortDefault + * } + */ + public static void kIOMasterPortDefault(int varValue) { + kIOMasterPortDefault$constants.SEGMENT.set(kIOMasterPortDefault$constants.LAYOUT, 0L, varValue); } - public static MemorySegment kIOMasterPortDefault$SEGMENT() { - return RuntimeHelper.requireNonNull(constants$0.kIOMasterPortDefault$SEGMENT,"kIOMasterPortDefault"); + + private static class IONotificationPortCreate { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + IOKit.C_POINTER, + IOKit.C_INT + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("IONotificationPortCreate"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } - public static int kIOMasterPortDefault$get() { - return (int) constants$0.kIOMasterPortDefault$VH.get(RuntimeHelper.requireNonNull(constants$0.kIOMasterPortDefault$SEGMENT, "kIOMasterPortDefault")); + + /** + * Function descriptor for: + * {@snippet lang=c : + * IONotificationPortRef IONotificationPortCreate(mach_port_t mainPort) + * } + */ + public static FunctionDescriptor IONotificationPortCreate$descriptor() { + return IONotificationPortCreate.DESC; } - public static void kIOMasterPortDefault$set( int x) { - constants$0.kIOMasterPortDefault$VH.set(RuntimeHelper.requireNonNull(constants$0.kIOMasterPortDefault$SEGMENT, "kIOMasterPortDefault"), x); + + /** + * Downcall method handle for: + * {@snippet lang=c : + * IONotificationPortRef IONotificationPortCreate(mach_port_t mainPort) + * } + */ + public static MethodHandle IONotificationPortCreate$handle() { + return IONotificationPortCreate.HANDLE; } - public static MethodHandle IONotificationPortCreate$MH() { - return RuntimeHelper.requireNonNull(constants$0.IONotificationPortCreate$MH,"IONotificationPortCreate"); + + /** + * Address for: + * {@snippet lang=c : + * IONotificationPortRef IONotificationPortCreate(mach_port_t mainPort) + * } + */ + public static MemorySegment IONotificationPortCreate$address() { + return IONotificationPortCreate.ADDR; } - public static MemoryAddress IONotificationPortCreate ( int mainPort) { - var mh$ = IONotificationPortCreate$MH(); + + /** + * {@snippet lang=c : + * IONotificationPortRef IONotificationPortCreate(mach_port_t mainPort) + * } + */ + public static MemorySegment IONotificationPortCreate(int mainPort) { + var mh$ = IONotificationPortCreate.HANDLE; try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(mainPort); + if (TRACE_DOWNCALLS) { + traceDowncall("IONotificationPortCreate", mainPort); + } + return (MemorySegment)mh$.invokeExact(mainPort); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle IONotificationPortGetRunLoopSource$MH() { - return RuntimeHelper.requireNonNull(constants$0.IONotificationPortGetRunLoopSource$MH,"IONotificationPortGetRunLoopSource"); + + private static class IONotificationPortGetRunLoopSource { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("IONotificationPortGetRunLoopSource"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * CFRunLoopSourceRef IONotificationPortGetRunLoopSource(IONotificationPortRef notify) + * } + */ + public static FunctionDescriptor IONotificationPortGetRunLoopSource$descriptor() { + return IONotificationPortGetRunLoopSource.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * CFRunLoopSourceRef IONotificationPortGetRunLoopSource(IONotificationPortRef notify) + * } + */ + public static MethodHandle IONotificationPortGetRunLoopSource$handle() { + return IONotificationPortGetRunLoopSource.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * CFRunLoopSourceRef IONotificationPortGetRunLoopSource(IONotificationPortRef notify) + * } + */ + public static MemorySegment IONotificationPortGetRunLoopSource$address() { + return IONotificationPortGetRunLoopSource.ADDR; } - public static MemoryAddress IONotificationPortGetRunLoopSource ( Addressable notify) { - var mh$ = IONotificationPortGetRunLoopSource$MH(); + + /** + * {@snippet lang=c : + * CFRunLoopSourceRef IONotificationPortGetRunLoopSource(IONotificationPortRef notify) + * } + */ + public static MemorySegment IONotificationPortGetRunLoopSource(MemorySegment notify) { + var mh$ = IONotificationPortGetRunLoopSource.HANDLE; try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(notify); + if (TRACE_DOWNCALLS) { + traceDowncall("IONotificationPortGetRunLoopSource", notify); + } + return (MemorySegment)mh$.invokeExact(notify); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle IOObjectRelease$MH() { - return RuntimeHelper.requireNonNull(constants$0.IOObjectRelease$MH,"IOObjectRelease"); + + private static class IOObjectRelease { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_INT + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("IOObjectRelease"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } - public static int IOObjectRelease ( int object) { - var mh$ = IOObjectRelease$MH(); + + /** + * Function descriptor for: + * {@snippet lang=c : + * kern_return_t IOObjectRelease(io_object_t object) + * } + */ + public static FunctionDescriptor IOObjectRelease$descriptor() { + return IOObjectRelease.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * kern_return_t IOObjectRelease(io_object_t object) + * } + */ + public static MethodHandle IOObjectRelease$handle() { + return IOObjectRelease.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * kern_return_t IOObjectRelease(io_object_t object) + * } + */ + public static MemorySegment IOObjectRelease$address() { + return IOObjectRelease.ADDR; + } + + /** + * {@snippet lang=c : + * kern_return_t IOObjectRelease(io_object_t object) + * } + */ + public static int IOObjectRelease(int object) { + var mh$ = IOObjectRelease.HANDLE; try { + if (TRACE_DOWNCALLS) { + traceDowncall("IOObjectRelease", object); + } return (int)mh$.invokeExact(object); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle IOIteratorNext$MH() { - return RuntimeHelper.requireNonNull(constants$0.IOIteratorNext$MH,"IOIteratorNext"); + + private static class IOIteratorNext { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_INT + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("IOIteratorNext"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * io_object_t IOIteratorNext(io_iterator_t iterator) + * } + */ + public static FunctionDescriptor IOIteratorNext$descriptor() { + return IOIteratorNext.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * io_object_t IOIteratorNext(io_iterator_t iterator) + * } + */ + public static MethodHandle IOIteratorNext$handle() { + return IOIteratorNext.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * io_object_t IOIteratorNext(io_iterator_t iterator) + * } + */ + public static MemorySegment IOIteratorNext$address() { + return IOIteratorNext.ADDR; } - public static int IOIteratorNext ( int iterator) { - var mh$ = IOIteratorNext$MH(); + + /** + * {@snippet lang=c : + * io_object_t IOIteratorNext(io_iterator_t iterator) + * } + */ + public static int IOIteratorNext(int iterator) { + var mh$ = IOIteratorNext.HANDLE; try { + if (TRACE_DOWNCALLS) { + traceDowncall("IOIteratorNext", iterator); + } return (int)mh$.invokeExact(iterator); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle IOServiceAddMatchingNotification$MH() { - return RuntimeHelper.requireNonNull(constants$1.IOServiceAddMatchingNotification$MH,"IOServiceAddMatchingNotification"); + + private static class IOServiceAddMatchingNotification { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("IOServiceAddMatchingNotification"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * kern_return_t IOServiceAddMatchingNotification(IONotificationPortRef notifyPort, const io_name_t notificationType, CFDictionaryRef matching, IOServiceMatchingCallback callback, void *refCon, io_iterator_t *notification) + * } + */ + public static FunctionDescriptor IOServiceAddMatchingNotification$descriptor() { + return IOServiceAddMatchingNotification.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * kern_return_t IOServiceAddMatchingNotification(IONotificationPortRef notifyPort, const io_name_t notificationType, CFDictionaryRef matching, IOServiceMatchingCallback callback, void *refCon, io_iterator_t *notification) + * } + */ + public static MethodHandle IOServiceAddMatchingNotification$handle() { + return IOServiceAddMatchingNotification.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * kern_return_t IOServiceAddMatchingNotification(IONotificationPortRef notifyPort, const io_name_t notificationType, CFDictionaryRef matching, IOServiceMatchingCallback callback, void *refCon, io_iterator_t *notification) + * } + */ + public static MemorySegment IOServiceAddMatchingNotification$address() { + return IOServiceAddMatchingNotification.ADDR; } - public static int IOServiceAddMatchingNotification ( Addressable notifyPort, Addressable notificationType, Addressable matching, Addressable callback, Addressable refCon, Addressable notification) { - var mh$ = IOServiceAddMatchingNotification$MH(); + + /** + * {@snippet lang=c : + * kern_return_t IOServiceAddMatchingNotification(IONotificationPortRef notifyPort, const io_name_t notificationType, CFDictionaryRef matching, IOServiceMatchingCallback callback, void *refCon, io_iterator_t *notification) + * } + */ + public static int IOServiceAddMatchingNotification(MemorySegment notifyPort, MemorySegment notificationType, MemorySegment matching, MemorySegment callback, MemorySegment refCon, MemorySegment notification) { + var mh$ = IOServiceAddMatchingNotification.HANDLE; try { + if (TRACE_DOWNCALLS) { + traceDowncall("IOServiceAddMatchingNotification", notifyPort, notificationType, matching, callback, refCon, notification); + } return (int)mh$.invokeExact(notifyPort, notificationType, matching, callback, refCon, notification); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle IORegistryEntryGetRegistryEntryID$MH() { - return RuntimeHelper.requireNonNull(constants$1.IORegistryEntryGetRegistryEntryID$MH,"IORegistryEntryGetRegistryEntryID"); + + private static class IORegistryEntryGetRegistryEntryID { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_INT, + IOKit.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("IORegistryEntryGetRegistryEntryID"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * kern_return_t IORegistryEntryGetRegistryEntryID(io_registry_entry_t entry, uint64_t *entryID) + * } + */ + public static FunctionDescriptor IORegistryEntryGetRegistryEntryID$descriptor() { + return IORegistryEntryGetRegistryEntryID.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * kern_return_t IORegistryEntryGetRegistryEntryID(io_registry_entry_t entry, uint64_t *entryID) + * } + */ + public static MethodHandle IORegistryEntryGetRegistryEntryID$handle() { + return IORegistryEntryGetRegistryEntryID.HANDLE; } - public static int IORegistryEntryGetRegistryEntryID ( int entry, Addressable entryID) { - var mh$ = IORegistryEntryGetRegistryEntryID$MH(); + + /** + * Address for: + * {@snippet lang=c : + * kern_return_t IORegistryEntryGetRegistryEntryID(io_registry_entry_t entry, uint64_t *entryID) + * } + */ + public static MemorySegment IORegistryEntryGetRegistryEntryID$address() { + return IORegistryEntryGetRegistryEntryID.ADDR; + } + + /** + * {@snippet lang=c : + * kern_return_t IORegistryEntryGetRegistryEntryID(io_registry_entry_t entry, uint64_t *entryID) + * } + */ + public static int IORegistryEntryGetRegistryEntryID(int entry, MemorySegment entryID) { + var mh$ = IORegistryEntryGetRegistryEntryID.HANDLE; try { + if (TRACE_DOWNCALLS) { + traceDowncall("IORegistryEntryGetRegistryEntryID", entry, entryID); + } return (int)mh$.invokeExact(entry, entryID); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle IORegistryEntryCreateCFProperty$MH() { - return RuntimeHelper.requireNonNull(constants$1.IORegistryEntryCreateCFProperty$MH,"IORegistryEntryCreateCFProperty"); + + private static class IORegistryEntryCreateCFProperty { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + IOKit.C_POINTER, + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_INT + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("IORegistryEntryCreateCFProperty"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * CFTypeRef IORegistryEntryCreateCFProperty(io_registry_entry_t entry, CFStringRef key, CFAllocatorRef allocator, IOOptionBits options) + * } + */ + public static FunctionDescriptor IORegistryEntryCreateCFProperty$descriptor() { + return IORegistryEntryCreateCFProperty.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * CFTypeRef IORegistryEntryCreateCFProperty(io_registry_entry_t entry, CFStringRef key, CFAllocatorRef allocator, IOOptionBits options) + * } + */ + public static MethodHandle IORegistryEntryCreateCFProperty$handle() { + return IORegistryEntryCreateCFProperty.HANDLE; } - public static MemoryAddress IORegistryEntryCreateCFProperty ( int entry, Addressable key, Addressable allocator, int options) { - var mh$ = IORegistryEntryCreateCFProperty$MH(); + + /** + * Address for: + * {@snippet lang=c : + * CFTypeRef IORegistryEntryCreateCFProperty(io_registry_entry_t entry, CFStringRef key, CFAllocatorRef allocator, IOOptionBits options) + * } + */ + public static MemorySegment IORegistryEntryCreateCFProperty$address() { + return IORegistryEntryCreateCFProperty.ADDR; + } + + /** + * {@snippet lang=c : + * CFTypeRef IORegistryEntryCreateCFProperty(io_registry_entry_t entry, CFStringRef key, CFAllocatorRef allocator, IOOptionBits options) + * } + */ + public static MemorySegment IORegistryEntryCreateCFProperty(int entry, MemorySegment key, MemorySegment allocator, int options) { + var mh$ = IORegistryEntryCreateCFProperty.HANDLE; try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(entry, key, allocator, options); + if (TRACE_DOWNCALLS) { + traceDowncall("IORegistryEntryCreateCFProperty", entry, key, allocator, options); + } + return (MemorySegment)mh$.invokeExact(entry, key, allocator, options); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle IOServiceMatching$MH() { - return RuntimeHelper.requireNonNull(constants$1.IOServiceMatching$MH,"IOServiceMatching"); + + private static class IOServiceMatching { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("IOServiceMatching"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * CFMutableDictionaryRef IOServiceMatching(const char *name) + * } + */ + public static FunctionDescriptor IOServiceMatching$descriptor() { + return IOServiceMatching.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * CFMutableDictionaryRef IOServiceMatching(const char *name) + * } + */ + public static MethodHandle IOServiceMatching$handle() { + return IOServiceMatching.HANDLE; } - public static MemoryAddress IOServiceMatching ( Addressable name) { - var mh$ = IOServiceMatching$MH(); + + /** + * Address for: + * {@snippet lang=c : + * CFMutableDictionaryRef IOServiceMatching(const char *name) + * } + */ + public static MemorySegment IOServiceMatching$address() { + return IOServiceMatching.ADDR; + } + + /** + * {@snippet lang=c : + * CFMutableDictionaryRef IOServiceMatching(const char *name) + * } + */ + public static MemorySegment IOServiceMatching(MemorySegment name) { + var mh$ = IOServiceMatching.HANDLE; try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(name); + if (TRACE_DOWNCALLS) { + traceDowncall("IOServiceMatching", name); + } + return (MemorySegment)mh$.invokeExact(name); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - public static MethodHandle IOCreatePlugInInterfaceForService$MH() { - return RuntimeHelper.requireNonNull(constants$1.IOCreatePlugInInterfaceForService$MH,"IOCreatePlugInInterfaceForService"); + + private static class IOCreatePlugInInterfaceForService { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("IOCreatePlugInInterfaceForService"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * kern_return_t IOCreatePlugInInterfaceForService(io_service_t service, CFUUIDRef pluginType, CFUUIDRef interfaceType, IOCFPlugInInterface ***theInterface, SInt32 *theScore) + * } + */ + public static FunctionDescriptor IOCreatePlugInInterfaceForService$descriptor() { + return IOCreatePlugInInterfaceForService.DESC; } - public static int IOCreatePlugInInterfaceForService ( int service, Addressable pluginType, Addressable interfaceType, Addressable theInterface, Addressable theScore) { - var mh$ = IOCreatePlugInInterfaceForService$MH(); + + /** + * Downcall method handle for: + * {@snippet lang=c : + * kern_return_t IOCreatePlugInInterfaceForService(io_service_t service, CFUUIDRef pluginType, CFUUIDRef interfaceType, IOCFPlugInInterface ***theInterface, SInt32 *theScore) + * } + */ + public static MethodHandle IOCreatePlugInInterfaceForService$handle() { + return IOCreatePlugInInterfaceForService.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * kern_return_t IOCreatePlugInInterfaceForService(io_service_t service, CFUUIDRef pluginType, CFUUIDRef interfaceType, IOCFPlugInInterface ***theInterface, SInt32 *theScore) + * } + */ + public static MemorySegment IOCreatePlugInInterfaceForService$address() { + return IOCreatePlugInInterfaceForService.ADDR; + } + + /** + * {@snippet lang=c : + * kern_return_t IOCreatePlugInInterfaceForService(io_service_t service, CFUUIDRef pluginType, CFUUIDRef interfaceType, IOCFPlugInInterface ***theInterface, SInt32 *theScore) + * } + */ + public static int IOCreatePlugInInterfaceForService(int service, MemorySegment pluginType, MemorySegment interfaceType, MemorySegment theInterface, MemorySegment theScore) { + var mh$ = IOCreatePlugInInterfaceForService.HANDLE; try { + if (TRACE_DOWNCALLS) { + traceDowncall("IOCreatePlugInInterfaceForService", service, pluginType, interfaceType, theInterface, theScore); + } return (int)mh$.invokeExact(service, pluginType, interfaceType, theInterface, theScore); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } + private static final int kIOReturnExclusiveAccess = (int)-536870203L; + /** + * {@snippet lang=c : + * #define kIOReturnExclusiveAccess -536870203 + * } + */ + public static int kIOReturnExclusiveAccess() { + return kIOReturnExclusiveAccess; + } + private static final int kIOReturnAborted = (int)-536870165L; + /** + * {@snippet lang=c : + * #define kIOReturnAborted -536870165 + * } + */ + public static int kIOReturnAborted() { + return kIOReturnAborted; + } + private static final int kIOUSBPipeStalled = (int)-536854449L; + /** + * {@snippet lang=c : + * #define kIOUSBPipeStalled -536854449 + * } + */ + public static int kIOUSBPipeStalled() { + return kIOUSBPipeStalled; + } + private static final int kIOUSBTransactionTimeout = (int)-536854447L; + /** + * {@snippet lang=c : + * #define kIOUSBTransactionTimeout -536854447 + * } + */ + public static int kIOUSBTransactionTimeout() { + return kIOUSBTransactionTimeout; + } + /** + * {@snippet lang=c : + * #define kIOFirstMatchNotification "IOServiceFirstMatch" + * } + */ public static MemorySegment kIOFirstMatchNotification() { - return constants$1.kIOFirstMatchNotification$SEGMENT; + class Holder { + static final MemorySegment kIOFirstMatchNotification + = IOKit.LIBRARY_ARENA.allocateFrom("IOServiceFirstMatch"); + } + return Holder.kIOFirstMatchNotification; } + /** + * {@snippet lang=c : + * #define kIOTerminatedNotification "IOServiceTerminate" + * } + */ public static MemorySegment kIOTerminatedNotification() { - return constants$2.kIOTerminatedNotification$SEGMENT; + class Holder { + static final MemorySegment kIOTerminatedNotification + = IOKit.LIBRARY_ARENA.allocateFrom("IOServiceTerminate"); + } + return Holder.kIOTerminatedNotification; } + /** + * {@snippet lang=c : + * #define kIOUSBDeviceClassName "IOUSBDevice" + * } + */ public static MemorySegment kIOUSBDeviceClassName() { - return constants$2.kIOUSBDeviceClassName$SEGMENT; + class Holder { + static final MemorySegment kIOUSBDeviceClassName + = IOKit.LIBRARY_ARENA.allocateFrom("IOUSBDevice"); + } + return Holder.kIOUSBDeviceClassName; } } - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOServiceAddMatchingNotification$callback.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOServiceAddMatchingNotification$callback.java new file mode 100644 index 00000000..87fa4df1 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOServiceAddMatchingNotification$callback.java @@ -0,0 +1,70 @@ +// Generated by jextract + +package net.codecrete.usb.macos.gen.iokit; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +/** + * {@snippet lang=c : + * IOServiceMatchingCallback callback + * } + */ +public final class IOServiceAddMatchingNotification$callback { + + private IOServiceAddMatchingNotification$callback() { + // Should not be called directly + } + + /** + * The function pointer signature, expressed as a functional interface + */ + public interface Function { + void apply(MemorySegment _x0, int _x1); + } + + private static final FunctionDescriptor $DESC = FunctionDescriptor.ofVoid( + IOKit.C_POINTER, + IOKit.C_INT + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + private static final MethodHandle UP$MH = IOKit.upcallHandle(IOServiceAddMatchingNotification$callback.Function.class, "apply", $DESC); + + /** + * Allocates a new upcall stub, whose implementation is defined by {@code fi}. + * The lifetime of the returned segment is managed by {@code arena} + */ + public static MemorySegment allocate(IOServiceAddMatchingNotification$callback.Function fi, Arena arena) { + return Linker.nativeLinker().upcallStub(UP$MH.bindTo(fi), $DESC, arena); + } + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static void invoke(MemorySegment funcPtr, MemorySegment _x0, int _x1) { + try { + DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } +} + diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBDevRequest.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBDevRequest.java index e376cdda..0f65940c 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBDevRequest.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBDevRequest.java @@ -2,141 +2,403 @@ package net.codecrete.usb.macos.gen.iokit; +import java.lang.invoke.*; import java.lang.foreign.*; -import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +/** + * {@snippet lang=c : + * struct { + * UInt8 bmRequestType; + * UInt8 bRequest; + * UInt16 wValue; + * UInt16 wIndex; + * UInt16 wLength; + * void *pData; + * UInt32 wLenDone; + * } + * } + */ public class IOUSBDevRequest { - static final GroupLayout $struct$LAYOUT = MemoryLayout.structLayout( - Constants$root.C_CHAR$LAYOUT.withName("bmRequestType"), - Constants$root.C_CHAR$LAYOUT.withName("bRequest"), - Constants$root.C_SHORT$LAYOUT.withName("wValue"), - Constants$root.C_SHORT$LAYOUT.withName("wIndex"), - Constants$root.C_SHORT$LAYOUT.withName("wLength"), - Constants$root.C_POINTER$LAYOUT.withName("pData"), - Constants$root.C_INT$LAYOUT.withName("wLenDone"), - MemoryLayout.paddingLayout(32) - ); - public static MemoryLayout $LAYOUT() { - return IOUSBDevRequest.$struct$LAYOUT; - } - static final VarHandle bmRequestType$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("bmRequestType")); - public static VarHandle bmRequestType$VH() { - return IOUSBDevRequest.bmRequestType$VH; - } - public static byte bmRequestType$get(MemorySegment seg) { - return (byte)IOUSBDevRequest.bmRequestType$VH.get(seg); + IOUSBDevRequest() { + // Should not be called directly } - public static void bmRequestType$set( MemorySegment seg, byte x) { - IOUSBDevRequest.bmRequestType$VH.set(seg, x); + + private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( + IOKit.C_CHAR.withName("bmRequestType"), + IOKit.C_CHAR.withName("bRequest"), + IOKit.C_SHORT.withName("wValue"), + IOKit.C_SHORT.withName("wIndex"), + IOKit.C_SHORT.withName("wLength"), + IOKit.C_POINTER.withName("pData"), + IOKit.C_INT.withName("wLenDone"), + MemoryLayout.paddingLayout(4) + ).withName("IOUSBDevRequest"); + + /** + * The layout of this struct + */ + public static final GroupLayout layout() { + return $LAYOUT; } - public static byte bmRequestType$get(MemorySegment seg, long index) { - return (byte)IOUSBDevRequest.bmRequestType$VH.get(seg.asSlice(index*sizeof())); + + private static final OfByte bmRequestType$LAYOUT = (OfByte)$LAYOUT.select(groupElement("bmRequestType")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 bmRequestType + * } + */ + public static final OfByte bmRequestType$layout() { + return bmRequestType$LAYOUT; } - public static void bmRequestType$set(MemorySegment seg, long index, byte x) { - IOUSBDevRequest.bmRequestType$VH.set(seg.asSlice(index*sizeof()), x); + + private static final long bmRequestType$OFFSET = $LAYOUT.byteOffset(groupElement("bmRequestType")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 bmRequestType + * } + */ + public static final long bmRequestType$offset() { + return bmRequestType$OFFSET; } - static final VarHandle bRequest$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("bRequest")); - public static VarHandle bRequest$VH() { - return IOUSBDevRequest.bRequest$VH; + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 bmRequestType + * } + */ + public static byte bmRequestType(MemorySegment struct) { + return struct.get(bmRequestType$LAYOUT, bmRequestType$OFFSET); } - public static byte bRequest$get(MemorySegment seg) { - return (byte)IOUSBDevRequest.bRequest$VH.get(seg); + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 bmRequestType + * } + */ + public static void bmRequestType(MemorySegment struct, byte fieldValue) { + struct.set(bmRequestType$LAYOUT, bmRequestType$OFFSET, fieldValue); } - public static void bRequest$set( MemorySegment seg, byte x) { - IOUSBDevRequest.bRequest$VH.set(seg, x); + + private static final OfByte bRequest$LAYOUT = (OfByte)$LAYOUT.select(groupElement("bRequest")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt8 bRequest + * } + */ + public static final OfByte bRequest$layout() { + return bRequest$LAYOUT; } - public static byte bRequest$get(MemorySegment seg, long index) { - return (byte)IOUSBDevRequest.bRequest$VH.get(seg.asSlice(index*sizeof())); + + private static final long bRequest$OFFSET = $LAYOUT.byteOffset(groupElement("bRequest")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt8 bRequest + * } + */ + public static final long bRequest$offset() { + return bRequest$OFFSET; } - public static void bRequest$set(MemorySegment seg, long index, byte x) { - IOUSBDevRequest.bRequest$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Getter for field: + * {@snippet lang=c : + * UInt8 bRequest + * } + */ + public static byte bRequest(MemorySegment struct) { + return struct.get(bRequest$LAYOUT, bRequest$OFFSET); } - static final VarHandle wValue$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("wValue")); - public static VarHandle wValue$VH() { - return IOUSBDevRequest.wValue$VH; + + /** + * Setter for field: + * {@snippet lang=c : + * UInt8 bRequest + * } + */ + public static void bRequest(MemorySegment struct, byte fieldValue) { + struct.set(bRequest$LAYOUT, bRequest$OFFSET, fieldValue); } - public static short wValue$get(MemorySegment seg) { - return (short)IOUSBDevRequest.wValue$VH.get(seg); + + private static final OfShort wValue$LAYOUT = (OfShort)$LAYOUT.select(groupElement("wValue")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt16 wValue + * } + */ + public static final OfShort wValue$layout() { + return wValue$LAYOUT; } - public static void wValue$set( MemorySegment seg, short x) { - IOUSBDevRequest.wValue$VH.set(seg, x); + + private static final long wValue$OFFSET = $LAYOUT.byteOffset(groupElement("wValue")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt16 wValue + * } + */ + public static final long wValue$offset() { + return wValue$OFFSET; } - public static short wValue$get(MemorySegment seg, long index) { - return (short)IOUSBDevRequest.wValue$VH.get(seg.asSlice(index*sizeof())); + + /** + * Getter for field: + * {@snippet lang=c : + * UInt16 wValue + * } + */ + public static short wValue(MemorySegment struct) { + return struct.get(wValue$LAYOUT, wValue$OFFSET); } - public static void wValue$set(MemorySegment seg, long index, short x) { - IOUSBDevRequest.wValue$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Setter for field: + * {@snippet lang=c : + * UInt16 wValue + * } + */ + public static void wValue(MemorySegment struct, short fieldValue) { + struct.set(wValue$LAYOUT, wValue$OFFSET, fieldValue); } - static final VarHandle wIndex$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("wIndex")); - public static VarHandle wIndex$VH() { - return IOUSBDevRequest.wIndex$VH; + + private static final OfShort wIndex$LAYOUT = (OfShort)$LAYOUT.select(groupElement("wIndex")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt16 wIndex + * } + */ + public static final OfShort wIndex$layout() { + return wIndex$LAYOUT; } - public static short wIndex$get(MemorySegment seg) { - return (short)IOUSBDevRequest.wIndex$VH.get(seg); + + private static final long wIndex$OFFSET = $LAYOUT.byteOffset(groupElement("wIndex")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt16 wIndex + * } + */ + public static final long wIndex$offset() { + return wIndex$OFFSET; } - public static void wIndex$set( MemorySegment seg, short x) { - IOUSBDevRequest.wIndex$VH.set(seg, x); + + /** + * Getter for field: + * {@snippet lang=c : + * UInt16 wIndex + * } + */ + public static short wIndex(MemorySegment struct) { + return struct.get(wIndex$LAYOUT, wIndex$OFFSET); } - public static short wIndex$get(MemorySegment seg, long index) { - return (short)IOUSBDevRequest.wIndex$VH.get(seg.asSlice(index*sizeof())); + + /** + * Setter for field: + * {@snippet lang=c : + * UInt16 wIndex + * } + */ + public static void wIndex(MemorySegment struct, short fieldValue) { + struct.set(wIndex$LAYOUT, wIndex$OFFSET, fieldValue); } - public static void wIndex$set(MemorySegment seg, long index, short x) { - IOUSBDevRequest.wIndex$VH.set(seg.asSlice(index*sizeof()), x); + + private static final OfShort wLength$LAYOUT = (OfShort)$LAYOUT.select(groupElement("wLength")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt16 wLength + * } + */ + public static final OfShort wLength$layout() { + return wLength$LAYOUT; } - static final VarHandle wLength$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("wLength")); - public static VarHandle wLength$VH() { - return IOUSBDevRequest.wLength$VH; + + private static final long wLength$OFFSET = $LAYOUT.byteOffset(groupElement("wLength")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt16 wLength + * } + */ + public static final long wLength$offset() { + return wLength$OFFSET; } - public static short wLength$get(MemorySegment seg) { - return (short)IOUSBDevRequest.wLength$VH.get(seg); + + /** + * Getter for field: + * {@snippet lang=c : + * UInt16 wLength + * } + */ + public static short wLength(MemorySegment struct) { + return struct.get(wLength$LAYOUT, wLength$OFFSET); } - public static void wLength$set( MemorySegment seg, short x) { - IOUSBDevRequest.wLength$VH.set(seg, x); + + /** + * Setter for field: + * {@snippet lang=c : + * UInt16 wLength + * } + */ + public static void wLength(MemorySegment struct, short fieldValue) { + struct.set(wLength$LAYOUT, wLength$OFFSET, fieldValue); } - public static short wLength$get(MemorySegment seg, long index) { - return (short)IOUSBDevRequest.wLength$VH.get(seg.asSlice(index*sizeof())); + + private static final AddressLayout pData$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("pData")); + + /** + * Layout for field: + * {@snippet lang=c : + * void *pData + * } + */ + public static final AddressLayout pData$layout() { + return pData$LAYOUT; } - public static void wLength$set(MemorySegment seg, long index, short x) { - IOUSBDevRequest.wLength$VH.set(seg.asSlice(index*sizeof()), x); + + private static final long pData$OFFSET = $LAYOUT.byteOffset(groupElement("pData")); + + /** + * Offset for field: + * {@snippet lang=c : + * void *pData + * } + */ + public static final long pData$offset() { + return pData$OFFSET; } - static final VarHandle pData$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("pData")); - public static VarHandle pData$VH() { - return IOUSBDevRequest.pData$VH; + + /** + * Getter for field: + * {@snippet lang=c : + * void *pData + * } + */ + public static MemorySegment pData(MemorySegment struct) { + return struct.get(pData$LAYOUT, pData$OFFSET); } - public static MemoryAddress pData$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDevRequest.pData$VH.get(seg); + + /** + * Setter for field: + * {@snippet lang=c : + * void *pData + * } + */ + public static void pData(MemorySegment struct, MemorySegment fieldValue) { + struct.set(pData$LAYOUT, pData$OFFSET, fieldValue); } - public static void pData$set( MemorySegment seg, MemoryAddress x) { - IOUSBDevRequest.pData$VH.set(seg, x); + + private static final OfInt wLenDone$LAYOUT = (OfInt)$LAYOUT.select(groupElement("wLenDone")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt32 wLenDone + * } + */ + public static final OfInt wLenDone$layout() { + return wLenDone$LAYOUT; } - public static MemoryAddress pData$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDevRequest.pData$VH.get(seg.asSlice(index*sizeof())); + + private static final long wLenDone$OFFSET = $LAYOUT.byteOffset(groupElement("wLenDone")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt32 wLenDone + * } + */ + public static final long wLenDone$offset() { + return wLenDone$OFFSET; } - public static void pData$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDevRequest.pData$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Getter for field: + * {@snippet lang=c : + * UInt32 wLenDone + * } + */ + public static int wLenDone(MemorySegment struct) { + return struct.get(wLenDone$LAYOUT, wLenDone$OFFSET); } - static final VarHandle wLenDone$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("wLenDone")); - public static VarHandle wLenDone$VH() { - return IOUSBDevRequest.wLenDone$VH; + + /** + * Setter for field: + * {@snippet lang=c : + * UInt32 wLenDone + * } + */ + public static void wLenDone(MemorySegment struct, int fieldValue) { + struct.set(wLenDone$LAYOUT, wLenDone$OFFSET, fieldValue); } - public static int wLenDone$get(MemorySegment seg) { - return (int)IOUSBDevRequest.wLenDone$VH.get(seg); + + /** + * Obtains a slice of {@code arrayParam} which selects the array element at {@code index}. + * The returned segment has address {@code arrayParam.address() + index * layout().byteSize()} + */ + public static MemorySegment asSlice(MemorySegment array, long index) { + return array.asSlice(layout().byteSize() * index); } - public static void wLenDone$set( MemorySegment seg, int x) { - IOUSBDevRequest.wLenDone$VH.set(seg, x); + + /** + * The size (in bytes) of this struct + */ + public static long sizeof() { return layout().byteSize(); } + + /** + * Allocate a segment of size {@code layout().byteSize()} using {@code allocator} + */ + public static MemorySegment allocate(SegmentAllocator allocator) { + return allocator.allocate(layout()); } - public static int wLenDone$get(MemorySegment seg, long index) { - return (int)IOUSBDevRequest.wLenDone$VH.get(seg.asSlice(index*sizeof())); + + /** + * Allocate an array of size {@code elementCount} using {@code allocator}. + * The returned segment has size {@code elementCount * layout().byteSize()}. + */ + public static MemorySegment allocateArray(long elementCount, SegmentAllocator allocator) { + return allocator.allocate(MemoryLayout.sequenceLayout(elementCount, layout())); } - public static void wLenDone$set(MemorySegment seg, long index, int x) { - IOUSBDevRequest.wLenDone$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, Arena arena, Consumer cleanup) { + return reinterpret(addr, 1, arena, cleanup); } - public static long sizeof() { return $LAYOUT().byteSize(); } - public static MemorySegment allocate(SegmentAllocator allocator) { return allocator.allocate($LAYOUT()); } - public static MemorySegment allocateArray(int len, SegmentAllocator allocator) { - return allocator.allocate(MemoryLayout.sequenceLayout(len, $LAYOUT())); + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code elementCount * layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, long elementCount, Arena arena, Consumer cleanup) { + return addr.reinterpret(layout().byteSize() * elementCount, arena, cleanup); } - public static MemorySegment ofAddress(MemoryAddress addr, MemorySession session) { return RuntimeHelper.asArray(addr, $LAYOUT(), 1, session); } } - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBDeviceInterface.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBDeviceInterface.java deleted file mode 100644 index 955982bc..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBDeviceInterface.java +++ /dev/null @@ -1,9 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.macos.gen.iokit; - -public class IOUSBDeviceInterface extends IOUSBDeviceStruct942 { - -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBDeviceStruct187.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBDeviceStruct187.java new file mode 100644 index 00000000..db64afe0 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBDeviceStruct187.java @@ -0,0 +1,3456 @@ +// Generated by jextract + +package net.codecrete.usb.macos.gen.iokit; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +/** + * {@snippet lang=c : + * struct IOUSBDeviceStruct187 { + * void *_reserved; + * HRESULT (*QueryInterface)(void *, REFIID, LPVOID *); + * ULONG (*AddRef)(void *); + * ULONG (*Release)(void *); + * IOReturn (*CreateDeviceAsyncEventSource)(void *, CFRunLoopSourceRef *); + * CFRunLoopSourceRef (*GetDeviceAsyncEventSource)(void *); + * IOReturn (*CreateDeviceAsyncPort)(void *, mach_port_t *); + * mach_port_t (*GetDeviceAsyncPort)(void *); + * IOReturn (*USBDeviceOpen)(void *); + * IOReturn (*USBDeviceClose)(void *); + * IOReturn (*GetDeviceClass)(void *, UInt8 *); + * IOReturn (*GetDeviceSubClass)(void *, UInt8 *); + * IOReturn (*GetDeviceProtocol)(void *, UInt8 *); + * IOReturn (*GetDeviceVendor)(void *, UInt16 *); + * IOReturn (*GetDeviceProduct)(void *, UInt16 *); + * IOReturn (*GetDeviceReleaseNumber)(void *, UInt16 *); + * IOReturn (*GetDeviceAddress)(void *, USBDeviceAddress *); + * IOReturn (*GetDeviceBusPowerAvailable)(void *, UInt32 *); + * IOReturn (*GetDeviceSpeed)(void *, UInt8 *); + * IOReturn (*GetNumberOfConfigurations)(void *, UInt8 *); + * IOReturn (*GetLocationID)(void *, UInt32 *); + * IOReturn (*GetConfigurationDescriptorPtr)(void *, UInt8, IOUSBConfigurationDescriptorPtr *); + * IOReturn (*GetConfiguration)(void *, UInt8 *); + * IOReturn (*SetConfiguration)(void *, UInt8); + * IOReturn (*GetBusFrameNumber)(void *, UInt64 *, AbsoluteTime *); + * IOReturn (*ResetDevice)(void *); + * IOReturn (*DeviceRequest)(void *, IOUSBDevRequest *); + * IOReturn (*DeviceRequestAsync)(void *, IOUSBDevRequest *, IOAsyncCallback1, void *); + * IOReturn (*CreateInterfaceIterator)(void *, IOUSBFindInterfaceRequest *, io_iterator_t *); + * IOReturn (*USBDeviceOpenSeize)(void *); + * IOReturn (*DeviceRequestTO)(void *, IOUSBDevRequestTO *); + * IOReturn (*DeviceRequestAsyncTO)(void *, IOUSBDevRequestTO *, IOAsyncCallback1, void *); + * IOReturn (*USBDeviceSuspend)(void *, Boolean); + * IOReturn (*USBDeviceAbortPipeZero)(void *); + * IOReturn (*USBGetManufacturerStringIndex)(void *, UInt8 *); + * IOReturn (*USBGetProductStringIndex)(void *, UInt8 *); + * IOReturn (*USBGetSerialNumberStringIndex)(void *, UInt8 *); + * IOReturn (*USBDeviceReEnumerate)(void *, UInt32); + * } + * } + */ +public class IOUSBDeviceStruct187 { + + IOUSBDeviceStruct187() { + // Should not be called directly + } + + private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( + IOKit.C_POINTER.withName("_reserved"), + IOKit.C_POINTER.withName("QueryInterface"), + IOKit.C_POINTER.withName("AddRef"), + IOKit.C_POINTER.withName("Release"), + IOKit.C_POINTER.withName("CreateDeviceAsyncEventSource"), + IOKit.C_POINTER.withName("GetDeviceAsyncEventSource"), + IOKit.C_POINTER.withName("CreateDeviceAsyncPort"), + IOKit.C_POINTER.withName("GetDeviceAsyncPort"), + IOKit.C_POINTER.withName("USBDeviceOpen"), + IOKit.C_POINTER.withName("USBDeviceClose"), + IOKit.C_POINTER.withName("GetDeviceClass"), + IOKit.C_POINTER.withName("GetDeviceSubClass"), + IOKit.C_POINTER.withName("GetDeviceProtocol"), + IOKit.C_POINTER.withName("GetDeviceVendor"), + IOKit.C_POINTER.withName("GetDeviceProduct"), + IOKit.C_POINTER.withName("GetDeviceReleaseNumber"), + IOKit.C_POINTER.withName("GetDeviceAddress"), + IOKit.C_POINTER.withName("GetDeviceBusPowerAvailable"), + IOKit.C_POINTER.withName("GetDeviceSpeed"), + IOKit.C_POINTER.withName("GetNumberOfConfigurations"), + IOKit.C_POINTER.withName("GetLocationID"), + IOKit.C_POINTER.withName("GetConfigurationDescriptorPtr"), + IOKit.C_POINTER.withName("GetConfiguration"), + IOKit.C_POINTER.withName("SetConfiguration"), + IOKit.C_POINTER.withName("GetBusFrameNumber"), + IOKit.C_POINTER.withName("ResetDevice"), + IOKit.C_POINTER.withName("DeviceRequest"), + IOKit.C_POINTER.withName("DeviceRequestAsync"), + IOKit.C_POINTER.withName("CreateInterfaceIterator"), + IOKit.C_POINTER.withName("USBDeviceOpenSeize"), + IOKit.C_POINTER.withName("DeviceRequestTO"), + IOKit.C_POINTER.withName("DeviceRequestAsyncTO"), + IOKit.C_POINTER.withName("USBDeviceSuspend"), + IOKit.C_POINTER.withName("USBDeviceAbortPipeZero"), + IOKit.C_POINTER.withName("USBGetManufacturerStringIndex"), + IOKit.C_POINTER.withName("USBGetProductStringIndex"), + IOKit.C_POINTER.withName("USBGetSerialNumberStringIndex"), + IOKit.C_POINTER.withName("USBDeviceReEnumerate") + ).withName("IOUSBDeviceStruct187"); + + /** + * The layout of this struct + */ + public static final GroupLayout layout() { + return $LAYOUT; + } + + private static final AddressLayout _reserved$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("_reserved")); + + /** + * Layout for field: + * {@snippet lang=c : + * void *_reserved + * } + */ + public static final AddressLayout _reserved$layout() { + return _reserved$LAYOUT; + } + + private static final long _reserved$OFFSET = $LAYOUT.byteOffset(groupElement("_reserved")); + + /** + * Offset for field: + * {@snippet lang=c : + * void *_reserved + * } + */ + public static final long _reserved$offset() { + return _reserved$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * void *_reserved + * } + */ + public static MemorySegment _reserved(MemorySegment struct) { + return struct.get(_reserved$LAYOUT, _reserved$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * void *_reserved + * } + */ + public static void _reserved(MemorySegment struct, MemorySegment fieldValue) { + struct.set(_reserved$LAYOUT, _reserved$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * HRESULT (*QueryInterface)(void *, REFIID, LPVOID *) + * } + */ + public final static class QueryInterface { + + private QueryInterface() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + CFUUIDBytes.layout(), + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1, MemorySegment _x2) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout QueryInterface$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("QueryInterface")); + + /** + * Layout for field: + * {@snippet lang=c : + * HRESULT (*QueryInterface)(void *, REFIID, LPVOID *) + * } + */ + public static final AddressLayout QueryInterface$layout() { + return QueryInterface$LAYOUT; + } + + private static final long QueryInterface$OFFSET = $LAYOUT.byteOffset(groupElement("QueryInterface")); + + /** + * Offset for field: + * {@snippet lang=c : + * HRESULT (*QueryInterface)(void *, REFIID, LPVOID *) + * } + */ + public static final long QueryInterface$offset() { + return QueryInterface$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * HRESULT (*QueryInterface)(void *, REFIID, LPVOID *) + * } + */ + public static MemorySegment QueryInterface(MemorySegment struct) { + return struct.get(QueryInterface$LAYOUT, QueryInterface$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * HRESULT (*QueryInterface)(void *, REFIID, LPVOID *) + * } + */ + public static void QueryInterface(MemorySegment struct, MemorySegment fieldValue) { + struct.set(QueryInterface$LAYOUT, QueryInterface$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * ULONG (*AddRef)(void *) + * } + */ + public final static class AddRef { + + private AddRef() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout AddRef$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("AddRef")); + + /** + * Layout for field: + * {@snippet lang=c : + * ULONG (*AddRef)(void *) + * } + */ + public static final AddressLayout AddRef$layout() { + return AddRef$LAYOUT; + } + + private static final long AddRef$OFFSET = $LAYOUT.byteOffset(groupElement("AddRef")); + + /** + * Offset for field: + * {@snippet lang=c : + * ULONG (*AddRef)(void *) + * } + */ + public static final long AddRef$offset() { + return AddRef$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * ULONG (*AddRef)(void *) + * } + */ + public static MemorySegment AddRef(MemorySegment struct) { + return struct.get(AddRef$LAYOUT, AddRef$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * ULONG (*AddRef)(void *) + * } + */ + public static void AddRef(MemorySegment struct, MemorySegment fieldValue) { + struct.set(AddRef$LAYOUT, AddRef$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * ULONG (*Release)(void *) + * } + */ + public final static class Release { + + private Release() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout Release$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("Release")); + + /** + * Layout for field: + * {@snippet lang=c : + * ULONG (*Release)(void *) + * } + */ + public static final AddressLayout Release$layout() { + return Release$LAYOUT; + } + + private static final long Release$OFFSET = $LAYOUT.byteOffset(groupElement("Release")); + + /** + * Offset for field: + * {@snippet lang=c : + * ULONG (*Release)(void *) + * } + */ + public static final long Release$offset() { + return Release$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * ULONG (*Release)(void *) + * } + */ + public static MemorySegment Release(MemorySegment struct) { + return struct.get(Release$LAYOUT, Release$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * ULONG (*Release)(void *) + * } + */ + public static void Release(MemorySegment struct, MemorySegment fieldValue) { + struct.set(Release$LAYOUT, Release$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*CreateDeviceAsyncEventSource)(void *, CFRunLoopSourceRef *) + * } + */ + public final static class CreateDeviceAsyncEventSource { + + private CreateDeviceAsyncEventSource() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout CreateDeviceAsyncEventSource$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("CreateDeviceAsyncEventSource")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*CreateDeviceAsyncEventSource)(void *, CFRunLoopSourceRef *) + * } + */ + public static final AddressLayout CreateDeviceAsyncEventSource$layout() { + return CreateDeviceAsyncEventSource$LAYOUT; + } + + private static final long CreateDeviceAsyncEventSource$OFFSET = $LAYOUT.byteOffset(groupElement("CreateDeviceAsyncEventSource")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*CreateDeviceAsyncEventSource)(void *, CFRunLoopSourceRef *) + * } + */ + public static final long CreateDeviceAsyncEventSource$offset() { + return CreateDeviceAsyncEventSource$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*CreateDeviceAsyncEventSource)(void *, CFRunLoopSourceRef *) + * } + */ + public static MemorySegment CreateDeviceAsyncEventSource(MemorySegment struct) { + return struct.get(CreateDeviceAsyncEventSource$LAYOUT, CreateDeviceAsyncEventSource$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*CreateDeviceAsyncEventSource)(void *, CFRunLoopSourceRef *) + * } + */ + public static void CreateDeviceAsyncEventSource(MemorySegment struct, MemorySegment fieldValue) { + struct.set(CreateDeviceAsyncEventSource$LAYOUT, CreateDeviceAsyncEventSource$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * CFRunLoopSourceRef (*GetDeviceAsyncEventSource)(void *) + * } + */ + public final static class GetDeviceAsyncEventSource { + + private GetDeviceAsyncEventSource() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static MemorySegment invoke(MemorySegment funcPtr, MemorySegment _x0) { + try { + return (MemorySegment) DOWN$MH.invokeExact(funcPtr, _x0); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetDeviceAsyncEventSource$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetDeviceAsyncEventSource")); + + /** + * Layout for field: + * {@snippet lang=c : + * CFRunLoopSourceRef (*GetDeviceAsyncEventSource)(void *) + * } + */ + public static final AddressLayout GetDeviceAsyncEventSource$layout() { + return GetDeviceAsyncEventSource$LAYOUT; + } + + private static final long GetDeviceAsyncEventSource$OFFSET = $LAYOUT.byteOffset(groupElement("GetDeviceAsyncEventSource")); + + /** + * Offset for field: + * {@snippet lang=c : + * CFRunLoopSourceRef (*GetDeviceAsyncEventSource)(void *) + * } + */ + public static final long GetDeviceAsyncEventSource$offset() { + return GetDeviceAsyncEventSource$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * CFRunLoopSourceRef (*GetDeviceAsyncEventSource)(void *) + * } + */ + public static MemorySegment GetDeviceAsyncEventSource(MemorySegment struct) { + return struct.get(GetDeviceAsyncEventSource$LAYOUT, GetDeviceAsyncEventSource$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * CFRunLoopSourceRef (*GetDeviceAsyncEventSource)(void *) + * } + */ + public static void GetDeviceAsyncEventSource(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetDeviceAsyncEventSource$LAYOUT, GetDeviceAsyncEventSource$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*CreateDeviceAsyncPort)(void *, mach_port_t *) + * } + */ + public final static class CreateDeviceAsyncPort { + + private CreateDeviceAsyncPort() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout CreateDeviceAsyncPort$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("CreateDeviceAsyncPort")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*CreateDeviceAsyncPort)(void *, mach_port_t *) + * } + */ + public static final AddressLayout CreateDeviceAsyncPort$layout() { + return CreateDeviceAsyncPort$LAYOUT; + } + + private static final long CreateDeviceAsyncPort$OFFSET = $LAYOUT.byteOffset(groupElement("CreateDeviceAsyncPort")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*CreateDeviceAsyncPort)(void *, mach_port_t *) + * } + */ + public static final long CreateDeviceAsyncPort$offset() { + return CreateDeviceAsyncPort$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*CreateDeviceAsyncPort)(void *, mach_port_t *) + * } + */ + public static MemorySegment CreateDeviceAsyncPort(MemorySegment struct) { + return struct.get(CreateDeviceAsyncPort$LAYOUT, CreateDeviceAsyncPort$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*CreateDeviceAsyncPort)(void *, mach_port_t *) + * } + */ + public static void CreateDeviceAsyncPort(MemorySegment struct, MemorySegment fieldValue) { + struct.set(CreateDeviceAsyncPort$LAYOUT, CreateDeviceAsyncPort$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * mach_port_t (*GetDeviceAsyncPort)(void *) + * } + */ + public final static class GetDeviceAsyncPort { + + private GetDeviceAsyncPort() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetDeviceAsyncPort$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetDeviceAsyncPort")); + + /** + * Layout for field: + * {@snippet lang=c : + * mach_port_t (*GetDeviceAsyncPort)(void *) + * } + */ + public static final AddressLayout GetDeviceAsyncPort$layout() { + return GetDeviceAsyncPort$LAYOUT; + } + + private static final long GetDeviceAsyncPort$OFFSET = $LAYOUT.byteOffset(groupElement("GetDeviceAsyncPort")); + + /** + * Offset for field: + * {@snippet lang=c : + * mach_port_t (*GetDeviceAsyncPort)(void *) + * } + */ + public static final long GetDeviceAsyncPort$offset() { + return GetDeviceAsyncPort$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * mach_port_t (*GetDeviceAsyncPort)(void *) + * } + */ + public static MemorySegment GetDeviceAsyncPort(MemorySegment struct) { + return struct.get(GetDeviceAsyncPort$LAYOUT, GetDeviceAsyncPort$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * mach_port_t (*GetDeviceAsyncPort)(void *) + * } + */ + public static void GetDeviceAsyncPort(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetDeviceAsyncPort$LAYOUT, GetDeviceAsyncPort$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*USBDeviceOpen)(void *) + * } + */ + public final static class USBDeviceOpen { + + private USBDeviceOpen() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout USBDeviceOpen$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("USBDeviceOpen")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceOpen)(void *) + * } + */ + public static final AddressLayout USBDeviceOpen$layout() { + return USBDeviceOpen$LAYOUT; + } + + private static final long USBDeviceOpen$OFFSET = $LAYOUT.byteOffset(groupElement("USBDeviceOpen")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceOpen)(void *) + * } + */ + public static final long USBDeviceOpen$offset() { + return USBDeviceOpen$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceOpen)(void *) + * } + */ + public static MemorySegment USBDeviceOpen(MemorySegment struct) { + return struct.get(USBDeviceOpen$LAYOUT, USBDeviceOpen$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceOpen)(void *) + * } + */ + public static void USBDeviceOpen(MemorySegment struct, MemorySegment fieldValue) { + struct.set(USBDeviceOpen$LAYOUT, USBDeviceOpen$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*USBDeviceClose)(void *) + * } + */ + public final static class USBDeviceClose { + + private USBDeviceClose() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout USBDeviceClose$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("USBDeviceClose")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceClose)(void *) + * } + */ + public static final AddressLayout USBDeviceClose$layout() { + return USBDeviceClose$LAYOUT; + } + + private static final long USBDeviceClose$OFFSET = $LAYOUT.byteOffset(groupElement("USBDeviceClose")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceClose)(void *) + * } + */ + public static final long USBDeviceClose$offset() { + return USBDeviceClose$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceClose)(void *) + * } + */ + public static MemorySegment USBDeviceClose(MemorySegment struct) { + return struct.get(USBDeviceClose$LAYOUT, USBDeviceClose$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceClose)(void *) + * } + */ + public static void USBDeviceClose(MemorySegment struct, MemorySegment fieldValue) { + struct.set(USBDeviceClose$LAYOUT, USBDeviceClose$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetDeviceClass)(void *, UInt8 *) + * } + */ + public final static class GetDeviceClass { + + private GetDeviceClass() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetDeviceClass$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetDeviceClass")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceClass)(void *, UInt8 *) + * } + */ + public static final AddressLayout GetDeviceClass$layout() { + return GetDeviceClass$LAYOUT; + } + + private static final long GetDeviceClass$OFFSET = $LAYOUT.byteOffset(groupElement("GetDeviceClass")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceClass)(void *, UInt8 *) + * } + */ + public static final long GetDeviceClass$offset() { + return GetDeviceClass$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceClass)(void *, UInt8 *) + * } + */ + public static MemorySegment GetDeviceClass(MemorySegment struct) { + return struct.get(GetDeviceClass$LAYOUT, GetDeviceClass$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceClass)(void *, UInt8 *) + * } + */ + public static void GetDeviceClass(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetDeviceClass$LAYOUT, GetDeviceClass$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetDeviceSubClass)(void *, UInt8 *) + * } + */ + public final static class GetDeviceSubClass { + + private GetDeviceSubClass() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetDeviceSubClass$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetDeviceSubClass")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceSubClass)(void *, UInt8 *) + * } + */ + public static final AddressLayout GetDeviceSubClass$layout() { + return GetDeviceSubClass$LAYOUT; + } + + private static final long GetDeviceSubClass$OFFSET = $LAYOUT.byteOffset(groupElement("GetDeviceSubClass")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceSubClass)(void *, UInt8 *) + * } + */ + public static final long GetDeviceSubClass$offset() { + return GetDeviceSubClass$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceSubClass)(void *, UInt8 *) + * } + */ + public static MemorySegment GetDeviceSubClass(MemorySegment struct) { + return struct.get(GetDeviceSubClass$LAYOUT, GetDeviceSubClass$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceSubClass)(void *, UInt8 *) + * } + */ + public static void GetDeviceSubClass(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetDeviceSubClass$LAYOUT, GetDeviceSubClass$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetDeviceProtocol)(void *, UInt8 *) + * } + */ + public final static class GetDeviceProtocol { + + private GetDeviceProtocol() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetDeviceProtocol$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetDeviceProtocol")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceProtocol)(void *, UInt8 *) + * } + */ + public static final AddressLayout GetDeviceProtocol$layout() { + return GetDeviceProtocol$LAYOUT; + } + + private static final long GetDeviceProtocol$OFFSET = $LAYOUT.byteOffset(groupElement("GetDeviceProtocol")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceProtocol)(void *, UInt8 *) + * } + */ + public static final long GetDeviceProtocol$offset() { + return GetDeviceProtocol$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceProtocol)(void *, UInt8 *) + * } + */ + public static MemorySegment GetDeviceProtocol(MemorySegment struct) { + return struct.get(GetDeviceProtocol$LAYOUT, GetDeviceProtocol$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceProtocol)(void *, UInt8 *) + * } + */ + public static void GetDeviceProtocol(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetDeviceProtocol$LAYOUT, GetDeviceProtocol$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetDeviceVendor)(void *, UInt16 *) + * } + */ + public final static class GetDeviceVendor { + + private GetDeviceVendor() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetDeviceVendor$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetDeviceVendor")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceVendor)(void *, UInt16 *) + * } + */ + public static final AddressLayout GetDeviceVendor$layout() { + return GetDeviceVendor$LAYOUT; + } + + private static final long GetDeviceVendor$OFFSET = $LAYOUT.byteOffset(groupElement("GetDeviceVendor")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceVendor)(void *, UInt16 *) + * } + */ + public static final long GetDeviceVendor$offset() { + return GetDeviceVendor$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceVendor)(void *, UInt16 *) + * } + */ + public static MemorySegment GetDeviceVendor(MemorySegment struct) { + return struct.get(GetDeviceVendor$LAYOUT, GetDeviceVendor$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceVendor)(void *, UInt16 *) + * } + */ + public static void GetDeviceVendor(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetDeviceVendor$LAYOUT, GetDeviceVendor$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetDeviceProduct)(void *, UInt16 *) + * } + */ + public final static class GetDeviceProduct { + + private GetDeviceProduct() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetDeviceProduct$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetDeviceProduct")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceProduct)(void *, UInt16 *) + * } + */ + public static final AddressLayout GetDeviceProduct$layout() { + return GetDeviceProduct$LAYOUT; + } + + private static final long GetDeviceProduct$OFFSET = $LAYOUT.byteOffset(groupElement("GetDeviceProduct")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceProduct)(void *, UInt16 *) + * } + */ + public static final long GetDeviceProduct$offset() { + return GetDeviceProduct$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceProduct)(void *, UInt16 *) + * } + */ + public static MemorySegment GetDeviceProduct(MemorySegment struct) { + return struct.get(GetDeviceProduct$LAYOUT, GetDeviceProduct$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceProduct)(void *, UInt16 *) + * } + */ + public static void GetDeviceProduct(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetDeviceProduct$LAYOUT, GetDeviceProduct$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetDeviceReleaseNumber)(void *, UInt16 *) + * } + */ + public final static class GetDeviceReleaseNumber { + + private GetDeviceReleaseNumber() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetDeviceReleaseNumber$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetDeviceReleaseNumber")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceReleaseNumber)(void *, UInt16 *) + * } + */ + public static final AddressLayout GetDeviceReleaseNumber$layout() { + return GetDeviceReleaseNumber$LAYOUT; + } + + private static final long GetDeviceReleaseNumber$OFFSET = $LAYOUT.byteOffset(groupElement("GetDeviceReleaseNumber")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceReleaseNumber)(void *, UInt16 *) + * } + */ + public static final long GetDeviceReleaseNumber$offset() { + return GetDeviceReleaseNumber$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceReleaseNumber)(void *, UInt16 *) + * } + */ + public static MemorySegment GetDeviceReleaseNumber(MemorySegment struct) { + return struct.get(GetDeviceReleaseNumber$LAYOUT, GetDeviceReleaseNumber$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceReleaseNumber)(void *, UInt16 *) + * } + */ + public static void GetDeviceReleaseNumber(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetDeviceReleaseNumber$LAYOUT, GetDeviceReleaseNumber$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetDeviceAddress)(void *, USBDeviceAddress *) + * } + */ + public final static class GetDeviceAddress { + + private GetDeviceAddress() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetDeviceAddress$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetDeviceAddress")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceAddress)(void *, USBDeviceAddress *) + * } + */ + public static final AddressLayout GetDeviceAddress$layout() { + return GetDeviceAddress$LAYOUT; + } + + private static final long GetDeviceAddress$OFFSET = $LAYOUT.byteOffset(groupElement("GetDeviceAddress")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceAddress)(void *, USBDeviceAddress *) + * } + */ + public static final long GetDeviceAddress$offset() { + return GetDeviceAddress$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceAddress)(void *, USBDeviceAddress *) + * } + */ + public static MemorySegment GetDeviceAddress(MemorySegment struct) { + return struct.get(GetDeviceAddress$LAYOUT, GetDeviceAddress$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceAddress)(void *, USBDeviceAddress *) + * } + */ + public static void GetDeviceAddress(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetDeviceAddress$LAYOUT, GetDeviceAddress$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetDeviceBusPowerAvailable)(void *, UInt32 *) + * } + */ + public final static class GetDeviceBusPowerAvailable { + + private GetDeviceBusPowerAvailable() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetDeviceBusPowerAvailable$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetDeviceBusPowerAvailable")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceBusPowerAvailable)(void *, UInt32 *) + * } + */ + public static final AddressLayout GetDeviceBusPowerAvailable$layout() { + return GetDeviceBusPowerAvailable$LAYOUT; + } + + private static final long GetDeviceBusPowerAvailable$OFFSET = $LAYOUT.byteOffset(groupElement("GetDeviceBusPowerAvailable")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceBusPowerAvailable)(void *, UInt32 *) + * } + */ + public static final long GetDeviceBusPowerAvailable$offset() { + return GetDeviceBusPowerAvailable$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceBusPowerAvailable)(void *, UInt32 *) + * } + */ + public static MemorySegment GetDeviceBusPowerAvailable(MemorySegment struct) { + return struct.get(GetDeviceBusPowerAvailable$LAYOUT, GetDeviceBusPowerAvailable$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceBusPowerAvailable)(void *, UInt32 *) + * } + */ + public static void GetDeviceBusPowerAvailable(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetDeviceBusPowerAvailable$LAYOUT, GetDeviceBusPowerAvailable$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetDeviceSpeed)(void *, UInt8 *) + * } + */ + public final static class GetDeviceSpeed { + + private GetDeviceSpeed() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetDeviceSpeed$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetDeviceSpeed")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceSpeed)(void *, UInt8 *) + * } + */ + public static final AddressLayout GetDeviceSpeed$layout() { + return GetDeviceSpeed$LAYOUT; + } + + private static final long GetDeviceSpeed$OFFSET = $LAYOUT.byteOffset(groupElement("GetDeviceSpeed")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceSpeed)(void *, UInt8 *) + * } + */ + public static final long GetDeviceSpeed$offset() { + return GetDeviceSpeed$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceSpeed)(void *, UInt8 *) + * } + */ + public static MemorySegment GetDeviceSpeed(MemorySegment struct) { + return struct.get(GetDeviceSpeed$LAYOUT, GetDeviceSpeed$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceSpeed)(void *, UInt8 *) + * } + */ + public static void GetDeviceSpeed(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetDeviceSpeed$LAYOUT, GetDeviceSpeed$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetNumberOfConfigurations)(void *, UInt8 *) + * } + */ + public final static class GetNumberOfConfigurations { + + private GetNumberOfConfigurations() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetNumberOfConfigurations$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetNumberOfConfigurations")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetNumberOfConfigurations)(void *, UInt8 *) + * } + */ + public static final AddressLayout GetNumberOfConfigurations$layout() { + return GetNumberOfConfigurations$LAYOUT; + } + + private static final long GetNumberOfConfigurations$OFFSET = $LAYOUT.byteOffset(groupElement("GetNumberOfConfigurations")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetNumberOfConfigurations)(void *, UInt8 *) + * } + */ + public static final long GetNumberOfConfigurations$offset() { + return GetNumberOfConfigurations$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetNumberOfConfigurations)(void *, UInt8 *) + * } + */ + public static MemorySegment GetNumberOfConfigurations(MemorySegment struct) { + return struct.get(GetNumberOfConfigurations$LAYOUT, GetNumberOfConfigurations$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetNumberOfConfigurations)(void *, UInt8 *) + * } + */ + public static void GetNumberOfConfigurations(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetNumberOfConfigurations$LAYOUT, GetNumberOfConfigurations$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetLocationID)(void *, UInt32 *) + * } + */ + public final static class GetLocationID { + + private GetLocationID() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetLocationID$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetLocationID")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetLocationID)(void *, UInt32 *) + * } + */ + public static final AddressLayout GetLocationID$layout() { + return GetLocationID$LAYOUT; + } + + private static final long GetLocationID$OFFSET = $LAYOUT.byteOffset(groupElement("GetLocationID")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetLocationID)(void *, UInt32 *) + * } + */ + public static final long GetLocationID$offset() { + return GetLocationID$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetLocationID)(void *, UInt32 *) + * } + */ + public static MemorySegment GetLocationID(MemorySegment struct) { + return struct.get(GetLocationID$LAYOUT, GetLocationID$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetLocationID)(void *, UInt32 *) + * } + */ + public static void GetLocationID(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetLocationID$LAYOUT, GetLocationID$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetConfigurationDescriptorPtr)(void *, UInt8, IOUSBConfigurationDescriptorPtr *) + * } + */ + public final static class GetConfigurationDescriptorPtr { + + private GetConfigurationDescriptorPtr() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1, MemorySegment _x2) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetConfigurationDescriptorPtr$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetConfigurationDescriptorPtr")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetConfigurationDescriptorPtr)(void *, UInt8, IOUSBConfigurationDescriptorPtr *) + * } + */ + public static final AddressLayout GetConfigurationDescriptorPtr$layout() { + return GetConfigurationDescriptorPtr$LAYOUT; + } + + private static final long GetConfigurationDescriptorPtr$OFFSET = $LAYOUT.byteOffset(groupElement("GetConfigurationDescriptorPtr")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetConfigurationDescriptorPtr)(void *, UInt8, IOUSBConfigurationDescriptorPtr *) + * } + */ + public static final long GetConfigurationDescriptorPtr$offset() { + return GetConfigurationDescriptorPtr$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetConfigurationDescriptorPtr)(void *, UInt8, IOUSBConfigurationDescriptorPtr *) + * } + */ + public static MemorySegment GetConfigurationDescriptorPtr(MemorySegment struct) { + return struct.get(GetConfigurationDescriptorPtr$LAYOUT, GetConfigurationDescriptorPtr$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetConfigurationDescriptorPtr)(void *, UInt8, IOUSBConfigurationDescriptorPtr *) + * } + */ + public static void GetConfigurationDescriptorPtr(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetConfigurationDescriptorPtr$LAYOUT, GetConfigurationDescriptorPtr$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetConfiguration)(void *, UInt8 *) + * } + */ + public final static class GetConfiguration { + + private GetConfiguration() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetConfiguration$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetConfiguration")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetConfiguration)(void *, UInt8 *) + * } + */ + public static final AddressLayout GetConfiguration$layout() { + return GetConfiguration$LAYOUT; + } + + private static final long GetConfiguration$OFFSET = $LAYOUT.byteOffset(groupElement("GetConfiguration")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetConfiguration)(void *, UInt8 *) + * } + */ + public static final long GetConfiguration$offset() { + return GetConfiguration$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetConfiguration)(void *, UInt8 *) + * } + */ + public static MemorySegment GetConfiguration(MemorySegment struct) { + return struct.get(GetConfiguration$LAYOUT, GetConfiguration$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetConfiguration)(void *, UInt8 *) + * } + */ + public static void GetConfiguration(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetConfiguration$LAYOUT, GetConfiguration$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*SetConfiguration)(void *, UInt8) + * } + */ + public final static class SetConfiguration { + + private SetConfiguration() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout SetConfiguration$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("SetConfiguration")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*SetConfiguration)(void *, UInt8) + * } + */ + public static final AddressLayout SetConfiguration$layout() { + return SetConfiguration$LAYOUT; + } + + private static final long SetConfiguration$OFFSET = $LAYOUT.byteOffset(groupElement("SetConfiguration")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*SetConfiguration)(void *, UInt8) + * } + */ + public static final long SetConfiguration$offset() { + return SetConfiguration$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*SetConfiguration)(void *, UInt8) + * } + */ + public static MemorySegment SetConfiguration(MemorySegment struct) { + return struct.get(SetConfiguration$LAYOUT, SetConfiguration$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*SetConfiguration)(void *, UInt8) + * } + */ + public static void SetConfiguration(MemorySegment struct, MemorySegment fieldValue) { + struct.set(SetConfiguration$LAYOUT, SetConfiguration$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetBusFrameNumber)(void *, UInt64 *, AbsoluteTime *) + * } + */ + public final static class GetBusFrameNumber { + + private GetBusFrameNumber() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1, MemorySegment _x2) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetBusFrameNumber$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetBusFrameNumber")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetBusFrameNumber)(void *, UInt64 *, AbsoluteTime *) + * } + */ + public static final AddressLayout GetBusFrameNumber$layout() { + return GetBusFrameNumber$LAYOUT; + } + + private static final long GetBusFrameNumber$OFFSET = $LAYOUT.byteOffset(groupElement("GetBusFrameNumber")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetBusFrameNumber)(void *, UInt64 *, AbsoluteTime *) + * } + */ + public static final long GetBusFrameNumber$offset() { + return GetBusFrameNumber$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetBusFrameNumber)(void *, UInt64 *, AbsoluteTime *) + * } + */ + public static MemorySegment GetBusFrameNumber(MemorySegment struct) { + return struct.get(GetBusFrameNumber$LAYOUT, GetBusFrameNumber$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetBusFrameNumber)(void *, UInt64 *, AbsoluteTime *) + * } + */ + public static void GetBusFrameNumber(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetBusFrameNumber$LAYOUT, GetBusFrameNumber$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*ResetDevice)(void *) + * } + */ + public final static class ResetDevice { + + private ResetDevice() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout ResetDevice$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("ResetDevice")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*ResetDevice)(void *) + * } + */ + public static final AddressLayout ResetDevice$layout() { + return ResetDevice$LAYOUT; + } + + private static final long ResetDevice$OFFSET = $LAYOUT.byteOffset(groupElement("ResetDevice")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*ResetDevice)(void *) + * } + */ + public static final long ResetDevice$offset() { + return ResetDevice$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*ResetDevice)(void *) + * } + */ + public static MemorySegment ResetDevice(MemorySegment struct) { + return struct.get(ResetDevice$LAYOUT, ResetDevice$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*ResetDevice)(void *) + * } + */ + public static void ResetDevice(MemorySegment struct, MemorySegment fieldValue) { + struct.set(ResetDevice$LAYOUT, ResetDevice$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*DeviceRequest)(void *, IOUSBDevRequest *) + * } + */ + public final static class DeviceRequest { + + private DeviceRequest() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout DeviceRequest$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("DeviceRequest")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*DeviceRequest)(void *, IOUSBDevRequest *) + * } + */ + public static final AddressLayout DeviceRequest$layout() { + return DeviceRequest$LAYOUT; + } + + private static final long DeviceRequest$OFFSET = $LAYOUT.byteOffset(groupElement("DeviceRequest")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*DeviceRequest)(void *, IOUSBDevRequest *) + * } + */ + public static final long DeviceRequest$offset() { + return DeviceRequest$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*DeviceRequest)(void *, IOUSBDevRequest *) + * } + */ + public static MemorySegment DeviceRequest(MemorySegment struct) { + return struct.get(DeviceRequest$LAYOUT, DeviceRequest$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*DeviceRequest)(void *, IOUSBDevRequest *) + * } + */ + public static void DeviceRequest(MemorySegment struct, MemorySegment fieldValue) { + struct.set(DeviceRequest$LAYOUT, DeviceRequest$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*DeviceRequestAsync)(void *, IOUSBDevRequest *, IOAsyncCallback1, void *) + * } + */ + public final static class DeviceRequestAsync { + + private DeviceRequestAsync() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1, MemorySegment _x2, MemorySegment _x3) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2, _x3); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout DeviceRequestAsync$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("DeviceRequestAsync")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*DeviceRequestAsync)(void *, IOUSBDevRequest *, IOAsyncCallback1, void *) + * } + */ + public static final AddressLayout DeviceRequestAsync$layout() { + return DeviceRequestAsync$LAYOUT; + } + + private static final long DeviceRequestAsync$OFFSET = $LAYOUT.byteOffset(groupElement("DeviceRequestAsync")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*DeviceRequestAsync)(void *, IOUSBDevRequest *, IOAsyncCallback1, void *) + * } + */ + public static final long DeviceRequestAsync$offset() { + return DeviceRequestAsync$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*DeviceRequestAsync)(void *, IOUSBDevRequest *, IOAsyncCallback1, void *) + * } + */ + public static MemorySegment DeviceRequestAsync(MemorySegment struct) { + return struct.get(DeviceRequestAsync$LAYOUT, DeviceRequestAsync$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*DeviceRequestAsync)(void *, IOUSBDevRequest *, IOAsyncCallback1, void *) + * } + */ + public static void DeviceRequestAsync(MemorySegment struct, MemorySegment fieldValue) { + struct.set(DeviceRequestAsync$LAYOUT, DeviceRequestAsync$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*CreateInterfaceIterator)(void *, IOUSBFindInterfaceRequest *, io_iterator_t *) + * } + */ + public final static class CreateInterfaceIterator { + + private CreateInterfaceIterator() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1, MemorySegment _x2) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout CreateInterfaceIterator$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("CreateInterfaceIterator")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*CreateInterfaceIterator)(void *, IOUSBFindInterfaceRequest *, io_iterator_t *) + * } + */ + public static final AddressLayout CreateInterfaceIterator$layout() { + return CreateInterfaceIterator$LAYOUT; + } + + private static final long CreateInterfaceIterator$OFFSET = $LAYOUT.byteOffset(groupElement("CreateInterfaceIterator")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*CreateInterfaceIterator)(void *, IOUSBFindInterfaceRequest *, io_iterator_t *) + * } + */ + public static final long CreateInterfaceIterator$offset() { + return CreateInterfaceIterator$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*CreateInterfaceIterator)(void *, IOUSBFindInterfaceRequest *, io_iterator_t *) + * } + */ + public static MemorySegment CreateInterfaceIterator(MemorySegment struct) { + return struct.get(CreateInterfaceIterator$LAYOUT, CreateInterfaceIterator$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*CreateInterfaceIterator)(void *, IOUSBFindInterfaceRequest *, io_iterator_t *) + * } + */ + public static void CreateInterfaceIterator(MemorySegment struct, MemorySegment fieldValue) { + struct.set(CreateInterfaceIterator$LAYOUT, CreateInterfaceIterator$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*USBDeviceOpenSeize)(void *) + * } + */ + public final static class USBDeviceOpenSeize { + + private USBDeviceOpenSeize() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout USBDeviceOpenSeize$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("USBDeviceOpenSeize")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceOpenSeize)(void *) + * } + */ + public static final AddressLayout USBDeviceOpenSeize$layout() { + return USBDeviceOpenSeize$LAYOUT; + } + + private static final long USBDeviceOpenSeize$OFFSET = $LAYOUT.byteOffset(groupElement("USBDeviceOpenSeize")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceOpenSeize)(void *) + * } + */ + public static final long USBDeviceOpenSeize$offset() { + return USBDeviceOpenSeize$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceOpenSeize)(void *) + * } + */ + public static MemorySegment USBDeviceOpenSeize(MemorySegment struct) { + return struct.get(USBDeviceOpenSeize$LAYOUT, USBDeviceOpenSeize$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceOpenSeize)(void *) + * } + */ + public static void USBDeviceOpenSeize(MemorySegment struct, MemorySegment fieldValue) { + struct.set(USBDeviceOpenSeize$LAYOUT, USBDeviceOpenSeize$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*DeviceRequestTO)(void *, IOUSBDevRequestTO *) + * } + */ + public final static class DeviceRequestTO { + + private DeviceRequestTO() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout DeviceRequestTO$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("DeviceRequestTO")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*DeviceRequestTO)(void *, IOUSBDevRequestTO *) + * } + */ + public static final AddressLayout DeviceRequestTO$layout() { + return DeviceRequestTO$LAYOUT; + } + + private static final long DeviceRequestTO$OFFSET = $LAYOUT.byteOffset(groupElement("DeviceRequestTO")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*DeviceRequestTO)(void *, IOUSBDevRequestTO *) + * } + */ + public static final long DeviceRequestTO$offset() { + return DeviceRequestTO$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*DeviceRequestTO)(void *, IOUSBDevRequestTO *) + * } + */ + public static MemorySegment DeviceRequestTO(MemorySegment struct) { + return struct.get(DeviceRequestTO$LAYOUT, DeviceRequestTO$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*DeviceRequestTO)(void *, IOUSBDevRequestTO *) + * } + */ + public static void DeviceRequestTO(MemorySegment struct, MemorySegment fieldValue) { + struct.set(DeviceRequestTO$LAYOUT, DeviceRequestTO$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*DeviceRequestAsyncTO)(void *, IOUSBDevRequestTO *, IOAsyncCallback1, void *) + * } + */ + public final static class DeviceRequestAsyncTO { + + private DeviceRequestAsyncTO() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1, MemorySegment _x2, MemorySegment _x3) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2, _x3); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout DeviceRequestAsyncTO$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("DeviceRequestAsyncTO")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*DeviceRequestAsyncTO)(void *, IOUSBDevRequestTO *, IOAsyncCallback1, void *) + * } + */ + public static final AddressLayout DeviceRequestAsyncTO$layout() { + return DeviceRequestAsyncTO$LAYOUT; + } + + private static final long DeviceRequestAsyncTO$OFFSET = $LAYOUT.byteOffset(groupElement("DeviceRequestAsyncTO")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*DeviceRequestAsyncTO)(void *, IOUSBDevRequestTO *, IOAsyncCallback1, void *) + * } + */ + public static final long DeviceRequestAsyncTO$offset() { + return DeviceRequestAsyncTO$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*DeviceRequestAsyncTO)(void *, IOUSBDevRequestTO *, IOAsyncCallback1, void *) + * } + */ + public static MemorySegment DeviceRequestAsyncTO(MemorySegment struct) { + return struct.get(DeviceRequestAsyncTO$LAYOUT, DeviceRequestAsyncTO$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*DeviceRequestAsyncTO)(void *, IOUSBDevRequestTO *, IOAsyncCallback1, void *) + * } + */ + public static void DeviceRequestAsyncTO(MemorySegment struct, MemorySegment fieldValue) { + struct.set(DeviceRequestAsyncTO$LAYOUT, DeviceRequestAsyncTO$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*USBDeviceSuspend)(void *, Boolean) + * } + */ + public final static class USBDeviceSuspend { + + private USBDeviceSuspend() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout USBDeviceSuspend$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("USBDeviceSuspend")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceSuspend)(void *, Boolean) + * } + */ + public static final AddressLayout USBDeviceSuspend$layout() { + return USBDeviceSuspend$LAYOUT; + } + + private static final long USBDeviceSuspend$OFFSET = $LAYOUT.byteOffset(groupElement("USBDeviceSuspend")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceSuspend)(void *, Boolean) + * } + */ + public static final long USBDeviceSuspend$offset() { + return USBDeviceSuspend$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceSuspend)(void *, Boolean) + * } + */ + public static MemorySegment USBDeviceSuspend(MemorySegment struct) { + return struct.get(USBDeviceSuspend$LAYOUT, USBDeviceSuspend$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceSuspend)(void *, Boolean) + * } + */ + public static void USBDeviceSuspend(MemorySegment struct, MemorySegment fieldValue) { + struct.set(USBDeviceSuspend$LAYOUT, USBDeviceSuspend$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*USBDeviceAbortPipeZero)(void *) + * } + */ + public final static class USBDeviceAbortPipeZero { + + private USBDeviceAbortPipeZero() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout USBDeviceAbortPipeZero$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("USBDeviceAbortPipeZero")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceAbortPipeZero)(void *) + * } + */ + public static final AddressLayout USBDeviceAbortPipeZero$layout() { + return USBDeviceAbortPipeZero$LAYOUT; + } + + private static final long USBDeviceAbortPipeZero$OFFSET = $LAYOUT.byteOffset(groupElement("USBDeviceAbortPipeZero")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceAbortPipeZero)(void *) + * } + */ + public static final long USBDeviceAbortPipeZero$offset() { + return USBDeviceAbortPipeZero$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceAbortPipeZero)(void *) + * } + */ + public static MemorySegment USBDeviceAbortPipeZero(MemorySegment struct) { + return struct.get(USBDeviceAbortPipeZero$LAYOUT, USBDeviceAbortPipeZero$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceAbortPipeZero)(void *) + * } + */ + public static void USBDeviceAbortPipeZero(MemorySegment struct, MemorySegment fieldValue) { + struct.set(USBDeviceAbortPipeZero$LAYOUT, USBDeviceAbortPipeZero$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*USBGetManufacturerStringIndex)(void *, UInt8 *) + * } + */ + public final static class USBGetManufacturerStringIndex { + + private USBGetManufacturerStringIndex() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout USBGetManufacturerStringIndex$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("USBGetManufacturerStringIndex")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*USBGetManufacturerStringIndex)(void *, UInt8 *) + * } + */ + public static final AddressLayout USBGetManufacturerStringIndex$layout() { + return USBGetManufacturerStringIndex$LAYOUT; + } + + private static final long USBGetManufacturerStringIndex$OFFSET = $LAYOUT.byteOffset(groupElement("USBGetManufacturerStringIndex")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*USBGetManufacturerStringIndex)(void *, UInt8 *) + * } + */ + public static final long USBGetManufacturerStringIndex$offset() { + return USBGetManufacturerStringIndex$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*USBGetManufacturerStringIndex)(void *, UInt8 *) + * } + */ + public static MemorySegment USBGetManufacturerStringIndex(MemorySegment struct) { + return struct.get(USBGetManufacturerStringIndex$LAYOUT, USBGetManufacturerStringIndex$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*USBGetManufacturerStringIndex)(void *, UInt8 *) + * } + */ + public static void USBGetManufacturerStringIndex(MemorySegment struct, MemorySegment fieldValue) { + struct.set(USBGetManufacturerStringIndex$LAYOUT, USBGetManufacturerStringIndex$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*USBGetProductStringIndex)(void *, UInt8 *) + * } + */ + public final static class USBGetProductStringIndex { + + private USBGetProductStringIndex() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout USBGetProductStringIndex$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("USBGetProductStringIndex")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*USBGetProductStringIndex)(void *, UInt8 *) + * } + */ + public static final AddressLayout USBGetProductStringIndex$layout() { + return USBGetProductStringIndex$LAYOUT; + } + + private static final long USBGetProductStringIndex$OFFSET = $LAYOUT.byteOffset(groupElement("USBGetProductStringIndex")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*USBGetProductStringIndex)(void *, UInt8 *) + * } + */ + public static final long USBGetProductStringIndex$offset() { + return USBGetProductStringIndex$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*USBGetProductStringIndex)(void *, UInt8 *) + * } + */ + public static MemorySegment USBGetProductStringIndex(MemorySegment struct) { + return struct.get(USBGetProductStringIndex$LAYOUT, USBGetProductStringIndex$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*USBGetProductStringIndex)(void *, UInt8 *) + * } + */ + public static void USBGetProductStringIndex(MemorySegment struct, MemorySegment fieldValue) { + struct.set(USBGetProductStringIndex$LAYOUT, USBGetProductStringIndex$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*USBGetSerialNumberStringIndex)(void *, UInt8 *) + * } + */ + public final static class USBGetSerialNumberStringIndex { + + private USBGetSerialNumberStringIndex() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout USBGetSerialNumberStringIndex$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("USBGetSerialNumberStringIndex")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*USBGetSerialNumberStringIndex)(void *, UInt8 *) + * } + */ + public static final AddressLayout USBGetSerialNumberStringIndex$layout() { + return USBGetSerialNumberStringIndex$LAYOUT; + } + + private static final long USBGetSerialNumberStringIndex$OFFSET = $LAYOUT.byteOffset(groupElement("USBGetSerialNumberStringIndex")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*USBGetSerialNumberStringIndex)(void *, UInt8 *) + * } + */ + public static final long USBGetSerialNumberStringIndex$offset() { + return USBGetSerialNumberStringIndex$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*USBGetSerialNumberStringIndex)(void *, UInt8 *) + * } + */ + public static MemorySegment USBGetSerialNumberStringIndex(MemorySegment struct) { + return struct.get(USBGetSerialNumberStringIndex$LAYOUT, USBGetSerialNumberStringIndex$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*USBGetSerialNumberStringIndex)(void *, UInt8 *) + * } + */ + public static void USBGetSerialNumberStringIndex(MemorySegment struct, MemorySegment fieldValue) { + struct.set(USBGetSerialNumberStringIndex$LAYOUT, USBGetSerialNumberStringIndex$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*USBDeviceReEnumerate)(void *, UInt32) + * } + */ + public final static class USBDeviceReEnumerate { + + private USBDeviceReEnumerate() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_INT + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, int _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout USBDeviceReEnumerate$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("USBDeviceReEnumerate")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceReEnumerate)(void *, UInt32) + * } + */ + public static final AddressLayout USBDeviceReEnumerate$layout() { + return USBDeviceReEnumerate$LAYOUT; + } + + private static final long USBDeviceReEnumerate$OFFSET = $LAYOUT.byteOffset(groupElement("USBDeviceReEnumerate")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceReEnumerate)(void *, UInt32) + * } + */ + public static final long USBDeviceReEnumerate$offset() { + return USBDeviceReEnumerate$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceReEnumerate)(void *, UInt32) + * } + */ + public static MemorySegment USBDeviceReEnumerate(MemorySegment struct) { + return struct.get(USBDeviceReEnumerate$LAYOUT, USBDeviceReEnumerate$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*USBDeviceReEnumerate)(void *, UInt32) + * } + */ + public static void USBDeviceReEnumerate(MemorySegment struct, MemorySegment fieldValue) { + struct.set(USBDeviceReEnumerate$LAYOUT, USBDeviceReEnumerate$OFFSET, fieldValue); + } + + /** + * Obtains a slice of {@code arrayParam} which selects the array element at {@code index}. + * The returned segment has address {@code arrayParam.address() + index * layout().byteSize()} + */ + public static MemorySegment asSlice(MemorySegment array, long index) { + return array.asSlice(layout().byteSize() * index); + } + + /** + * The size (in bytes) of this struct + */ + public static long sizeof() { return layout().byteSize(); } + + /** + * Allocate a segment of size {@code layout().byteSize()} using {@code allocator} + */ + public static MemorySegment allocate(SegmentAllocator allocator) { + return allocator.allocate(layout()); + } + + /** + * Allocate an array of size {@code elementCount} using {@code allocator}. + * The returned segment has size {@code elementCount * layout().byteSize()}. + */ + public static MemorySegment allocateArray(long elementCount, SegmentAllocator allocator) { + return allocator.allocate(MemoryLayout.sequenceLayout(elementCount, layout())); + } + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, Arena arena, Consumer cleanup) { + return reinterpret(addr, 1, arena, cleanup); + } + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code elementCount * layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, long elementCount, Arena arena, Consumer cleanup) { + return addr.reinterpret(layout().byteSize() * elementCount, arena, cleanup); + } +} + diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBDeviceStruct942.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBDeviceStruct942.java deleted file mode 100644 index 064a6458..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBDeviceStruct942.java +++ /dev/null @@ -1,2317 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.macos.gen.iokit; - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.VarHandle; -public class IOUSBDeviceStruct942 { - - static final GroupLayout $struct$LAYOUT = MemoryLayout.structLayout( - Constants$root.C_POINTER$LAYOUT.withName("_reserved"), - Constants$root.C_POINTER$LAYOUT.withName("QueryInterface"), - Constants$root.C_POINTER$LAYOUT.withName("AddRef"), - Constants$root.C_POINTER$LAYOUT.withName("Release"), - Constants$root.C_POINTER$LAYOUT.withName("CreateDeviceAsyncEventSource"), - Constants$root.C_POINTER$LAYOUT.withName("GetDeviceAsyncEventSource"), - Constants$root.C_POINTER$LAYOUT.withName("CreateDeviceAsyncPort"), - Constants$root.C_POINTER$LAYOUT.withName("GetDeviceAsyncPort"), - Constants$root.C_POINTER$LAYOUT.withName("USBDeviceOpen"), - Constants$root.C_POINTER$LAYOUT.withName("USBDeviceClose"), - Constants$root.C_POINTER$LAYOUT.withName("GetDeviceClass"), - Constants$root.C_POINTER$LAYOUT.withName("GetDeviceSubClass"), - Constants$root.C_POINTER$LAYOUT.withName("GetDeviceProtocol"), - Constants$root.C_POINTER$LAYOUT.withName("GetDeviceVendor"), - Constants$root.C_POINTER$LAYOUT.withName("GetDeviceProduct"), - Constants$root.C_POINTER$LAYOUT.withName("GetDeviceReleaseNumber"), - Constants$root.C_POINTER$LAYOUT.withName("GetDeviceAddress"), - Constants$root.C_POINTER$LAYOUT.withName("GetDeviceBusPowerAvailable"), - Constants$root.C_POINTER$LAYOUT.withName("GetDeviceSpeed"), - Constants$root.C_POINTER$LAYOUT.withName("GetNumberOfConfigurations"), - Constants$root.C_POINTER$LAYOUT.withName("GetLocationID"), - Constants$root.C_POINTER$LAYOUT.withName("GetConfigurationDescriptorPtr"), - Constants$root.C_POINTER$LAYOUT.withName("GetConfiguration"), - Constants$root.C_POINTER$LAYOUT.withName("SetConfiguration"), - Constants$root.C_POINTER$LAYOUT.withName("GetBusFrameNumber"), - Constants$root.C_POINTER$LAYOUT.withName("ResetDevice"), - Constants$root.C_POINTER$LAYOUT.withName("DeviceRequest"), - Constants$root.C_POINTER$LAYOUT.withName("DeviceRequestAsync"), - Constants$root.C_POINTER$LAYOUT.withName("CreateInterfaceIterator"), - Constants$root.C_POINTER$LAYOUT.withName("USBDeviceOpenSeize"), - Constants$root.C_POINTER$LAYOUT.withName("DeviceRequestTO"), - Constants$root.C_POINTER$LAYOUT.withName("DeviceRequestAsyncTO"), - Constants$root.C_POINTER$LAYOUT.withName("USBDeviceSuspend"), - Constants$root.C_POINTER$LAYOUT.withName("USBDeviceAbortPipeZero"), - Constants$root.C_POINTER$LAYOUT.withName("USBGetManufacturerStringIndex"), - Constants$root.C_POINTER$LAYOUT.withName("USBGetProductStringIndex"), - Constants$root.C_POINTER$LAYOUT.withName("USBGetSerialNumberStringIndex"), - Constants$root.C_POINTER$LAYOUT.withName("USBDeviceReEnumerate"), - Constants$root.C_POINTER$LAYOUT.withName("GetBusMicroFrameNumber"), - Constants$root.C_POINTER$LAYOUT.withName("GetIOUSBLibVersion"), - Constants$root.C_POINTER$LAYOUT.withName("GetBusFrameNumberWithTime"), - Constants$root.C_POINTER$LAYOUT.withName("GetUSBDeviceInformation"), - Constants$root.C_POINTER$LAYOUT.withName("RequestExtraPower"), - Constants$root.C_POINTER$LAYOUT.withName("ReturnExtraPower"), - Constants$root.C_POINTER$LAYOUT.withName("GetExtraPowerAllocated"), - Constants$root.C_POINTER$LAYOUT.withName("GetBandwidthAvailableForDevice"), - Constants$root.C_POINTER$LAYOUT.withName("SetConfigurationV2"), - Constants$root.C_POINTER$LAYOUT.withName("RegisterForNotification"), - Constants$root.C_POINTER$LAYOUT.withName("UnregisterNotification"), - Constants$root.C_POINTER$LAYOUT.withName("AcknowledgeNotification"), - Constants$root.C_POINTER$LAYOUT.withName("GetDeviceAsyncNotificationPort") - ).withName("IOUSBDeviceStruct942"); - public static MemoryLayout $LAYOUT() { - return IOUSBDeviceStruct942.$struct$LAYOUT; - } - static final VarHandle _reserved$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("_reserved")); - public static VarHandle _reserved$VH() { - return IOUSBDeviceStruct942._reserved$VH; - } - public static MemoryAddress _reserved$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942._reserved$VH.get(seg); - } - public static void _reserved$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942._reserved$VH.set(seg, x); - } - public static MemoryAddress _reserved$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942._reserved$VH.get(seg.asSlice(index*sizeof())); - } - public static void _reserved$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942._reserved$VH.set(seg.asSlice(index*sizeof()), x); - } - static final FunctionDescriptor QueryInterface$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - MemoryLayout.structLayout( - Constants$root.C_CHAR$LAYOUT.withName("byte0"), - Constants$root.C_CHAR$LAYOUT.withName("byte1"), - Constants$root.C_CHAR$LAYOUT.withName("byte2"), - Constants$root.C_CHAR$LAYOUT.withName("byte3"), - Constants$root.C_CHAR$LAYOUT.withName("byte4"), - Constants$root.C_CHAR$LAYOUT.withName("byte5"), - Constants$root.C_CHAR$LAYOUT.withName("byte6"), - Constants$root.C_CHAR$LAYOUT.withName("byte7"), - Constants$root.C_CHAR$LAYOUT.withName("byte8"), - Constants$root.C_CHAR$LAYOUT.withName("byte9"), - Constants$root.C_CHAR$LAYOUT.withName("byte10"), - Constants$root.C_CHAR$LAYOUT.withName("byte11"), - Constants$root.C_CHAR$LAYOUT.withName("byte12"), - Constants$root.C_CHAR$LAYOUT.withName("byte13"), - Constants$root.C_CHAR$LAYOUT.withName("byte14"), - Constants$root.C_CHAR$LAYOUT.withName("byte15") - ), - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle QueryInterface$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.QueryInterface$FUNC - ); - public interface QueryInterface { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemorySegment _x1, java.lang.foreign.MemoryAddress _x2); - static MemorySegment allocate(QueryInterface fi, MemorySession session) { - return RuntimeHelper.upcallStub(QueryInterface.class, fi, IOUSBDeviceStruct942.QueryInterface$FUNC, session); - } - static QueryInterface ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemorySegment __x1, java.lang.foreign.MemoryAddress __x2) -> { - try { - return (int)IOUSBDeviceStruct942.QueryInterface$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle QueryInterface$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("QueryInterface")); - public static VarHandle QueryInterface$VH() { - return IOUSBDeviceStruct942.QueryInterface$VH; - } - public static MemoryAddress QueryInterface$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.QueryInterface$VH.get(seg); - } - public static void QueryInterface$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.QueryInterface$VH.set(seg, x); - } - public static MemoryAddress QueryInterface$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.QueryInterface$VH.get(seg.asSlice(index*sizeof())); - } - public static void QueryInterface$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.QueryInterface$VH.set(seg.asSlice(index*sizeof()), x); - } - public static QueryInterface QueryInterface (MemorySegment segment, MemorySession session) { - return QueryInterface.ofAddress(QueryInterface$get(segment), session); - } - static final FunctionDescriptor AddRef$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle AddRef$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.AddRef$FUNC - ); - public interface AddRef { - - int apply(java.lang.foreign.MemoryAddress _x0); - static MemorySegment allocate(AddRef fi, MemorySession session) { - return RuntimeHelper.upcallStub(AddRef.class, fi, IOUSBDeviceStruct942.AddRef$FUNC, session); - } - static AddRef ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0) -> { - try { - return (int)IOUSBDeviceStruct942.AddRef$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle AddRef$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("AddRef")); - public static VarHandle AddRef$VH() { - return IOUSBDeviceStruct942.AddRef$VH; - } - public static MemoryAddress AddRef$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.AddRef$VH.get(seg); - } - public static void AddRef$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.AddRef$VH.set(seg, x); - } - public static MemoryAddress AddRef$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.AddRef$VH.get(seg.asSlice(index*sizeof())); - } - public static void AddRef$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.AddRef$VH.set(seg.asSlice(index*sizeof()), x); - } - public static AddRef AddRef (MemorySegment segment, MemorySession session) { - return AddRef.ofAddress(AddRef$get(segment), session); - } - static final FunctionDescriptor Release$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle Release$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.Release$FUNC - ); - public interface Release { - - int apply(java.lang.foreign.MemoryAddress _x0); - static MemorySegment allocate(Release fi, MemorySession session) { - return RuntimeHelper.upcallStub(Release.class, fi, IOUSBDeviceStruct942.Release$FUNC, session); - } - static Release ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0) -> { - try { - return (int)IOUSBDeviceStruct942.Release$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle Release$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("Release")); - public static VarHandle Release$VH() { - return IOUSBDeviceStruct942.Release$VH; - } - public static MemoryAddress Release$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.Release$VH.get(seg); - } - public static void Release$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.Release$VH.set(seg, x); - } - public static MemoryAddress Release$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.Release$VH.get(seg.asSlice(index*sizeof())); - } - public static void Release$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.Release$VH.set(seg.asSlice(index*sizeof()), x); - } - public static Release Release (MemorySegment segment, MemorySession session) { - return Release.ofAddress(Release$get(segment), session); - } - static final FunctionDescriptor CreateDeviceAsyncEventSource$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle CreateDeviceAsyncEventSource$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.CreateDeviceAsyncEventSource$FUNC - ); - public interface CreateDeviceAsyncEventSource { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(CreateDeviceAsyncEventSource fi, MemorySession session) { - return RuntimeHelper.upcallStub(CreateDeviceAsyncEventSource.class, fi, IOUSBDeviceStruct942.CreateDeviceAsyncEventSource$FUNC, session); - } - static CreateDeviceAsyncEventSource ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBDeviceStruct942.CreateDeviceAsyncEventSource$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle CreateDeviceAsyncEventSource$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("CreateDeviceAsyncEventSource")); - public static VarHandle CreateDeviceAsyncEventSource$VH() { - return IOUSBDeviceStruct942.CreateDeviceAsyncEventSource$VH; - } - public static MemoryAddress CreateDeviceAsyncEventSource$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.CreateDeviceAsyncEventSource$VH.get(seg); - } - public static void CreateDeviceAsyncEventSource$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.CreateDeviceAsyncEventSource$VH.set(seg, x); - } - public static MemoryAddress CreateDeviceAsyncEventSource$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.CreateDeviceAsyncEventSource$VH.get(seg.asSlice(index*sizeof())); - } - public static void CreateDeviceAsyncEventSource$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.CreateDeviceAsyncEventSource$VH.set(seg.asSlice(index*sizeof()), x); - } - public static CreateDeviceAsyncEventSource CreateDeviceAsyncEventSource (MemorySegment segment, MemorySession session) { - return CreateDeviceAsyncEventSource.ofAddress(CreateDeviceAsyncEventSource$get(segment), session); - } - static final FunctionDescriptor GetDeviceAsyncEventSource$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetDeviceAsyncEventSource$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.GetDeviceAsyncEventSource$FUNC - ); - public interface GetDeviceAsyncEventSource { - - java.lang.foreign.Addressable apply(java.lang.foreign.MemoryAddress _x0); - static MemorySegment allocate(GetDeviceAsyncEventSource fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetDeviceAsyncEventSource.class, fi, IOUSBDeviceStruct942.GetDeviceAsyncEventSource$FUNC, session); - } - static GetDeviceAsyncEventSource ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0) -> { - try { - return (java.lang.foreign.Addressable)(java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceAsyncEventSource$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetDeviceAsyncEventSource$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetDeviceAsyncEventSource")); - public static VarHandle GetDeviceAsyncEventSource$VH() { - return IOUSBDeviceStruct942.GetDeviceAsyncEventSource$VH; - } - public static MemoryAddress GetDeviceAsyncEventSource$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceAsyncEventSource$VH.get(seg); - } - public static void GetDeviceAsyncEventSource$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceAsyncEventSource$VH.set(seg, x); - } - public static MemoryAddress GetDeviceAsyncEventSource$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceAsyncEventSource$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetDeviceAsyncEventSource$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceAsyncEventSource$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetDeviceAsyncEventSource GetDeviceAsyncEventSource (MemorySegment segment, MemorySession session) { - return GetDeviceAsyncEventSource.ofAddress(GetDeviceAsyncEventSource$get(segment), session); - } - static final FunctionDescriptor CreateDeviceAsyncPort$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle CreateDeviceAsyncPort$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.CreateDeviceAsyncPort$FUNC - ); - public interface CreateDeviceAsyncPort { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(CreateDeviceAsyncPort fi, MemorySession session) { - return RuntimeHelper.upcallStub(CreateDeviceAsyncPort.class, fi, IOUSBDeviceStruct942.CreateDeviceAsyncPort$FUNC, session); - } - static CreateDeviceAsyncPort ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBDeviceStruct942.CreateDeviceAsyncPort$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle CreateDeviceAsyncPort$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("CreateDeviceAsyncPort")); - public static VarHandle CreateDeviceAsyncPort$VH() { - return IOUSBDeviceStruct942.CreateDeviceAsyncPort$VH; - } - public static MemoryAddress CreateDeviceAsyncPort$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.CreateDeviceAsyncPort$VH.get(seg); - } - public static void CreateDeviceAsyncPort$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.CreateDeviceAsyncPort$VH.set(seg, x); - } - public static MemoryAddress CreateDeviceAsyncPort$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.CreateDeviceAsyncPort$VH.get(seg.asSlice(index*sizeof())); - } - public static void CreateDeviceAsyncPort$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.CreateDeviceAsyncPort$VH.set(seg.asSlice(index*sizeof()), x); - } - public static CreateDeviceAsyncPort CreateDeviceAsyncPort (MemorySegment segment, MemorySession session) { - return CreateDeviceAsyncPort.ofAddress(CreateDeviceAsyncPort$get(segment), session); - } - static final FunctionDescriptor GetDeviceAsyncPort$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetDeviceAsyncPort$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.GetDeviceAsyncPort$FUNC - ); - public interface GetDeviceAsyncPort { - - int apply(java.lang.foreign.MemoryAddress _x0); - static MemorySegment allocate(GetDeviceAsyncPort fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetDeviceAsyncPort.class, fi, IOUSBDeviceStruct942.GetDeviceAsyncPort$FUNC, session); - } - static GetDeviceAsyncPort ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0) -> { - try { - return (int)IOUSBDeviceStruct942.GetDeviceAsyncPort$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetDeviceAsyncPort$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetDeviceAsyncPort")); - public static VarHandle GetDeviceAsyncPort$VH() { - return IOUSBDeviceStruct942.GetDeviceAsyncPort$VH; - } - public static MemoryAddress GetDeviceAsyncPort$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceAsyncPort$VH.get(seg); - } - public static void GetDeviceAsyncPort$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceAsyncPort$VH.set(seg, x); - } - public static MemoryAddress GetDeviceAsyncPort$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceAsyncPort$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetDeviceAsyncPort$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceAsyncPort$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetDeviceAsyncPort GetDeviceAsyncPort (MemorySegment segment, MemorySession session) { - return GetDeviceAsyncPort.ofAddress(GetDeviceAsyncPort$get(segment), session); - } - static final FunctionDescriptor USBDeviceOpen$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle USBDeviceOpen$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.USBDeviceOpen$FUNC - ); - public interface USBDeviceOpen { - - int apply(java.lang.foreign.MemoryAddress _x0); - static MemorySegment allocate(USBDeviceOpen fi, MemorySession session) { - return RuntimeHelper.upcallStub(USBDeviceOpen.class, fi, IOUSBDeviceStruct942.USBDeviceOpen$FUNC, session); - } - static USBDeviceOpen ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0) -> { - try { - return (int)IOUSBDeviceStruct942.USBDeviceOpen$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle USBDeviceOpen$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("USBDeviceOpen")); - public static VarHandle USBDeviceOpen$VH() { - return IOUSBDeviceStruct942.USBDeviceOpen$VH; - } - public static MemoryAddress USBDeviceOpen$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.USBDeviceOpen$VH.get(seg); - } - public static void USBDeviceOpen$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.USBDeviceOpen$VH.set(seg, x); - } - public static MemoryAddress USBDeviceOpen$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.USBDeviceOpen$VH.get(seg.asSlice(index*sizeof())); - } - public static void USBDeviceOpen$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.USBDeviceOpen$VH.set(seg.asSlice(index*sizeof()), x); - } - public static USBDeviceOpen USBDeviceOpen (MemorySegment segment, MemorySession session) { - return USBDeviceOpen.ofAddress(USBDeviceOpen$get(segment), session); - } - static final FunctionDescriptor USBDeviceClose$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle USBDeviceClose$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.USBDeviceClose$FUNC - ); - public interface USBDeviceClose { - - int apply(java.lang.foreign.MemoryAddress _x0); - static MemorySegment allocate(USBDeviceClose fi, MemorySession session) { - return RuntimeHelper.upcallStub(USBDeviceClose.class, fi, IOUSBDeviceStruct942.USBDeviceClose$FUNC, session); - } - static USBDeviceClose ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0) -> { - try { - return (int)IOUSBDeviceStruct942.USBDeviceClose$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle USBDeviceClose$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("USBDeviceClose")); - public static VarHandle USBDeviceClose$VH() { - return IOUSBDeviceStruct942.USBDeviceClose$VH; - } - public static MemoryAddress USBDeviceClose$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.USBDeviceClose$VH.get(seg); - } - public static void USBDeviceClose$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.USBDeviceClose$VH.set(seg, x); - } - public static MemoryAddress USBDeviceClose$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.USBDeviceClose$VH.get(seg.asSlice(index*sizeof())); - } - public static void USBDeviceClose$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.USBDeviceClose$VH.set(seg.asSlice(index*sizeof()), x); - } - public static USBDeviceClose USBDeviceClose (MemorySegment segment, MemorySession session) { - return USBDeviceClose.ofAddress(USBDeviceClose$get(segment), session); - } - static final FunctionDescriptor GetDeviceClass$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetDeviceClass$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.GetDeviceClass$FUNC - ); - public interface GetDeviceClass { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetDeviceClass fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetDeviceClass.class, fi, IOUSBDeviceStruct942.GetDeviceClass$FUNC, session); - } - static GetDeviceClass ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBDeviceStruct942.GetDeviceClass$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetDeviceClass$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetDeviceClass")); - public static VarHandle GetDeviceClass$VH() { - return IOUSBDeviceStruct942.GetDeviceClass$VH; - } - public static MemoryAddress GetDeviceClass$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceClass$VH.get(seg); - } - public static void GetDeviceClass$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceClass$VH.set(seg, x); - } - public static MemoryAddress GetDeviceClass$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceClass$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetDeviceClass$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceClass$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetDeviceClass GetDeviceClass (MemorySegment segment, MemorySession session) { - return GetDeviceClass.ofAddress(GetDeviceClass$get(segment), session); - } - static final FunctionDescriptor GetDeviceSubClass$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetDeviceSubClass$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.GetDeviceSubClass$FUNC - ); - public interface GetDeviceSubClass { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetDeviceSubClass fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetDeviceSubClass.class, fi, IOUSBDeviceStruct942.GetDeviceSubClass$FUNC, session); - } - static GetDeviceSubClass ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBDeviceStruct942.GetDeviceSubClass$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetDeviceSubClass$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetDeviceSubClass")); - public static VarHandle GetDeviceSubClass$VH() { - return IOUSBDeviceStruct942.GetDeviceSubClass$VH; - } - public static MemoryAddress GetDeviceSubClass$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceSubClass$VH.get(seg); - } - public static void GetDeviceSubClass$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceSubClass$VH.set(seg, x); - } - public static MemoryAddress GetDeviceSubClass$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceSubClass$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetDeviceSubClass$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceSubClass$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetDeviceSubClass GetDeviceSubClass (MemorySegment segment, MemorySession session) { - return GetDeviceSubClass.ofAddress(GetDeviceSubClass$get(segment), session); - } - static final FunctionDescriptor GetDeviceProtocol$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetDeviceProtocol$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.GetDeviceProtocol$FUNC - ); - public interface GetDeviceProtocol { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetDeviceProtocol fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetDeviceProtocol.class, fi, IOUSBDeviceStruct942.GetDeviceProtocol$FUNC, session); - } - static GetDeviceProtocol ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBDeviceStruct942.GetDeviceProtocol$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetDeviceProtocol$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetDeviceProtocol")); - public static VarHandle GetDeviceProtocol$VH() { - return IOUSBDeviceStruct942.GetDeviceProtocol$VH; - } - public static MemoryAddress GetDeviceProtocol$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceProtocol$VH.get(seg); - } - public static void GetDeviceProtocol$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceProtocol$VH.set(seg, x); - } - public static MemoryAddress GetDeviceProtocol$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceProtocol$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetDeviceProtocol$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceProtocol$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetDeviceProtocol GetDeviceProtocol (MemorySegment segment, MemorySession session) { - return GetDeviceProtocol.ofAddress(GetDeviceProtocol$get(segment), session); - } - static final FunctionDescriptor GetDeviceVendor$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetDeviceVendor$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.GetDeviceVendor$FUNC - ); - public interface GetDeviceVendor { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetDeviceVendor fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetDeviceVendor.class, fi, IOUSBDeviceStruct942.GetDeviceVendor$FUNC, session); - } - static GetDeviceVendor ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBDeviceStruct942.GetDeviceVendor$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetDeviceVendor$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetDeviceVendor")); - public static VarHandle GetDeviceVendor$VH() { - return IOUSBDeviceStruct942.GetDeviceVendor$VH; - } - public static MemoryAddress GetDeviceVendor$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceVendor$VH.get(seg); - } - public static void GetDeviceVendor$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceVendor$VH.set(seg, x); - } - public static MemoryAddress GetDeviceVendor$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceVendor$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetDeviceVendor$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceVendor$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetDeviceVendor GetDeviceVendor (MemorySegment segment, MemorySession session) { - return GetDeviceVendor.ofAddress(GetDeviceVendor$get(segment), session); - } - static final FunctionDescriptor GetDeviceProduct$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetDeviceProduct$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.GetDeviceProduct$FUNC - ); - public interface GetDeviceProduct { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetDeviceProduct fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetDeviceProduct.class, fi, IOUSBDeviceStruct942.GetDeviceProduct$FUNC, session); - } - static GetDeviceProduct ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBDeviceStruct942.GetDeviceProduct$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetDeviceProduct$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetDeviceProduct")); - public static VarHandle GetDeviceProduct$VH() { - return IOUSBDeviceStruct942.GetDeviceProduct$VH; - } - public static MemoryAddress GetDeviceProduct$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceProduct$VH.get(seg); - } - public static void GetDeviceProduct$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceProduct$VH.set(seg, x); - } - public static MemoryAddress GetDeviceProduct$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceProduct$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetDeviceProduct$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceProduct$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetDeviceProduct GetDeviceProduct (MemorySegment segment, MemorySession session) { - return GetDeviceProduct.ofAddress(GetDeviceProduct$get(segment), session); - } - static final FunctionDescriptor GetDeviceReleaseNumber$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetDeviceReleaseNumber$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.GetDeviceReleaseNumber$FUNC - ); - public interface GetDeviceReleaseNumber { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetDeviceReleaseNumber fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetDeviceReleaseNumber.class, fi, IOUSBDeviceStruct942.GetDeviceReleaseNumber$FUNC, session); - } - static GetDeviceReleaseNumber ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBDeviceStruct942.GetDeviceReleaseNumber$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetDeviceReleaseNumber$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetDeviceReleaseNumber")); - public static VarHandle GetDeviceReleaseNumber$VH() { - return IOUSBDeviceStruct942.GetDeviceReleaseNumber$VH; - } - public static MemoryAddress GetDeviceReleaseNumber$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceReleaseNumber$VH.get(seg); - } - public static void GetDeviceReleaseNumber$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceReleaseNumber$VH.set(seg, x); - } - public static MemoryAddress GetDeviceReleaseNumber$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceReleaseNumber$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetDeviceReleaseNumber$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceReleaseNumber$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetDeviceReleaseNumber GetDeviceReleaseNumber (MemorySegment segment, MemorySession session) { - return GetDeviceReleaseNumber.ofAddress(GetDeviceReleaseNumber$get(segment), session); - } - static final FunctionDescriptor GetDeviceAddress$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetDeviceAddress$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.GetDeviceAddress$FUNC - ); - public interface GetDeviceAddress { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetDeviceAddress fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetDeviceAddress.class, fi, IOUSBDeviceStruct942.GetDeviceAddress$FUNC, session); - } - static GetDeviceAddress ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBDeviceStruct942.GetDeviceAddress$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetDeviceAddress$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetDeviceAddress")); - public static VarHandle GetDeviceAddress$VH() { - return IOUSBDeviceStruct942.GetDeviceAddress$VH; - } - public static MemoryAddress GetDeviceAddress$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceAddress$VH.get(seg); - } - public static void GetDeviceAddress$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceAddress$VH.set(seg, x); - } - public static MemoryAddress GetDeviceAddress$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceAddress$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetDeviceAddress$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceAddress$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetDeviceAddress GetDeviceAddress (MemorySegment segment, MemorySession session) { - return GetDeviceAddress.ofAddress(GetDeviceAddress$get(segment), session); - } - static final FunctionDescriptor GetDeviceBusPowerAvailable$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetDeviceBusPowerAvailable$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.GetDeviceBusPowerAvailable$FUNC - ); - public interface GetDeviceBusPowerAvailable { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetDeviceBusPowerAvailable fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetDeviceBusPowerAvailable.class, fi, IOUSBDeviceStruct942.GetDeviceBusPowerAvailable$FUNC, session); - } - static GetDeviceBusPowerAvailable ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBDeviceStruct942.GetDeviceBusPowerAvailable$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetDeviceBusPowerAvailable$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetDeviceBusPowerAvailable")); - public static VarHandle GetDeviceBusPowerAvailable$VH() { - return IOUSBDeviceStruct942.GetDeviceBusPowerAvailable$VH; - } - public static MemoryAddress GetDeviceBusPowerAvailable$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceBusPowerAvailable$VH.get(seg); - } - public static void GetDeviceBusPowerAvailable$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceBusPowerAvailable$VH.set(seg, x); - } - public static MemoryAddress GetDeviceBusPowerAvailable$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceBusPowerAvailable$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetDeviceBusPowerAvailable$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceBusPowerAvailable$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetDeviceBusPowerAvailable GetDeviceBusPowerAvailable (MemorySegment segment, MemorySession session) { - return GetDeviceBusPowerAvailable.ofAddress(GetDeviceBusPowerAvailable$get(segment), session); - } - static final FunctionDescriptor GetDeviceSpeed$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetDeviceSpeed$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.GetDeviceSpeed$FUNC - ); - public interface GetDeviceSpeed { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetDeviceSpeed fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetDeviceSpeed.class, fi, IOUSBDeviceStruct942.GetDeviceSpeed$FUNC, session); - } - static GetDeviceSpeed ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBDeviceStruct942.GetDeviceSpeed$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetDeviceSpeed$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetDeviceSpeed")); - public static VarHandle GetDeviceSpeed$VH() { - return IOUSBDeviceStruct942.GetDeviceSpeed$VH; - } - public static MemoryAddress GetDeviceSpeed$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceSpeed$VH.get(seg); - } - public static void GetDeviceSpeed$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceSpeed$VH.set(seg, x); - } - public static MemoryAddress GetDeviceSpeed$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceSpeed$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetDeviceSpeed$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceSpeed$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetDeviceSpeed GetDeviceSpeed (MemorySegment segment, MemorySession session) { - return GetDeviceSpeed.ofAddress(GetDeviceSpeed$get(segment), session); - } - static final FunctionDescriptor GetNumberOfConfigurations$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetNumberOfConfigurations$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.GetNumberOfConfigurations$FUNC - ); - public interface GetNumberOfConfigurations { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetNumberOfConfigurations fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetNumberOfConfigurations.class, fi, IOUSBDeviceStruct942.GetNumberOfConfigurations$FUNC, session); - } - static GetNumberOfConfigurations ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBDeviceStruct942.GetNumberOfConfigurations$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetNumberOfConfigurations$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetNumberOfConfigurations")); - public static VarHandle GetNumberOfConfigurations$VH() { - return IOUSBDeviceStruct942.GetNumberOfConfigurations$VH; - } - public static MemoryAddress GetNumberOfConfigurations$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetNumberOfConfigurations$VH.get(seg); - } - public static void GetNumberOfConfigurations$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.GetNumberOfConfigurations$VH.set(seg, x); - } - public static MemoryAddress GetNumberOfConfigurations$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetNumberOfConfigurations$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetNumberOfConfigurations$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.GetNumberOfConfigurations$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetNumberOfConfigurations GetNumberOfConfigurations (MemorySegment segment, MemorySession session) { - return GetNumberOfConfigurations.ofAddress(GetNumberOfConfigurations$get(segment), session); - } - static final FunctionDescriptor GetLocationID$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetLocationID$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.GetLocationID$FUNC - ); - public interface GetLocationID { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetLocationID fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetLocationID.class, fi, IOUSBDeviceStruct942.GetLocationID$FUNC, session); - } - static GetLocationID ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBDeviceStruct942.GetLocationID$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetLocationID$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetLocationID")); - public static VarHandle GetLocationID$VH() { - return IOUSBDeviceStruct942.GetLocationID$VH; - } - public static MemoryAddress GetLocationID$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetLocationID$VH.get(seg); - } - public static void GetLocationID$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.GetLocationID$VH.set(seg, x); - } - public static MemoryAddress GetLocationID$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetLocationID$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetLocationID$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.GetLocationID$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetLocationID GetLocationID (MemorySegment segment, MemorySession session) { - return GetLocationID.ofAddress(GetLocationID$get(segment), session); - } - static final FunctionDescriptor GetConfigurationDescriptorPtr$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetConfigurationDescriptorPtr$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.GetConfigurationDescriptorPtr$FUNC - ); - public interface GetConfigurationDescriptorPtr { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, java.lang.foreign.MemoryAddress _x2); - static MemorySegment allocate(GetConfigurationDescriptorPtr fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetConfigurationDescriptorPtr.class, fi, IOUSBDeviceStruct942.GetConfigurationDescriptorPtr$FUNC, session); - } - static GetConfigurationDescriptorPtr ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, java.lang.foreign.MemoryAddress __x2) -> { - try { - return (int)IOUSBDeviceStruct942.GetConfigurationDescriptorPtr$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetConfigurationDescriptorPtr$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetConfigurationDescriptorPtr")); - public static VarHandle GetConfigurationDescriptorPtr$VH() { - return IOUSBDeviceStruct942.GetConfigurationDescriptorPtr$VH; - } - public static MemoryAddress GetConfigurationDescriptorPtr$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetConfigurationDescriptorPtr$VH.get(seg); - } - public static void GetConfigurationDescriptorPtr$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.GetConfigurationDescriptorPtr$VH.set(seg, x); - } - public static MemoryAddress GetConfigurationDescriptorPtr$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetConfigurationDescriptorPtr$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetConfigurationDescriptorPtr$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.GetConfigurationDescriptorPtr$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetConfigurationDescriptorPtr GetConfigurationDescriptorPtr (MemorySegment segment, MemorySession session) { - return GetConfigurationDescriptorPtr.ofAddress(GetConfigurationDescriptorPtr$get(segment), session); - } - static final FunctionDescriptor GetConfiguration$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetConfiguration$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.GetConfiguration$FUNC - ); - public interface GetConfiguration { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetConfiguration fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetConfiguration.class, fi, IOUSBDeviceStruct942.GetConfiguration$FUNC, session); - } - static GetConfiguration ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBDeviceStruct942.GetConfiguration$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetConfiguration$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetConfiguration")); - public static VarHandle GetConfiguration$VH() { - return IOUSBDeviceStruct942.GetConfiguration$VH; - } - public static MemoryAddress GetConfiguration$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetConfiguration$VH.get(seg); - } - public static void GetConfiguration$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.GetConfiguration$VH.set(seg, x); - } - public static MemoryAddress GetConfiguration$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetConfiguration$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetConfiguration$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.GetConfiguration$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetConfiguration GetConfiguration (MemorySegment segment, MemorySession session) { - return GetConfiguration.ofAddress(GetConfiguration$get(segment), session); - } - static final FunctionDescriptor SetConfiguration$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT - ); - static final MethodHandle SetConfiguration$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.SetConfiguration$FUNC - ); - public interface SetConfiguration { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1); - static MemorySegment allocate(SetConfiguration fi, MemorySession session) { - return RuntimeHelper.upcallStub(SetConfiguration.class, fi, IOUSBDeviceStruct942.SetConfiguration$FUNC, session); - } - static SetConfiguration ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1) -> { - try { - return (int)IOUSBDeviceStruct942.SetConfiguration$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle SetConfiguration$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("SetConfiguration")); - public static VarHandle SetConfiguration$VH() { - return IOUSBDeviceStruct942.SetConfiguration$VH; - } - public static MemoryAddress SetConfiguration$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.SetConfiguration$VH.get(seg); - } - public static void SetConfiguration$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.SetConfiguration$VH.set(seg, x); - } - public static MemoryAddress SetConfiguration$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.SetConfiguration$VH.get(seg.asSlice(index*sizeof())); - } - public static void SetConfiguration$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.SetConfiguration$VH.set(seg.asSlice(index*sizeof()), x); - } - public static SetConfiguration SetConfiguration (MemorySegment segment, MemorySession session) { - return SetConfiguration.ofAddress(SetConfiguration$get(segment), session); - } - static final FunctionDescriptor GetBusFrameNumber$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetBusFrameNumber$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.GetBusFrameNumber$FUNC - ); - public interface GetBusFrameNumber { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1, java.lang.foreign.MemoryAddress _x2); - static MemorySegment allocate(GetBusFrameNumber fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetBusFrameNumber.class, fi, IOUSBDeviceStruct942.GetBusFrameNumber$FUNC, session); - } - static GetBusFrameNumber ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1, java.lang.foreign.MemoryAddress __x2) -> { - try { - return (int)IOUSBDeviceStruct942.GetBusFrameNumber$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1, (java.lang.foreign.Addressable)__x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetBusFrameNumber$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetBusFrameNumber")); - public static VarHandle GetBusFrameNumber$VH() { - return IOUSBDeviceStruct942.GetBusFrameNumber$VH; - } - public static MemoryAddress GetBusFrameNumber$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetBusFrameNumber$VH.get(seg); - } - public static void GetBusFrameNumber$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.GetBusFrameNumber$VH.set(seg, x); - } - public static MemoryAddress GetBusFrameNumber$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetBusFrameNumber$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetBusFrameNumber$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.GetBusFrameNumber$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetBusFrameNumber GetBusFrameNumber (MemorySegment segment, MemorySession session) { - return GetBusFrameNumber.ofAddress(GetBusFrameNumber$get(segment), session); - } - static final FunctionDescriptor ResetDevice$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle ResetDevice$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.ResetDevice$FUNC - ); - public interface ResetDevice { - - int apply(java.lang.foreign.MemoryAddress _x0); - static MemorySegment allocate(ResetDevice fi, MemorySession session) { - return RuntimeHelper.upcallStub(ResetDevice.class, fi, IOUSBDeviceStruct942.ResetDevice$FUNC, session); - } - static ResetDevice ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0) -> { - try { - return (int)IOUSBDeviceStruct942.ResetDevice$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle ResetDevice$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("ResetDevice")); - public static VarHandle ResetDevice$VH() { - return IOUSBDeviceStruct942.ResetDevice$VH; - } - public static MemoryAddress ResetDevice$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.ResetDevice$VH.get(seg); - } - public static void ResetDevice$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.ResetDevice$VH.set(seg, x); - } - public static MemoryAddress ResetDevice$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.ResetDevice$VH.get(seg.asSlice(index*sizeof())); - } - public static void ResetDevice$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.ResetDevice$VH.set(seg.asSlice(index*sizeof()), x); - } - public static ResetDevice ResetDevice (MemorySegment segment, MemorySession session) { - return ResetDevice.ofAddress(ResetDevice$get(segment), session); - } - static final FunctionDescriptor DeviceRequest$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle DeviceRequest$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.DeviceRequest$FUNC - ); - public interface DeviceRequest { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(DeviceRequest fi, MemorySession session) { - return RuntimeHelper.upcallStub(DeviceRequest.class, fi, IOUSBDeviceStruct942.DeviceRequest$FUNC, session); - } - static DeviceRequest ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBDeviceStruct942.DeviceRequest$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle DeviceRequest$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("DeviceRequest")); - public static VarHandle DeviceRequest$VH() { - return IOUSBDeviceStruct942.DeviceRequest$VH; - } - public static MemoryAddress DeviceRequest$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.DeviceRequest$VH.get(seg); - } - public static void DeviceRequest$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.DeviceRequest$VH.set(seg, x); - } - public static MemoryAddress DeviceRequest$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.DeviceRequest$VH.get(seg.asSlice(index*sizeof())); - } - public static void DeviceRequest$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.DeviceRequest$VH.set(seg.asSlice(index*sizeof()), x); - } - public static DeviceRequest DeviceRequest (MemorySegment segment, MemorySession session) { - return DeviceRequest.ofAddress(DeviceRequest$get(segment), session); - } - static final FunctionDescriptor DeviceRequestAsync$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle DeviceRequestAsync$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.DeviceRequestAsync$FUNC - ); - public interface DeviceRequestAsync { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1, java.lang.foreign.MemoryAddress _x2, java.lang.foreign.MemoryAddress _x3); - static MemorySegment allocate(DeviceRequestAsync fi, MemorySession session) { - return RuntimeHelper.upcallStub(DeviceRequestAsync.class, fi, IOUSBDeviceStruct942.DeviceRequestAsync$FUNC, session); - } - static DeviceRequestAsync ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1, java.lang.foreign.MemoryAddress __x2, java.lang.foreign.MemoryAddress __x3) -> { - try { - return (int)IOUSBDeviceStruct942.DeviceRequestAsync$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1, (java.lang.foreign.Addressable)__x2, (java.lang.foreign.Addressable)__x3); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle DeviceRequestAsync$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("DeviceRequestAsync")); - public static VarHandle DeviceRequestAsync$VH() { - return IOUSBDeviceStruct942.DeviceRequestAsync$VH; - } - public static MemoryAddress DeviceRequestAsync$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.DeviceRequestAsync$VH.get(seg); - } - public static void DeviceRequestAsync$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.DeviceRequestAsync$VH.set(seg, x); - } - public static MemoryAddress DeviceRequestAsync$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.DeviceRequestAsync$VH.get(seg.asSlice(index*sizeof())); - } - public static void DeviceRequestAsync$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.DeviceRequestAsync$VH.set(seg.asSlice(index*sizeof()), x); - } - public static DeviceRequestAsync DeviceRequestAsync (MemorySegment segment, MemorySession session) { - return DeviceRequestAsync.ofAddress(DeviceRequestAsync$get(segment), session); - } - static final FunctionDescriptor CreateInterfaceIterator$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle CreateInterfaceIterator$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.CreateInterfaceIterator$FUNC - ); - public interface CreateInterfaceIterator { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1, java.lang.foreign.MemoryAddress _x2); - static MemorySegment allocate(CreateInterfaceIterator fi, MemorySession session) { - return RuntimeHelper.upcallStub(CreateInterfaceIterator.class, fi, IOUSBDeviceStruct942.CreateInterfaceIterator$FUNC, session); - } - static CreateInterfaceIterator ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1, java.lang.foreign.MemoryAddress __x2) -> { - try { - return (int)IOUSBDeviceStruct942.CreateInterfaceIterator$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1, (java.lang.foreign.Addressable)__x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle CreateInterfaceIterator$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("CreateInterfaceIterator")); - public static VarHandle CreateInterfaceIterator$VH() { - return IOUSBDeviceStruct942.CreateInterfaceIterator$VH; - } - public static MemoryAddress CreateInterfaceIterator$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.CreateInterfaceIterator$VH.get(seg); - } - public static void CreateInterfaceIterator$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.CreateInterfaceIterator$VH.set(seg, x); - } - public static MemoryAddress CreateInterfaceIterator$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.CreateInterfaceIterator$VH.get(seg.asSlice(index*sizeof())); - } - public static void CreateInterfaceIterator$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.CreateInterfaceIterator$VH.set(seg.asSlice(index*sizeof()), x); - } - public static CreateInterfaceIterator CreateInterfaceIterator (MemorySegment segment, MemorySession session) { - return CreateInterfaceIterator.ofAddress(CreateInterfaceIterator$get(segment), session); - } - static final FunctionDescriptor USBDeviceOpenSeize$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle USBDeviceOpenSeize$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.USBDeviceOpenSeize$FUNC - ); - public interface USBDeviceOpenSeize { - - int apply(java.lang.foreign.MemoryAddress _x0); - static MemorySegment allocate(USBDeviceOpenSeize fi, MemorySession session) { - return RuntimeHelper.upcallStub(USBDeviceOpenSeize.class, fi, IOUSBDeviceStruct942.USBDeviceOpenSeize$FUNC, session); - } - static USBDeviceOpenSeize ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0) -> { - try { - return (int)IOUSBDeviceStruct942.USBDeviceOpenSeize$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle USBDeviceOpenSeize$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("USBDeviceOpenSeize")); - public static VarHandle USBDeviceOpenSeize$VH() { - return IOUSBDeviceStruct942.USBDeviceOpenSeize$VH; - } - public static MemoryAddress USBDeviceOpenSeize$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.USBDeviceOpenSeize$VH.get(seg); - } - public static void USBDeviceOpenSeize$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.USBDeviceOpenSeize$VH.set(seg, x); - } - public static MemoryAddress USBDeviceOpenSeize$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.USBDeviceOpenSeize$VH.get(seg.asSlice(index*sizeof())); - } - public static void USBDeviceOpenSeize$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.USBDeviceOpenSeize$VH.set(seg.asSlice(index*sizeof()), x); - } - public static USBDeviceOpenSeize USBDeviceOpenSeize (MemorySegment segment, MemorySession session) { - return USBDeviceOpenSeize.ofAddress(USBDeviceOpenSeize$get(segment), session); - } - static final FunctionDescriptor DeviceRequestTO$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle DeviceRequestTO$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.DeviceRequestTO$FUNC - ); - public interface DeviceRequestTO { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(DeviceRequestTO fi, MemorySession session) { - return RuntimeHelper.upcallStub(DeviceRequestTO.class, fi, IOUSBDeviceStruct942.DeviceRequestTO$FUNC, session); - } - static DeviceRequestTO ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBDeviceStruct942.DeviceRequestTO$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle DeviceRequestTO$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("DeviceRequestTO")); - public static VarHandle DeviceRequestTO$VH() { - return IOUSBDeviceStruct942.DeviceRequestTO$VH; - } - public static MemoryAddress DeviceRequestTO$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.DeviceRequestTO$VH.get(seg); - } - public static void DeviceRequestTO$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.DeviceRequestTO$VH.set(seg, x); - } - public static MemoryAddress DeviceRequestTO$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.DeviceRequestTO$VH.get(seg.asSlice(index*sizeof())); - } - public static void DeviceRequestTO$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.DeviceRequestTO$VH.set(seg.asSlice(index*sizeof()), x); - } - public static DeviceRequestTO DeviceRequestTO (MemorySegment segment, MemorySession session) { - return DeviceRequestTO.ofAddress(DeviceRequestTO$get(segment), session); - } - static final FunctionDescriptor DeviceRequestAsyncTO$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle DeviceRequestAsyncTO$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.DeviceRequestAsyncTO$FUNC - ); - public interface DeviceRequestAsyncTO { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1, java.lang.foreign.MemoryAddress _x2, java.lang.foreign.MemoryAddress _x3); - static MemorySegment allocate(DeviceRequestAsyncTO fi, MemorySession session) { - return RuntimeHelper.upcallStub(DeviceRequestAsyncTO.class, fi, IOUSBDeviceStruct942.DeviceRequestAsyncTO$FUNC, session); - } - static DeviceRequestAsyncTO ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1, java.lang.foreign.MemoryAddress __x2, java.lang.foreign.MemoryAddress __x3) -> { - try { - return (int)IOUSBDeviceStruct942.DeviceRequestAsyncTO$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1, (java.lang.foreign.Addressable)__x2, (java.lang.foreign.Addressable)__x3); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle DeviceRequestAsyncTO$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("DeviceRequestAsyncTO")); - public static VarHandle DeviceRequestAsyncTO$VH() { - return IOUSBDeviceStruct942.DeviceRequestAsyncTO$VH; - } - public static MemoryAddress DeviceRequestAsyncTO$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.DeviceRequestAsyncTO$VH.get(seg); - } - public static void DeviceRequestAsyncTO$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.DeviceRequestAsyncTO$VH.set(seg, x); - } - public static MemoryAddress DeviceRequestAsyncTO$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.DeviceRequestAsyncTO$VH.get(seg.asSlice(index*sizeof())); - } - public static void DeviceRequestAsyncTO$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.DeviceRequestAsyncTO$VH.set(seg.asSlice(index*sizeof()), x); - } - public static DeviceRequestAsyncTO DeviceRequestAsyncTO (MemorySegment segment, MemorySession session) { - return DeviceRequestAsyncTO.ofAddress(DeviceRequestAsyncTO$get(segment), session); - } - static final FunctionDescriptor USBDeviceSuspend$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT - ); - static final MethodHandle USBDeviceSuspend$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.USBDeviceSuspend$FUNC - ); - public interface USBDeviceSuspend { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1); - static MemorySegment allocate(USBDeviceSuspend fi, MemorySession session) { - return RuntimeHelper.upcallStub(USBDeviceSuspend.class, fi, IOUSBDeviceStruct942.USBDeviceSuspend$FUNC, session); - } - static USBDeviceSuspend ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1) -> { - try { - return (int)IOUSBDeviceStruct942.USBDeviceSuspend$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle USBDeviceSuspend$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("USBDeviceSuspend")); - public static VarHandle USBDeviceSuspend$VH() { - return IOUSBDeviceStruct942.USBDeviceSuspend$VH; - } - public static MemoryAddress USBDeviceSuspend$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.USBDeviceSuspend$VH.get(seg); - } - public static void USBDeviceSuspend$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.USBDeviceSuspend$VH.set(seg, x); - } - public static MemoryAddress USBDeviceSuspend$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.USBDeviceSuspend$VH.get(seg.asSlice(index*sizeof())); - } - public static void USBDeviceSuspend$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.USBDeviceSuspend$VH.set(seg.asSlice(index*sizeof()), x); - } - public static USBDeviceSuspend USBDeviceSuspend (MemorySegment segment, MemorySession session) { - return USBDeviceSuspend.ofAddress(USBDeviceSuspend$get(segment), session); - } - static final FunctionDescriptor USBDeviceAbortPipeZero$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle USBDeviceAbortPipeZero$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.USBDeviceAbortPipeZero$FUNC - ); - public interface USBDeviceAbortPipeZero { - - int apply(java.lang.foreign.MemoryAddress _x0); - static MemorySegment allocate(USBDeviceAbortPipeZero fi, MemorySession session) { - return RuntimeHelper.upcallStub(USBDeviceAbortPipeZero.class, fi, IOUSBDeviceStruct942.USBDeviceAbortPipeZero$FUNC, session); - } - static USBDeviceAbortPipeZero ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0) -> { - try { - return (int)IOUSBDeviceStruct942.USBDeviceAbortPipeZero$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle USBDeviceAbortPipeZero$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("USBDeviceAbortPipeZero")); - public static VarHandle USBDeviceAbortPipeZero$VH() { - return IOUSBDeviceStruct942.USBDeviceAbortPipeZero$VH; - } - public static MemoryAddress USBDeviceAbortPipeZero$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.USBDeviceAbortPipeZero$VH.get(seg); - } - public static void USBDeviceAbortPipeZero$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.USBDeviceAbortPipeZero$VH.set(seg, x); - } - public static MemoryAddress USBDeviceAbortPipeZero$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.USBDeviceAbortPipeZero$VH.get(seg.asSlice(index*sizeof())); - } - public static void USBDeviceAbortPipeZero$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.USBDeviceAbortPipeZero$VH.set(seg.asSlice(index*sizeof()), x); - } - public static USBDeviceAbortPipeZero USBDeviceAbortPipeZero (MemorySegment segment, MemorySession session) { - return USBDeviceAbortPipeZero.ofAddress(USBDeviceAbortPipeZero$get(segment), session); - } - static final FunctionDescriptor USBGetManufacturerStringIndex$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle USBGetManufacturerStringIndex$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.USBGetManufacturerStringIndex$FUNC - ); - public interface USBGetManufacturerStringIndex { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(USBGetManufacturerStringIndex fi, MemorySession session) { - return RuntimeHelper.upcallStub(USBGetManufacturerStringIndex.class, fi, IOUSBDeviceStruct942.USBGetManufacturerStringIndex$FUNC, session); - } - static USBGetManufacturerStringIndex ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBDeviceStruct942.USBGetManufacturerStringIndex$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle USBGetManufacturerStringIndex$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("USBGetManufacturerStringIndex")); - public static VarHandle USBGetManufacturerStringIndex$VH() { - return IOUSBDeviceStruct942.USBGetManufacturerStringIndex$VH; - } - public static MemoryAddress USBGetManufacturerStringIndex$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.USBGetManufacturerStringIndex$VH.get(seg); - } - public static void USBGetManufacturerStringIndex$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.USBGetManufacturerStringIndex$VH.set(seg, x); - } - public static MemoryAddress USBGetManufacturerStringIndex$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.USBGetManufacturerStringIndex$VH.get(seg.asSlice(index*sizeof())); - } - public static void USBGetManufacturerStringIndex$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.USBGetManufacturerStringIndex$VH.set(seg.asSlice(index*sizeof()), x); - } - public static USBGetManufacturerStringIndex USBGetManufacturerStringIndex (MemorySegment segment, MemorySession session) { - return USBGetManufacturerStringIndex.ofAddress(USBGetManufacturerStringIndex$get(segment), session); - } - static final FunctionDescriptor USBGetProductStringIndex$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle USBGetProductStringIndex$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.USBGetProductStringIndex$FUNC - ); - public interface USBGetProductStringIndex { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(USBGetProductStringIndex fi, MemorySession session) { - return RuntimeHelper.upcallStub(USBGetProductStringIndex.class, fi, IOUSBDeviceStruct942.USBGetProductStringIndex$FUNC, session); - } - static USBGetProductStringIndex ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBDeviceStruct942.USBGetProductStringIndex$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle USBGetProductStringIndex$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("USBGetProductStringIndex")); - public static VarHandle USBGetProductStringIndex$VH() { - return IOUSBDeviceStruct942.USBGetProductStringIndex$VH; - } - public static MemoryAddress USBGetProductStringIndex$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.USBGetProductStringIndex$VH.get(seg); - } - public static void USBGetProductStringIndex$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.USBGetProductStringIndex$VH.set(seg, x); - } - public static MemoryAddress USBGetProductStringIndex$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.USBGetProductStringIndex$VH.get(seg.asSlice(index*sizeof())); - } - public static void USBGetProductStringIndex$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.USBGetProductStringIndex$VH.set(seg.asSlice(index*sizeof()), x); - } - public static USBGetProductStringIndex USBGetProductStringIndex (MemorySegment segment, MemorySession session) { - return USBGetProductStringIndex.ofAddress(USBGetProductStringIndex$get(segment), session); - } - static final FunctionDescriptor USBGetSerialNumberStringIndex$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle USBGetSerialNumberStringIndex$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.USBGetSerialNumberStringIndex$FUNC - ); - public interface USBGetSerialNumberStringIndex { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(USBGetSerialNumberStringIndex fi, MemorySession session) { - return RuntimeHelper.upcallStub(USBGetSerialNumberStringIndex.class, fi, IOUSBDeviceStruct942.USBGetSerialNumberStringIndex$FUNC, session); - } - static USBGetSerialNumberStringIndex ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBDeviceStruct942.USBGetSerialNumberStringIndex$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle USBGetSerialNumberStringIndex$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("USBGetSerialNumberStringIndex")); - public static VarHandle USBGetSerialNumberStringIndex$VH() { - return IOUSBDeviceStruct942.USBGetSerialNumberStringIndex$VH; - } - public static MemoryAddress USBGetSerialNumberStringIndex$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.USBGetSerialNumberStringIndex$VH.get(seg); - } - public static void USBGetSerialNumberStringIndex$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.USBGetSerialNumberStringIndex$VH.set(seg, x); - } - public static MemoryAddress USBGetSerialNumberStringIndex$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.USBGetSerialNumberStringIndex$VH.get(seg.asSlice(index*sizeof())); - } - public static void USBGetSerialNumberStringIndex$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.USBGetSerialNumberStringIndex$VH.set(seg.asSlice(index*sizeof()), x); - } - public static USBGetSerialNumberStringIndex USBGetSerialNumberStringIndex (MemorySegment segment, MemorySession session) { - return USBGetSerialNumberStringIndex.ofAddress(USBGetSerialNumberStringIndex$get(segment), session); - } - static final FunctionDescriptor USBDeviceReEnumerate$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT - ); - static final MethodHandle USBDeviceReEnumerate$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.USBDeviceReEnumerate$FUNC - ); - public interface USBDeviceReEnumerate { - - int apply(java.lang.foreign.MemoryAddress _x0, int _x1); - static MemorySegment allocate(USBDeviceReEnumerate fi, MemorySession session) { - return RuntimeHelper.upcallStub(USBDeviceReEnumerate.class, fi, IOUSBDeviceStruct942.USBDeviceReEnumerate$FUNC, session); - } - static USBDeviceReEnumerate ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, int __x1) -> { - try { - return (int)IOUSBDeviceStruct942.USBDeviceReEnumerate$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle USBDeviceReEnumerate$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("USBDeviceReEnumerate")); - public static VarHandle USBDeviceReEnumerate$VH() { - return IOUSBDeviceStruct942.USBDeviceReEnumerate$VH; - } - public static MemoryAddress USBDeviceReEnumerate$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.USBDeviceReEnumerate$VH.get(seg); - } - public static void USBDeviceReEnumerate$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.USBDeviceReEnumerate$VH.set(seg, x); - } - public static MemoryAddress USBDeviceReEnumerate$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.USBDeviceReEnumerate$VH.get(seg.asSlice(index*sizeof())); - } - public static void USBDeviceReEnumerate$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.USBDeviceReEnumerate$VH.set(seg.asSlice(index*sizeof()), x); - } - public static USBDeviceReEnumerate USBDeviceReEnumerate (MemorySegment segment, MemorySession session) { - return USBDeviceReEnumerate.ofAddress(USBDeviceReEnumerate$get(segment), session); - } - static final FunctionDescriptor GetBusMicroFrameNumber$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetBusMicroFrameNumber$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.GetBusMicroFrameNumber$FUNC - ); - public interface GetBusMicroFrameNumber { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1, java.lang.foreign.MemoryAddress _x2); - static MemorySegment allocate(GetBusMicroFrameNumber fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetBusMicroFrameNumber.class, fi, IOUSBDeviceStruct942.GetBusMicroFrameNumber$FUNC, session); - } - static GetBusMicroFrameNumber ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1, java.lang.foreign.MemoryAddress __x2) -> { - try { - return (int)IOUSBDeviceStruct942.GetBusMicroFrameNumber$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1, (java.lang.foreign.Addressable)__x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetBusMicroFrameNumber$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetBusMicroFrameNumber")); - public static VarHandle GetBusMicroFrameNumber$VH() { - return IOUSBDeviceStruct942.GetBusMicroFrameNumber$VH; - } - public static MemoryAddress GetBusMicroFrameNumber$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetBusMicroFrameNumber$VH.get(seg); - } - public static void GetBusMicroFrameNumber$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.GetBusMicroFrameNumber$VH.set(seg, x); - } - public static MemoryAddress GetBusMicroFrameNumber$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetBusMicroFrameNumber$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetBusMicroFrameNumber$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.GetBusMicroFrameNumber$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetBusMicroFrameNumber GetBusMicroFrameNumber (MemorySegment segment, MemorySession session) { - return GetBusMicroFrameNumber.ofAddress(GetBusMicroFrameNumber$get(segment), session); - } - static final FunctionDescriptor GetIOUSBLibVersion$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetIOUSBLibVersion$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.GetIOUSBLibVersion$FUNC - ); - public interface GetIOUSBLibVersion { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1, java.lang.foreign.MemoryAddress _x2); - static MemorySegment allocate(GetIOUSBLibVersion fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetIOUSBLibVersion.class, fi, IOUSBDeviceStruct942.GetIOUSBLibVersion$FUNC, session); - } - static GetIOUSBLibVersion ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1, java.lang.foreign.MemoryAddress __x2) -> { - try { - return (int)IOUSBDeviceStruct942.GetIOUSBLibVersion$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1, (java.lang.foreign.Addressable)__x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetIOUSBLibVersion$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetIOUSBLibVersion")); - public static VarHandle GetIOUSBLibVersion$VH() { - return IOUSBDeviceStruct942.GetIOUSBLibVersion$VH; - } - public static MemoryAddress GetIOUSBLibVersion$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetIOUSBLibVersion$VH.get(seg); - } - public static void GetIOUSBLibVersion$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.GetIOUSBLibVersion$VH.set(seg, x); - } - public static MemoryAddress GetIOUSBLibVersion$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetIOUSBLibVersion$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetIOUSBLibVersion$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.GetIOUSBLibVersion$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetIOUSBLibVersion GetIOUSBLibVersion (MemorySegment segment, MemorySession session) { - return GetIOUSBLibVersion.ofAddress(GetIOUSBLibVersion$get(segment), session); - } - static final FunctionDescriptor GetBusFrameNumberWithTime$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetBusFrameNumberWithTime$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.GetBusFrameNumberWithTime$FUNC - ); - public interface GetBusFrameNumberWithTime { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1, java.lang.foreign.MemoryAddress _x2); - static MemorySegment allocate(GetBusFrameNumberWithTime fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetBusFrameNumberWithTime.class, fi, IOUSBDeviceStruct942.GetBusFrameNumberWithTime$FUNC, session); - } - static GetBusFrameNumberWithTime ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1, java.lang.foreign.MemoryAddress __x2) -> { - try { - return (int)IOUSBDeviceStruct942.GetBusFrameNumberWithTime$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1, (java.lang.foreign.Addressable)__x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetBusFrameNumberWithTime$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetBusFrameNumberWithTime")); - public static VarHandle GetBusFrameNumberWithTime$VH() { - return IOUSBDeviceStruct942.GetBusFrameNumberWithTime$VH; - } - public static MemoryAddress GetBusFrameNumberWithTime$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetBusFrameNumberWithTime$VH.get(seg); - } - public static void GetBusFrameNumberWithTime$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.GetBusFrameNumberWithTime$VH.set(seg, x); - } - public static MemoryAddress GetBusFrameNumberWithTime$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetBusFrameNumberWithTime$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetBusFrameNumberWithTime$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.GetBusFrameNumberWithTime$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetBusFrameNumberWithTime GetBusFrameNumberWithTime (MemorySegment segment, MemorySession session) { - return GetBusFrameNumberWithTime.ofAddress(GetBusFrameNumberWithTime$get(segment), session); - } - static final FunctionDescriptor GetUSBDeviceInformation$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetUSBDeviceInformation$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.GetUSBDeviceInformation$FUNC - ); - public interface GetUSBDeviceInformation { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetUSBDeviceInformation fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetUSBDeviceInformation.class, fi, IOUSBDeviceStruct942.GetUSBDeviceInformation$FUNC, session); - } - static GetUSBDeviceInformation ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBDeviceStruct942.GetUSBDeviceInformation$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetUSBDeviceInformation$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetUSBDeviceInformation")); - public static VarHandle GetUSBDeviceInformation$VH() { - return IOUSBDeviceStruct942.GetUSBDeviceInformation$VH; - } - public static MemoryAddress GetUSBDeviceInformation$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetUSBDeviceInformation$VH.get(seg); - } - public static void GetUSBDeviceInformation$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.GetUSBDeviceInformation$VH.set(seg, x); - } - public static MemoryAddress GetUSBDeviceInformation$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetUSBDeviceInformation$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetUSBDeviceInformation$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.GetUSBDeviceInformation$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetUSBDeviceInformation GetUSBDeviceInformation (MemorySegment segment, MemorySession session) { - return GetUSBDeviceInformation.ofAddress(GetUSBDeviceInformation$get(segment), session); - } - static final FunctionDescriptor RequestExtraPower$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle RequestExtraPower$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.RequestExtraPower$FUNC - ); - public interface RequestExtraPower { - - int apply(java.lang.foreign.MemoryAddress _x0, int _x1, int _x2, java.lang.foreign.MemoryAddress _x3); - static MemorySegment allocate(RequestExtraPower fi, MemorySession session) { - return RuntimeHelper.upcallStub(RequestExtraPower.class, fi, IOUSBDeviceStruct942.RequestExtraPower$FUNC, session); - } - static RequestExtraPower ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, int __x1, int __x2, java.lang.foreign.MemoryAddress __x3) -> { - try { - return (int)IOUSBDeviceStruct942.RequestExtraPower$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, __x2, (java.lang.foreign.Addressable)__x3); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle RequestExtraPower$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("RequestExtraPower")); - public static VarHandle RequestExtraPower$VH() { - return IOUSBDeviceStruct942.RequestExtraPower$VH; - } - public static MemoryAddress RequestExtraPower$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.RequestExtraPower$VH.get(seg); - } - public static void RequestExtraPower$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.RequestExtraPower$VH.set(seg, x); - } - public static MemoryAddress RequestExtraPower$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.RequestExtraPower$VH.get(seg.asSlice(index*sizeof())); - } - public static void RequestExtraPower$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.RequestExtraPower$VH.set(seg.asSlice(index*sizeof()), x); - } - public static RequestExtraPower RequestExtraPower (MemorySegment segment, MemorySession session) { - return RequestExtraPower.ofAddress(RequestExtraPower$get(segment), session); - } - static final FunctionDescriptor ReturnExtraPower$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT - ); - static final MethodHandle ReturnExtraPower$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.ReturnExtraPower$FUNC - ); - public interface ReturnExtraPower { - - int apply(java.lang.foreign.MemoryAddress _x0, int _x1, int _x2); - static MemorySegment allocate(ReturnExtraPower fi, MemorySession session) { - return RuntimeHelper.upcallStub(ReturnExtraPower.class, fi, IOUSBDeviceStruct942.ReturnExtraPower$FUNC, session); - } - static ReturnExtraPower ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, int __x1, int __x2) -> { - try { - return (int)IOUSBDeviceStruct942.ReturnExtraPower$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, __x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle ReturnExtraPower$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("ReturnExtraPower")); - public static VarHandle ReturnExtraPower$VH() { - return IOUSBDeviceStruct942.ReturnExtraPower$VH; - } - public static MemoryAddress ReturnExtraPower$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.ReturnExtraPower$VH.get(seg); - } - public static void ReturnExtraPower$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.ReturnExtraPower$VH.set(seg, x); - } - public static MemoryAddress ReturnExtraPower$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.ReturnExtraPower$VH.get(seg.asSlice(index*sizeof())); - } - public static void ReturnExtraPower$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.ReturnExtraPower$VH.set(seg.asSlice(index*sizeof()), x); - } - public static ReturnExtraPower ReturnExtraPower (MemorySegment segment, MemorySession session) { - return ReturnExtraPower.ofAddress(ReturnExtraPower$get(segment), session); - } - static final FunctionDescriptor GetExtraPowerAllocated$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetExtraPowerAllocated$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.GetExtraPowerAllocated$FUNC - ); - public interface GetExtraPowerAllocated { - - int apply(java.lang.foreign.MemoryAddress _x0, int _x1, java.lang.foreign.MemoryAddress _x2); - static MemorySegment allocate(GetExtraPowerAllocated fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetExtraPowerAllocated.class, fi, IOUSBDeviceStruct942.GetExtraPowerAllocated$FUNC, session); - } - static GetExtraPowerAllocated ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, int __x1, java.lang.foreign.MemoryAddress __x2) -> { - try { - return (int)IOUSBDeviceStruct942.GetExtraPowerAllocated$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetExtraPowerAllocated$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetExtraPowerAllocated")); - public static VarHandle GetExtraPowerAllocated$VH() { - return IOUSBDeviceStruct942.GetExtraPowerAllocated$VH; - } - public static MemoryAddress GetExtraPowerAllocated$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetExtraPowerAllocated$VH.get(seg); - } - public static void GetExtraPowerAllocated$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.GetExtraPowerAllocated$VH.set(seg, x); - } - public static MemoryAddress GetExtraPowerAllocated$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetExtraPowerAllocated$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetExtraPowerAllocated$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.GetExtraPowerAllocated$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetExtraPowerAllocated GetExtraPowerAllocated (MemorySegment segment, MemorySession session) { - return GetExtraPowerAllocated.ofAddress(GetExtraPowerAllocated$get(segment), session); - } - static final FunctionDescriptor GetBandwidthAvailableForDevice$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetBandwidthAvailableForDevice$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.GetBandwidthAvailableForDevice$FUNC - ); - public interface GetBandwidthAvailableForDevice { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetBandwidthAvailableForDevice fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetBandwidthAvailableForDevice.class, fi, IOUSBDeviceStruct942.GetBandwidthAvailableForDevice$FUNC, session); - } - static GetBandwidthAvailableForDevice ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBDeviceStruct942.GetBandwidthAvailableForDevice$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetBandwidthAvailableForDevice$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetBandwidthAvailableForDevice")); - public static VarHandle GetBandwidthAvailableForDevice$VH() { - return IOUSBDeviceStruct942.GetBandwidthAvailableForDevice$VH; - } - public static MemoryAddress GetBandwidthAvailableForDevice$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetBandwidthAvailableForDevice$VH.get(seg); - } - public static void GetBandwidthAvailableForDevice$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.GetBandwidthAvailableForDevice$VH.set(seg, x); - } - public static MemoryAddress GetBandwidthAvailableForDevice$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetBandwidthAvailableForDevice$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetBandwidthAvailableForDevice$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.GetBandwidthAvailableForDevice$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetBandwidthAvailableForDevice GetBandwidthAvailableForDevice (MemorySegment segment, MemorySession session) { - return GetBandwidthAvailableForDevice.ofAddress(GetBandwidthAvailableForDevice$get(segment), session); - } - static final FunctionDescriptor SetConfigurationV2$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_BOOL$LAYOUT, - Constants$root.C_BOOL$LAYOUT - ); - static final MethodHandle SetConfigurationV2$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.SetConfigurationV2$FUNC - ); - public interface SetConfigurationV2 { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, boolean _x2, boolean _x3); - static MemorySegment allocate(SetConfigurationV2 fi, MemorySession session) { - return RuntimeHelper.upcallStub(SetConfigurationV2.class, fi, IOUSBDeviceStruct942.SetConfigurationV2$FUNC, session); - } - static SetConfigurationV2 ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, boolean __x2, boolean __x3) -> { - try { - return (int)IOUSBDeviceStruct942.SetConfigurationV2$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, __x2, __x3); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle SetConfigurationV2$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("SetConfigurationV2")); - public static VarHandle SetConfigurationV2$VH() { - return IOUSBDeviceStruct942.SetConfigurationV2$VH; - } - public static MemoryAddress SetConfigurationV2$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.SetConfigurationV2$VH.get(seg); - } - public static void SetConfigurationV2$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.SetConfigurationV2$VH.set(seg, x); - } - public static MemoryAddress SetConfigurationV2$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.SetConfigurationV2$VH.get(seg.asSlice(index*sizeof())); - } - public static void SetConfigurationV2$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.SetConfigurationV2$VH.set(seg.asSlice(index*sizeof()), x); - } - public static SetConfigurationV2 SetConfigurationV2 (MemorySegment segment, MemorySession session) { - return SetConfigurationV2.ofAddress(SetConfigurationV2$get(segment), session); - } - static final FunctionDescriptor RegisterForNotification$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle RegisterForNotification$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.RegisterForNotification$FUNC - ); - public interface RegisterForNotification { - - int apply(java.lang.foreign.MemoryAddress _x0, long _x1, java.lang.foreign.MemoryAddress _x2, java.lang.foreign.MemoryAddress _x3, java.lang.foreign.MemoryAddress _x4); - static MemorySegment allocate(RegisterForNotification fi, MemorySession session) { - return RuntimeHelper.upcallStub(RegisterForNotification.class, fi, IOUSBDeviceStruct942.RegisterForNotification$FUNC, session); - } - static RegisterForNotification ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, long __x1, java.lang.foreign.MemoryAddress __x2, java.lang.foreign.MemoryAddress __x3, java.lang.foreign.MemoryAddress __x4) -> { - try { - return (int)IOUSBDeviceStruct942.RegisterForNotification$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2, (java.lang.foreign.Addressable)__x3, (java.lang.foreign.Addressable)__x4); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle RegisterForNotification$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("RegisterForNotification")); - public static VarHandle RegisterForNotification$VH() { - return IOUSBDeviceStruct942.RegisterForNotification$VH; - } - public static MemoryAddress RegisterForNotification$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.RegisterForNotification$VH.get(seg); - } - public static void RegisterForNotification$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.RegisterForNotification$VH.set(seg, x); - } - public static MemoryAddress RegisterForNotification$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.RegisterForNotification$VH.get(seg.asSlice(index*sizeof())); - } - public static void RegisterForNotification$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.RegisterForNotification$VH.set(seg.asSlice(index*sizeof()), x); - } - public static RegisterForNotification RegisterForNotification (MemorySegment segment, MemorySession session) { - return RegisterForNotification.ofAddress(RegisterForNotification$get(segment), session); - } - static final FunctionDescriptor UnregisterNotification$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG_LONG$LAYOUT - ); - static final MethodHandle UnregisterNotification$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.UnregisterNotification$FUNC - ); - public interface UnregisterNotification { - - int apply(java.lang.foreign.MemoryAddress _x0, long _x1); - static MemorySegment allocate(UnregisterNotification fi, MemorySession session) { - return RuntimeHelper.upcallStub(UnregisterNotification.class, fi, IOUSBDeviceStruct942.UnregisterNotification$FUNC, session); - } - static UnregisterNotification ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, long __x1) -> { - try { - return (int)IOUSBDeviceStruct942.UnregisterNotification$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle UnregisterNotification$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("UnregisterNotification")); - public static VarHandle UnregisterNotification$VH() { - return IOUSBDeviceStruct942.UnregisterNotification$VH; - } - public static MemoryAddress UnregisterNotification$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.UnregisterNotification$VH.get(seg); - } - public static void UnregisterNotification$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.UnregisterNotification$VH.set(seg, x); - } - public static MemoryAddress UnregisterNotification$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.UnregisterNotification$VH.get(seg.asSlice(index*sizeof())); - } - public static void UnregisterNotification$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.UnregisterNotification$VH.set(seg.asSlice(index*sizeof()), x); - } - public static UnregisterNotification UnregisterNotification (MemorySegment segment, MemorySession session) { - return UnregisterNotification.ofAddress(UnregisterNotification$get(segment), session); - } - static final FunctionDescriptor AcknowledgeNotification$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG_LONG$LAYOUT - ); - static final MethodHandle AcknowledgeNotification$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.AcknowledgeNotification$FUNC - ); - public interface AcknowledgeNotification { - - int apply(java.lang.foreign.MemoryAddress _x0, long _x1); - static MemorySegment allocate(AcknowledgeNotification fi, MemorySession session) { - return RuntimeHelper.upcallStub(AcknowledgeNotification.class, fi, IOUSBDeviceStruct942.AcknowledgeNotification$FUNC, session); - } - static AcknowledgeNotification ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, long __x1) -> { - try { - return (int)IOUSBDeviceStruct942.AcknowledgeNotification$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle AcknowledgeNotification$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("AcknowledgeNotification")); - public static VarHandle AcknowledgeNotification$VH() { - return IOUSBDeviceStruct942.AcknowledgeNotification$VH; - } - public static MemoryAddress AcknowledgeNotification$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.AcknowledgeNotification$VH.get(seg); - } - public static void AcknowledgeNotification$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.AcknowledgeNotification$VH.set(seg, x); - } - public static MemoryAddress AcknowledgeNotification$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.AcknowledgeNotification$VH.get(seg.asSlice(index*sizeof())); - } - public static void AcknowledgeNotification$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.AcknowledgeNotification$VH.set(seg.asSlice(index*sizeof()), x); - } - public static AcknowledgeNotification AcknowledgeNotification (MemorySegment segment, MemorySession session) { - return AcknowledgeNotification.ofAddress(AcknowledgeNotification$get(segment), session); - } - static final FunctionDescriptor GetDeviceAsyncNotificationPort$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetDeviceAsyncNotificationPort$MH = RuntimeHelper.downcallHandle( - IOUSBDeviceStruct942.GetDeviceAsyncNotificationPort$FUNC - ); - public interface GetDeviceAsyncNotificationPort { - - java.lang.foreign.Addressable apply(java.lang.foreign.MemoryAddress _x0); - static MemorySegment allocate(GetDeviceAsyncNotificationPort fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetDeviceAsyncNotificationPort.class, fi, IOUSBDeviceStruct942.GetDeviceAsyncNotificationPort$FUNC, session); - } - static GetDeviceAsyncNotificationPort ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0) -> { - try { - return (java.lang.foreign.Addressable)(java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceAsyncNotificationPort$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetDeviceAsyncNotificationPort$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetDeviceAsyncNotificationPort")); - public static VarHandle GetDeviceAsyncNotificationPort$VH() { - return IOUSBDeviceStruct942.GetDeviceAsyncNotificationPort$VH; - } - public static MemoryAddress GetDeviceAsyncNotificationPort$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceAsyncNotificationPort$VH.get(seg); - } - public static void GetDeviceAsyncNotificationPort$set( MemorySegment seg, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceAsyncNotificationPort$VH.set(seg, x); - } - public static MemoryAddress GetDeviceAsyncNotificationPort$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBDeviceStruct942.GetDeviceAsyncNotificationPort$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetDeviceAsyncNotificationPort$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBDeviceStruct942.GetDeviceAsyncNotificationPort$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetDeviceAsyncNotificationPort GetDeviceAsyncNotificationPort (MemorySegment segment, MemorySession session) { - return GetDeviceAsyncNotificationPort.ofAddress(GetDeviceAsyncNotificationPort$get(segment), session); - } - public static long sizeof() { return $LAYOUT().byteSize(); } - public static MemorySegment allocate(SegmentAllocator allocator) { return allocator.allocate($LAYOUT()); } - public static MemorySegment allocateArray(int len, SegmentAllocator allocator) { - return allocator.allocate(MemoryLayout.sequenceLayout(len, $LAYOUT())); - } - public static MemorySegment ofAddress(MemoryAddress addr, MemorySession session) { return RuntimeHelper.asArray(addr, $LAYOUT(), 1, session); } -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBFindInterfaceRequest.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBFindInterfaceRequest.java index e4e95f67..e0ded3eb 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBFindInterfaceRequest.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBFindInterfaceRequest.java @@ -2,89 +2,264 @@ package net.codecrete.usb.macos.gen.iokit; +import java.lang.invoke.*; import java.lang.foreign.*; -import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +/** + * {@snippet lang=c : + * struct { + * UInt16 bInterfaceClass; + * UInt16 bInterfaceSubClass; + * UInt16 bInterfaceProtocol; + * UInt16 bAlternateSetting; + * } + * } + */ public class IOUSBFindInterfaceRequest { - static final GroupLayout $struct$LAYOUT = MemoryLayout.structLayout( - Constants$root.C_SHORT$LAYOUT.withName("bInterfaceClass"), - Constants$root.C_SHORT$LAYOUT.withName("bInterfaceSubClass"), - Constants$root.C_SHORT$LAYOUT.withName("bInterfaceProtocol"), - Constants$root.C_SHORT$LAYOUT.withName("bAlternateSetting") - ); - public static MemoryLayout $LAYOUT() { - return IOUSBFindInterfaceRequest.$struct$LAYOUT; + IOUSBFindInterfaceRequest() { + // Should not be called directly } - static final VarHandle bInterfaceClass$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("bInterfaceClass")); - public static VarHandle bInterfaceClass$VH() { - return IOUSBFindInterfaceRequest.bInterfaceClass$VH; + + private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( + IOKit.C_SHORT.withName("bInterfaceClass"), + IOKit.C_SHORT.withName("bInterfaceSubClass"), + IOKit.C_SHORT.withName("bInterfaceProtocol"), + IOKit.C_SHORT.withName("bAlternateSetting") + ).withName("IOUSBFindInterfaceRequest"); + + /** + * The layout of this struct + */ + public static final GroupLayout layout() { + return $LAYOUT; } - public static short bInterfaceClass$get(MemorySegment seg) { - return (short)IOUSBFindInterfaceRequest.bInterfaceClass$VH.get(seg); + + private static final OfShort bInterfaceClass$LAYOUT = (OfShort)$LAYOUT.select(groupElement("bInterfaceClass")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt16 bInterfaceClass + * } + */ + public static final OfShort bInterfaceClass$layout() { + return bInterfaceClass$LAYOUT; + } + + private static final long bInterfaceClass$OFFSET = $LAYOUT.byteOffset(groupElement("bInterfaceClass")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt16 bInterfaceClass + * } + */ + public static final long bInterfaceClass$offset() { + return bInterfaceClass$OFFSET; } - public static void bInterfaceClass$set( MemorySegment seg, short x) { - IOUSBFindInterfaceRequest.bInterfaceClass$VH.set(seg, x); + + /** + * Getter for field: + * {@snippet lang=c : + * UInt16 bInterfaceClass + * } + */ + public static short bInterfaceClass(MemorySegment struct) { + return struct.get(bInterfaceClass$LAYOUT, bInterfaceClass$OFFSET); } - public static short bInterfaceClass$get(MemorySegment seg, long index) { - return (short)IOUSBFindInterfaceRequest.bInterfaceClass$VH.get(seg.asSlice(index*sizeof())); + + /** + * Setter for field: + * {@snippet lang=c : + * UInt16 bInterfaceClass + * } + */ + public static void bInterfaceClass(MemorySegment struct, short fieldValue) { + struct.set(bInterfaceClass$LAYOUT, bInterfaceClass$OFFSET, fieldValue); } - public static void bInterfaceClass$set(MemorySegment seg, long index, short x) { - IOUSBFindInterfaceRequest.bInterfaceClass$VH.set(seg.asSlice(index*sizeof()), x); + + private static final OfShort bInterfaceSubClass$LAYOUT = (OfShort)$LAYOUT.select(groupElement("bInterfaceSubClass")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt16 bInterfaceSubClass + * } + */ + public static final OfShort bInterfaceSubClass$layout() { + return bInterfaceSubClass$LAYOUT; } - static final VarHandle bInterfaceSubClass$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("bInterfaceSubClass")); - public static VarHandle bInterfaceSubClass$VH() { - return IOUSBFindInterfaceRequest.bInterfaceSubClass$VH; + + private static final long bInterfaceSubClass$OFFSET = $LAYOUT.byteOffset(groupElement("bInterfaceSubClass")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt16 bInterfaceSubClass + * } + */ + public static final long bInterfaceSubClass$offset() { + return bInterfaceSubClass$OFFSET; } - public static short bInterfaceSubClass$get(MemorySegment seg) { - return (short)IOUSBFindInterfaceRequest.bInterfaceSubClass$VH.get(seg); + + /** + * Getter for field: + * {@snippet lang=c : + * UInt16 bInterfaceSubClass + * } + */ + public static short bInterfaceSubClass(MemorySegment struct) { + return struct.get(bInterfaceSubClass$LAYOUT, bInterfaceSubClass$OFFSET); } - public static void bInterfaceSubClass$set( MemorySegment seg, short x) { - IOUSBFindInterfaceRequest.bInterfaceSubClass$VH.set(seg, x); + + /** + * Setter for field: + * {@snippet lang=c : + * UInt16 bInterfaceSubClass + * } + */ + public static void bInterfaceSubClass(MemorySegment struct, short fieldValue) { + struct.set(bInterfaceSubClass$LAYOUT, bInterfaceSubClass$OFFSET, fieldValue); } - public static short bInterfaceSubClass$get(MemorySegment seg, long index) { - return (short)IOUSBFindInterfaceRequest.bInterfaceSubClass$VH.get(seg.asSlice(index*sizeof())); + + private static final OfShort bInterfaceProtocol$LAYOUT = (OfShort)$LAYOUT.select(groupElement("bInterfaceProtocol")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt16 bInterfaceProtocol + * } + */ + public static final OfShort bInterfaceProtocol$layout() { + return bInterfaceProtocol$LAYOUT; } - public static void bInterfaceSubClass$set(MemorySegment seg, long index, short x) { - IOUSBFindInterfaceRequest.bInterfaceSubClass$VH.set(seg.asSlice(index*sizeof()), x); + + private static final long bInterfaceProtocol$OFFSET = $LAYOUT.byteOffset(groupElement("bInterfaceProtocol")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt16 bInterfaceProtocol + * } + */ + public static final long bInterfaceProtocol$offset() { + return bInterfaceProtocol$OFFSET; } - static final VarHandle bInterfaceProtocol$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("bInterfaceProtocol")); - public static VarHandle bInterfaceProtocol$VH() { - return IOUSBFindInterfaceRequest.bInterfaceProtocol$VH; + + /** + * Getter for field: + * {@snippet lang=c : + * UInt16 bInterfaceProtocol + * } + */ + public static short bInterfaceProtocol(MemorySegment struct) { + return struct.get(bInterfaceProtocol$LAYOUT, bInterfaceProtocol$OFFSET); } - public static short bInterfaceProtocol$get(MemorySegment seg) { - return (short)IOUSBFindInterfaceRequest.bInterfaceProtocol$VH.get(seg); + + /** + * Setter for field: + * {@snippet lang=c : + * UInt16 bInterfaceProtocol + * } + */ + public static void bInterfaceProtocol(MemorySegment struct, short fieldValue) { + struct.set(bInterfaceProtocol$LAYOUT, bInterfaceProtocol$OFFSET, fieldValue); } - public static void bInterfaceProtocol$set( MemorySegment seg, short x) { - IOUSBFindInterfaceRequest.bInterfaceProtocol$VH.set(seg, x); + + private static final OfShort bAlternateSetting$LAYOUT = (OfShort)$LAYOUT.select(groupElement("bAlternateSetting")); + + /** + * Layout for field: + * {@snippet lang=c : + * UInt16 bAlternateSetting + * } + */ + public static final OfShort bAlternateSetting$layout() { + return bAlternateSetting$LAYOUT; } - public static short bInterfaceProtocol$get(MemorySegment seg, long index) { - return (short)IOUSBFindInterfaceRequest.bInterfaceProtocol$VH.get(seg.asSlice(index*sizeof())); + + private static final long bAlternateSetting$OFFSET = $LAYOUT.byteOffset(groupElement("bAlternateSetting")); + + /** + * Offset for field: + * {@snippet lang=c : + * UInt16 bAlternateSetting + * } + */ + public static final long bAlternateSetting$offset() { + return bAlternateSetting$OFFSET; } - public static void bInterfaceProtocol$set(MemorySegment seg, long index, short x) { - IOUSBFindInterfaceRequest.bInterfaceProtocol$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Getter for field: + * {@snippet lang=c : + * UInt16 bAlternateSetting + * } + */ + public static short bAlternateSetting(MemorySegment struct) { + return struct.get(bAlternateSetting$LAYOUT, bAlternateSetting$OFFSET); } - static final VarHandle bAlternateSetting$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("bAlternateSetting")); - public static VarHandle bAlternateSetting$VH() { - return IOUSBFindInterfaceRequest.bAlternateSetting$VH; + + /** + * Setter for field: + * {@snippet lang=c : + * UInt16 bAlternateSetting + * } + */ + public static void bAlternateSetting(MemorySegment struct, short fieldValue) { + struct.set(bAlternateSetting$LAYOUT, bAlternateSetting$OFFSET, fieldValue); } - public static short bAlternateSetting$get(MemorySegment seg) { - return (short)IOUSBFindInterfaceRequest.bAlternateSetting$VH.get(seg); + + /** + * Obtains a slice of {@code arrayParam} which selects the array element at {@code index}. + * The returned segment has address {@code arrayParam.address() + index * layout().byteSize()} + */ + public static MemorySegment asSlice(MemorySegment array, long index) { + return array.asSlice(layout().byteSize() * index); } - public static void bAlternateSetting$set( MemorySegment seg, short x) { - IOUSBFindInterfaceRequest.bAlternateSetting$VH.set(seg, x); + + /** + * The size (in bytes) of this struct + */ + public static long sizeof() { return layout().byteSize(); } + + /** + * Allocate a segment of size {@code layout().byteSize()} using {@code allocator} + */ + public static MemorySegment allocate(SegmentAllocator allocator) { + return allocator.allocate(layout()); } - public static short bAlternateSetting$get(MemorySegment seg, long index) { - return (short)IOUSBFindInterfaceRequest.bAlternateSetting$VH.get(seg.asSlice(index*sizeof())); + + /** + * Allocate an array of size {@code elementCount} using {@code allocator}. + * The returned segment has size {@code elementCount * layout().byteSize()}. + */ + public static MemorySegment allocateArray(long elementCount, SegmentAllocator allocator) { + return allocator.allocate(MemoryLayout.sequenceLayout(elementCount, layout())); } - public static void bAlternateSetting$set(MemorySegment seg, long index, short x) { - IOUSBFindInterfaceRequest.bAlternateSetting$VH.set(seg.asSlice(index*sizeof()), x); + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, Arena arena, Consumer cleanup) { + return reinterpret(addr, 1, arena, cleanup); } - public static long sizeof() { return $LAYOUT().byteSize(); } - public static MemorySegment allocate(SegmentAllocator allocator) { return allocator.allocate($LAYOUT()); } - public static MemorySegment allocateArray(int len, SegmentAllocator allocator) { - return allocator.allocate(MemoryLayout.sequenceLayout(len, $LAYOUT())); + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code elementCount * layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, long elementCount, Arena arena, Consumer cleanup) { + return addr.reinterpret(layout().byteSize() * elementCount, arena, cleanup); } - public static MemorySegment ofAddress(MemoryAddress addr, MemorySession session) { return RuntimeHelper.asArray(addr, $LAYOUT(), 1, session); } } - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBInterfaceInterface.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBInterfaceInterface.java deleted file mode 100644 index 11bc0952..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBInterfaceInterface.java +++ /dev/null @@ -1,9 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.macos.gen.iokit; - -public class IOUSBInterfaceInterface extends IOUSBInterfaceStruct942 { - -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBInterfaceStruct190.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBInterfaceStruct190.java new file mode 100644 index 00000000..f63ecf5f --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBInterfaceStruct190.java @@ -0,0 +1,4506 @@ +// Generated by jextract + +package net.codecrete.usb.macos.gen.iokit; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +/** + * {@snippet lang=c : + * struct IOUSBInterfaceStruct190 { + * void *_reserved; + * HRESULT (*QueryInterface)(void *, REFIID, LPVOID *); + * ULONG (*AddRef)(void *); + * ULONG (*Release)(void *); + * IOReturn (*CreateInterfaceAsyncEventSource)(void *, CFRunLoopSourceRef *); + * CFRunLoopSourceRef (*GetInterfaceAsyncEventSource)(void *); + * IOReturn (*CreateInterfaceAsyncPort)(void *, mach_port_t *); + * mach_port_t (*GetInterfaceAsyncPort)(void *); + * IOReturn (*USBInterfaceOpen)(void *); + * IOReturn (*USBInterfaceClose)(void *); + * IOReturn (*GetInterfaceClass)(void *, UInt8 *); + * IOReturn (*GetInterfaceSubClass)(void *, UInt8 *); + * IOReturn (*GetInterfaceProtocol)(void *, UInt8 *); + * IOReturn (*GetDeviceVendor)(void *, UInt16 *); + * IOReturn (*GetDeviceProduct)(void *, UInt16 *); + * IOReturn (*GetDeviceReleaseNumber)(void *, UInt16 *); + * IOReturn (*GetConfigurationValue)(void *, UInt8 *); + * IOReturn (*GetInterfaceNumber)(void *, UInt8 *); + * IOReturn (*GetAlternateSetting)(void *, UInt8 *); + * IOReturn (*GetNumEndpoints)(void *, UInt8 *); + * IOReturn (*GetLocationID)(void *, UInt32 *); + * IOReturn (*GetDevice)(void *, io_service_t *); + * IOReturn (*SetAlternateInterface)(void *, UInt8); + * IOReturn (*GetBusFrameNumber)(void *, UInt64 *, AbsoluteTime *); + * IOReturn (*ControlRequest)(void *, UInt8, IOUSBDevRequest *); + * IOReturn (*ControlRequestAsync)(void *, UInt8, IOUSBDevRequest *, IOAsyncCallback1, void *); + * IOReturn (*GetPipeProperties)(void *, UInt8, UInt8 *, UInt8 *, UInt8 *, UInt16 *, UInt8 *); + * IOReturn (*GetPipeStatus)(void *, UInt8); + * IOReturn (*AbortPipe)(void *, UInt8); + * IOReturn (*ResetPipe)(void *, UInt8); + * IOReturn (*ClearPipeStall)(void *, UInt8); + * IOReturn (*ReadPipe)(void *, UInt8, void *, UInt32 *); + * IOReturn (*WritePipe)(void *, UInt8, void *, UInt32); + * IOReturn (*ReadPipeAsync)(void *, UInt8, void *, UInt32, IOAsyncCallback1, void *); + * IOReturn (*WritePipeAsync)(void *, UInt8, void *, UInt32, IOAsyncCallback1, void *); + * IOReturn (*ReadIsochPipeAsync)(void *, UInt8, void *, UInt64, UInt32, IOUSBIsocFrame *, IOAsyncCallback1, void *); + * IOReturn (*WriteIsochPipeAsync)(void *, UInt8, void *, UInt64, UInt32, IOUSBIsocFrame *, IOAsyncCallback1, void *); + * IOReturn (*ControlRequestTO)(void *, UInt8, IOUSBDevRequestTO *); + * IOReturn (*ControlRequestAsyncTO)(void *, UInt8, IOUSBDevRequestTO *, IOAsyncCallback1, void *); + * IOReturn (*ReadPipeTO)(void *, UInt8, void *, UInt32 *, UInt32, UInt32); + * IOReturn (*WritePipeTO)(void *, UInt8, void *, UInt32, UInt32, UInt32); + * IOReturn (*ReadPipeAsyncTO)(void *, UInt8, void *, UInt32, UInt32, UInt32, IOAsyncCallback1, void *); + * IOReturn (*WritePipeAsyncTO)(void *, UInt8, void *, UInt32, UInt32, UInt32, IOAsyncCallback1, void *); + * IOReturn (*USBInterfaceGetStringIndex)(void *, UInt8 *); + * IOReturn (*USBInterfaceOpenSeize)(void *); + * IOReturn (*ClearPipeStallBothEnds)(void *, UInt8); + * IOReturn (*SetPipePolicy)(void *, UInt8, UInt16, UInt8); + * IOReturn (*GetBandwidthAvailable)(void *, UInt32 *); + * IOReturn (*GetEndpointProperties)(void *, UInt8, UInt8, UInt8, UInt8 *, UInt16 *, UInt8 *); + * } + * } + */ +public class IOUSBInterfaceStruct190 { + + IOUSBInterfaceStruct190() { + // Should not be called directly + } + + private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( + IOKit.C_POINTER.withName("_reserved"), + IOKit.C_POINTER.withName("QueryInterface"), + IOKit.C_POINTER.withName("AddRef"), + IOKit.C_POINTER.withName("Release"), + IOKit.C_POINTER.withName("CreateInterfaceAsyncEventSource"), + IOKit.C_POINTER.withName("GetInterfaceAsyncEventSource"), + IOKit.C_POINTER.withName("CreateInterfaceAsyncPort"), + IOKit.C_POINTER.withName("GetInterfaceAsyncPort"), + IOKit.C_POINTER.withName("USBInterfaceOpen"), + IOKit.C_POINTER.withName("USBInterfaceClose"), + IOKit.C_POINTER.withName("GetInterfaceClass"), + IOKit.C_POINTER.withName("GetInterfaceSubClass"), + IOKit.C_POINTER.withName("GetInterfaceProtocol"), + IOKit.C_POINTER.withName("GetDeviceVendor"), + IOKit.C_POINTER.withName("GetDeviceProduct"), + IOKit.C_POINTER.withName("GetDeviceReleaseNumber"), + IOKit.C_POINTER.withName("GetConfigurationValue"), + IOKit.C_POINTER.withName("GetInterfaceNumber"), + IOKit.C_POINTER.withName("GetAlternateSetting"), + IOKit.C_POINTER.withName("GetNumEndpoints"), + IOKit.C_POINTER.withName("GetLocationID"), + IOKit.C_POINTER.withName("GetDevice"), + IOKit.C_POINTER.withName("SetAlternateInterface"), + IOKit.C_POINTER.withName("GetBusFrameNumber"), + IOKit.C_POINTER.withName("ControlRequest"), + IOKit.C_POINTER.withName("ControlRequestAsync"), + IOKit.C_POINTER.withName("GetPipeProperties"), + IOKit.C_POINTER.withName("GetPipeStatus"), + IOKit.C_POINTER.withName("AbortPipe"), + IOKit.C_POINTER.withName("ResetPipe"), + IOKit.C_POINTER.withName("ClearPipeStall"), + IOKit.C_POINTER.withName("ReadPipe"), + IOKit.C_POINTER.withName("WritePipe"), + IOKit.C_POINTER.withName("ReadPipeAsync"), + IOKit.C_POINTER.withName("WritePipeAsync"), + IOKit.C_POINTER.withName("ReadIsochPipeAsync"), + IOKit.C_POINTER.withName("WriteIsochPipeAsync"), + IOKit.C_POINTER.withName("ControlRequestTO"), + IOKit.C_POINTER.withName("ControlRequestAsyncTO"), + IOKit.C_POINTER.withName("ReadPipeTO"), + IOKit.C_POINTER.withName("WritePipeTO"), + IOKit.C_POINTER.withName("ReadPipeAsyncTO"), + IOKit.C_POINTER.withName("WritePipeAsyncTO"), + IOKit.C_POINTER.withName("USBInterfaceGetStringIndex"), + IOKit.C_POINTER.withName("USBInterfaceOpenSeize"), + IOKit.C_POINTER.withName("ClearPipeStallBothEnds"), + IOKit.C_POINTER.withName("SetPipePolicy"), + IOKit.C_POINTER.withName("GetBandwidthAvailable"), + IOKit.C_POINTER.withName("GetEndpointProperties") + ).withName("IOUSBInterfaceStruct190"); + + /** + * The layout of this struct + */ + public static final GroupLayout layout() { + return $LAYOUT; + } + + private static final AddressLayout _reserved$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("_reserved")); + + /** + * Layout for field: + * {@snippet lang=c : + * void *_reserved + * } + */ + public static final AddressLayout _reserved$layout() { + return _reserved$LAYOUT; + } + + private static final long _reserved$OFFSET = $LAYOUT.byteOffset(groupElement("_reserved")); + + /** + * Offset for field: + * {@snippet lang=c : + * void *_reserved + * } + */ + public static final long _reserved$offset() { + return _reserved$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * void *_reserved + * } + */ + public static MemorySegment _reserved(MemorySegment struct) { + return struct.get(_reserved$LAYOUT, _reserved$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * void *_reserved + * } + */ + public static void _reserved(MemorySegment struct, MemorySegment fieldValue) { + struct.set(_reserved$LAYOUT, _reserved$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * HRESULT (*QueryInterface)(void *, REFIID, LPVOID *) + * } + */ + public final static class QueryInterface { + + private QueryInterface() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + CFUUIDBytes.layout(), + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1, MemorySegment _x2) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout QueryInterface$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("QueryInterface")); + + /** + * Layout for field: + * {@snippet lang=c : + * HRESULT (*QueryInterface)(void *, REFIID, LPVOID *) + * } + */ + public static final AddressLayout QueryInterface$layout() { + return QueryInterface$LAYOUT; + } + + private static final long QueryInterface$OFFSET = $LAYOUT.byteOffset(groupElement("QueryInterface")); + + /** + * Offset for field: + * {@snippet lang=c : + * HRESULT (*QueryInterface)(void *, REFIID, LPVOID *) + * } + */ + public static final long QueryInterface$offset() { + return QueryInterface$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * HRESULT (*QueryInterface)(void *, REFIID, LPVOID *) + * } + */ + public static MemorySegment QueryInterface(MemorySegment struct) { + return struct.get(QueryInterface$LAYOUT, QueryInterface$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * HRESULT (*QueryInterface)(void *, REFIID, LPVOID *) + * } + */ + public static void QueryInterface(MemorySegment struct, MemorySegment fieldValue) { + struct.set(QueryInterface$LAYOUT, QueryInterface$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * ULONG (*AddRef)(void *) + * } + */ + public final static class AddRef { + + private AddRef() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout AddRef$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("AddRef")); + + /** + * Layout for field: + * {@snippet lang=c : + * ULONG (*AddRef)(void *) + * } + */ + public static final AddressLayout AddRef$layout() { + return AddRef$LAYOUT; + } + + private static final long AddRef$OFFSET = $LAYOUT.byteOffset(groupElement("AddRef")); + + /** + * Offset for field: + * {@snippet lang=c : + * ULONG (*AddRef)(void *) + * } + */ + public static final long AddRef$offset() { + return AddRef$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * ULONG (*AddRef)(void *) + * } + */ + public static MemorySegment AddRef(MemorySegment struct) { + return struct.get(AddRef$LAYOUT, AddRef$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * ULONG (*AddRef)(void *) + * } + */ + public static void AddRef(MemorySegment struct, MemorySegment fieldValue) { + struct.set(AddRef$LAYOUT, AddRef$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * ULONG (*Release)(void *) + * } + */ + public final static class Release { + + private Release() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout Release$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("Release")); + + /** + * Layout for field: + * {@snippet lang=c : + * ULONG (*Release)(void *) + * } + */ + public static final AddressLayout Release$layout() { + return Release$LAYOUT; + } + + private static final long Release$OFFSET = $LAYOUT.byteOffset(groupElement("Release")); + + /** + * Offset for field: + * {@snippet lang=c : + * ULONG (*Release)(void *) + * } + */ + public static final long Release$offset() { + return Release$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * ULONG (*Release)(void *) + * } + */ + public static MemorySegment Release(MemorySegment struct) { + return struct.get(Release$LAYOUT, Release$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * ULONG (*Release)(void *) + * } + */ + public static void Release(MemorySegment struct, MemorySegment fieldValue) { + struct.set(Release$LAYOUT, Release$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*CreateInterfaceAsyncEventSource)(void *, CFRunLoopSourceRef *) + * } + */ + public final static class CreateInterfaceAsyncEventSource { + + private CreateInterfaceAsyncEventSource() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout CreateInterfaceAsyncEventSource$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("CreateInterfaceAsyncEventSource")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*CreateInterfaceAsyncEventSource)(void *, CFRunLoopSourceRef *) + * } + */ + public static final AddressLayout CreateInterfaceAsyncEventSource$layout() { + return CreateInterfaceAsyncEventSource$LAYOUT; + } + + private static final long CreateInterfaceAsyncEventSource$OFFSET = $LAYOUT.byteOffset(groupElement("CreateInterfaceAsyncEventSource")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*CreateInterfaceAsyncEventSource)(void *, CFRunLoopSourceRef *) + * } + */ + public static final long CreateInterfaceAsyncEventSource$offset() { + return CreateInterfaceAsyncEventSource$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*CreateInterfaceAsyncEventSource)(void *, CFRunLoopSourceRef *) + * } + */ + public static MemorySegment CreateInterfaceAsyncEventSource(MemorySegment struct) { + return struct.get(CreateInterfaceAsyncEventSource$LAYOUT, CreateInterfaceAsyncEventSource$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*CreateInterfaceAsyncEventSource)(void *, CFRunLoopSourceRef *) + * } + */ + public static void CreateInterfaceAsyncEventSource(MemorySegment struct, MemorySegment fieldValue) { + struct.set(CreateInterfaceAsyncEventSource$LAYOUT, CreateInterfaceAsyncEventSource$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * CFRunLoopSourceRef (*GetInterfaceAsyncEventSource)(void *) + * } + */ + public final static class GetInterfaceAsyncEventSource { + + private GetInterfaceAsyncEventSource() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static MemorySegment invoke(MemorySegment funcPtr, MemorySegment _x0) { + try { + return (MemorySegment) DOWN$MH.invokeExact(funcPtr, _x0); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetInterfaceAsyncEventSource$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetInterfaceAsyncEventSource")); + + /** + * Layout for field: + * {@snippet lang=c : + * CFRunLoopSourceRef (*GetInterfaceAsyncEventSource)(void *) + * } + */ + public static final AddressLayout GetInterfaceAsyncEventSource$layout() { + return GetInterfaceAsyncEventSource$LAYOUT; + } + + private static final long GetInterfaceAsyncEventSource$OFFSET = $LAYOUT.byteOffset(groupElement("GetInterfaceAsyncEventSource")); + + /** + * Offset for field: + * {@snippet lang=c : + * CFRunLoopSourceRef (*GetInterfaceAsyncEventSource)(void *) + * } + */ + public static final long GetInterfaceAsyncEventSource$offset() { + return GetInterfaceAsyncEventSource$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * CFRunLoopSourceRef (*GetInterfaceAsyncEventSource)(void *) + * } + */ + public static MemorySegment GetInterfaceAsyncEventSource(MemorySegment struct) { + return struct.get(GetInterfaceAsyncEventSource$LAYOUT, GetInterfaceAsyncEventSource$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * CFRunLoopSourceRef (*GetInterfaceAsyncEventSource)(void *) + * } + */ + public static void GetInterfaceAsyncEventSource(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetInterfaceAsyncEventSource$LAYOUT, GetInterfaceAsyncEventSource$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*CreateInterfaceAsyncPort)(void *, mach_port_t *) + * } + */ + public final static class CreateInterfaceAsyncPort { + + private CreateInterfaceAsyncPort() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout CreateInterfaceAsyncPort$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("CreateInterfaceAsyncPort")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*CreateInterfaceAsyncPort)(void *, mach_port_t *) + * } + */ + public static final AddressLayout CreateInterfaceAsyncPort$layout() { + return CreateInterfaceAsyncPort$LAYOUT; + } + + private static final long CreateInterfaceAsyncPort$OFFSET = $LAYOUT.byteOffset(groupElement("CreateInterfaceAsyncPort")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*CreateInterfaceAsyncPort)(void *, mach_port_t *) + * } + */ + public static final long CreateInterfaceAsyncPort$offset() { + return CreateInterfaceAsyncPort$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*CreateInterfaceAsyncPort)(void *, mach_port_t *) + * } + */ + public static MemorySegment CreateInterfaceAsyncPort(MemorySegment struct) { + return struct.get(CreateInterfaceAsyncPort$LAYOUT, CreateInterfaceAsyncPort$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*CreateInterfaceAsyncPort)(void *, mach_port_t *) + * } + */ + public static void CreateInterfaceAsyncPort(MemorySegment struct, MemorySegment fieldValue) { + struct.set(CreateInterfaceAsyncPort$LAYOUT, CreateInterfaceAsyncPort$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * mach_port_t (*GetInterfaceAsyncPort)(void *) + * } + */ + public final static class GetInterfaceAsyncPort { + + private GetInterfaceAsyncPort() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetInterfaceAsyncPort$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetInterfaceAsyncPort")); + + /** + * Layout for field: + * {@snippet lang=c : + * mach_port_t (*GetInterfaceAsyncPort)(void *) + * } + */ + public static final AddressLayout GetInterfaceAsyncPort$layout() { + return GetInterfaceAsyncPort$LAYOUT; + } + + private static final long GetInterfaceAsyncPort$OFFSET = $LAYOUT.byteOffset(groupElement("GetInterfaceAsyncPort")); + + /** + * Offset for field: + * {@snippet lang=c : + * mach_port_t (*GetInterfaceAsyncPort)(void *) + * } + */ + public static final long GetInterfaceAsyncPort$offset() { + return GetInterfaceAsyncPort$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * mach_port_t (*GetInterfaceAsyncPort)(void *) + * } + */ + public static MemorySegment GetInterfaceAsyncPort(MemorySegment struct) { + return struct.get(GetInterfaceAsyncPort$LAYOUT, GetInterfaceAsyncPort$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * mach_port_t (*GetInterfaceAsyncPort)(void *) + * } + */ + public static void GetInterfaceAsyncPort(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetInterfaceAsyncPort$LAYOUT, GetInterfaceAsyncPort$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*USBInterfaceOpen)(void *) + * } + */ + public final static class USBInterfaceOpen { + + private USBInterfaceOpen() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout USBInterfaceOpen$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("USBInterfaceOpen")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*USBInterfaceOpen)(void *) + * } + */ + public static final AddressLayout USBInterfaceOpen$layout() { + return USBInterfaceOpen$LAYOUT; + } + + private static final long USBInterfaceOpen$OFFSET = $LAYOUT.byteOffset(groupElement("USBInterfaceOpen")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*USBInterfaceOpen)(void *) + * } + */ + public static final long USBInterfaceOpen$offset() { + return USBInterfaceOpen$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*USBInterfaceOpen)(void *) + * } + */ + public static MemorySegment USBInterfaceOpen(MemorySegment struct) { + return struct.get(USBInterfaceOpen$LAYOUT, USBInterfaceOpen$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*USBInterfaceOpen)(void *) + * } + */ + public static void USBInterfaceOpen(MemorySegment struct, MemorySegment fieldValue) { + struct.set(USBInterfaceOpen$LAYOUT, USBInterfaceOpen$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*USBInterfaceClose)(void *) + * } + */ + public final static class USBInterfaceClose { + + private USBInterfaceClose() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout USBInterfaceClose$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("USBInterfaceClose")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*USBInterfaceClose)(void *) + * } + */ + public static final AddressLayout USBInterfaceClose$layout() { + return USBInterfaceClose$LAYOUT; + } + + private static final long USBInterfaceClose$OFFSET = $LAYOUT.byteOffset(groupElement("USBInterfaceClose")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*USBInterfaceClose)(void *) + * } + */ + public static final long USBInterfaceClose$offset() { + return USBInterfaceClose$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*USBInterfaceClose)(void *) + * } + */ + public static MemorySegment USBInterfaceClose(MemorySegment struct) { + return struct.get(USBInterfaceClose$LAYOUT, USBInterfaceClose$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*USBInterfaceClose)(void *) + * } + */ + public static void USBInterfaceClose(MemorySegment struct, MemorySegment fieldValue) { + struct.set(USBInterfaceClose$LAYOUT, USBInterfaceClose$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetInterfaceClass)(void *, UInt8 *) + * } + */ + public final static class GetInterfaceClass { + + private GetInterfaceClass() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetInterfaceClass$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetInterfaceClass")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetInterfaceClass)(void *, UInt8 *) + * } + */ + public static final AddressLayout GetInterfaceClass$layout() { + return GetInterfaceClass$LAYOUT; + } + + private static final long GetInterfaceClass$OFFSET = $LAYOUT.byteOffset(groupElement("GetInterfaceClass")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetInterfaceClass)(void *, UInt8 *) + * } + */ + public static final long GetInterfaceClass$offset() { + return GetInterfaceClass$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetInterfaceClass)(void *, UInt8 *) + * } + */ + public static MemorySegment GetInterfaceClass(MemorySegment struct) { + return struct.get(GetInterfaceClass$LAYOUT, GetInterfaceClass$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetInterfaceClass)(void *, UInt8 *) + * } + */ + public static void GetInterfaceClass(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetInterfaceClass$LAYOUT, GetInterfaceClass$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetInterfaceSubClass)(void *, UInt8 *) + * } + */ + public final static class GetInterfaceSubClass { + + private GetInterfaceSubClass() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetInterfaceSubClass$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetInterfaceSubClass")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetInterfaceSubClass)(void *, UInt8 *) + * } + */ + public static final AddressLayout GetInterfaceSubClass$layout() { + return GetInterfaceSubClass$LAYOUT; + } + + private static final long GetInterfaceSubClass$OFFSET = $LAYOUT.byteOffset(groupElement("GetInterfaceSubClass")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetInterfaceSubClass)(void *, UInt8 *) + * } + */ + public static final long GetInterfaceSubClass$offset() { + return GetInterfaceSubClass$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetInterfaceSubClass)(void *, UInt8 *) + * } + */ + public static MemorySegment GetInterfaceSubClass(MemorySegment struct) { + return struct.get(GetInterfaceSubClass$LAYOUT, GetInterfaceSubClass$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetInterfaceSubClass)(void *, UInt8 *) + * } + */ + public static void GetInterfaceSubClass(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetInterfaceSubClass$LAYOUT, GetInterfaceSubClass$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetInterfaceProtocol)(void *, UInt8 *) + * } + */ + public final static class GetInterfaceProtocol { + + private GetInterfaceProtocol() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetInterfaceProtocol$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetInterfaceProtocol")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetInterfaceProtocol)(void *, UInt8 *) + * } + */ + public static final AddressLayout GetInterfaceProtocol$layout() { + return GetInterfaceProtocol$LAYOUT; + } + + private static final long GetInterfaceProtocol$OFFSET = $LAYOUT.byteOffset(groupElement("GetInterfaceProtocol")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetInterfaceProtocol)(void *, UInt8 *) + * } + */ + public static final long GetInterfaceProtocol$offset() { + return GetInterfaceProtocol$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetInterfaceProtocol)(void *, UInt8 *) + * } + */ + public static MemorySegment GetInterfaceProtocol(MemorySegment struct) { + return struct.get(GetInterfaceProtocol$LAYOUT, GetInterfaceProtocol$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetInterfaceProtocol)(void *, UInt8 *) + * } + */ + public static void GetInterfaceProtocol(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetInterfaceProtocol$LAYOUT, GetInterfaceProtocol$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetDeviceVendor)(void *, UInt16 *) + * } + */ + public final static class GetDeviceVendor { + + private GetDeviceVendor() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetDeviceVendor$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetDeviceVendor")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceVendor)(void *, UInt16 *) + * } + */ + public static final AddressLayout GetDeviceVendor$layout() { + return GetDeviceVendor$LAYOUT; + } + + private static final long GetDeviceVendor$OFFSET = $LAYOUT.byteOffset(groupElement("GetDeviceVendor")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceVendor)(void *, UInt16 *) + * } + */ + public static final long GetDeviceVendor$offset() { + return GetDeviceVendor$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceVendor)(void *, UInt16 *) + * } + */ + public static MemorySegment GetDeviceVendor(MemorySegment struct) { + return struct.get(GetDeviceVendor$LAYOUT, GetDeviceVendor$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceVendor)(void *, UInt16 *) + * } + */ + public static void GetDeviceVendor(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetDeviceVendor$LAYOUT, GetDeviceVendor$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetDeviceProduct)(void *, UInt16 *) + * } + */ + public final static class GetDeviceProduct { + + private GetDeviceProduct() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetDeviceProduct$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetDeviceProduct")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceProduct)(void *, UInt16 *) + * } + */ + public static final AddressLayout GetDeviceProduct$layout() { + return GetDeviceProduct$LAYOUT; + } + + private static final long GetDeviceProduct$OFFSET = $LAYOUT.byteOffset(groupElement("GetDeviceProduct")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceProduct)(void *, UInt16 *) + * } + */ + public static final long GetDeviceProduct$offset() { + return GetDeviceProduct$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceProduct)(void *, UInt16 *) + * } + */ + public static MemorySegment GetDeviceProduct(MemorySegment struct) { + return struct.get(GetDeviceProduct$LAYOUT, GetDeviceProduct$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceProduct)(void *, UInt16 *) + * } + */ + public static void GetDeviceProduct(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetDeviceProduct$LAYOUT, GetDeviceProduct$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetDeviceReleaseNumber)(void *, UInt16 *) + * } + */ + public final static class GetDeviceReleaseNumber { + + private GetDeviceReleaseNumber() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetDeviceReleaseNumber$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetDeviceReleaseNumber")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceReleaseNumber)(void *, UInt16 *) + * } + */ + public static final AddressLayout GetDeviceReleaseNumber$layout() { + return GetDeviceReleaseNumber$LAYOUT; + } + + private static final long GetDeviceReleaseNumber$OFFSET = $LAYOUT.byteOffset(groupElement("GetDeviceReleaseNumber")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceReleaseNumber)(void *, UInt16 *) + * } + */ + public static final long GetDeviceReleaseNumber$offset() { + return GetDeviceReleaseNumber$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceReleaseNumber)(void *, UInt16 *) + * } + */ + public static MemorySegment GetDeviceReleaseNumber(MemorySegment struct) { + return struct.get(GetDeviceReleaseNumber$LAYOUT, GetDeviceReleaseNumber$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetDeviceReleaseNumber)(void *, UInt16 *) + * } + */ + public static void GetDeviceReleaseNumber(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetDeviceReleaseNumber$LAYOUT, GetDeviceReleaseNumber$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetConfigurationValue)(void *, UInt8 *) + * } + */ + public final static class GetConfigurationValue { + + private GetConfigurationValue() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetConfigurationValue$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetConfigurationValue")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetConfigurationValue)(void *, UInt8 *) + * } + */ + public static final AddressLayout GetConfigurationValue$layout() { + return GetConfigurationValue$LAYOUT; + } + + private static final long GetConfigurationValue$OFFSET = $LAYOUT.byteOffset(groupElement("GetConfigurationValue")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetConfigurationValue)(void *, UInt8 *) + * } + */ + public static final long GetConfigurationValue$offset() { + return GetConfigurationValue$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetConfigurationValue)(void *, UInt8 *) + * } + */ + public static MemorySegment GetConfigurationValue(MemorySegment struct) { + return struct.get(GetConfigurationValue$LAYOUT, GetConfigurationValue$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetConfigurationValue)(void *, UInt8 *) + * } + */ + public static void GetConfigurationValue(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetConfigurationValue$LAYOUT, GetConfigurationValue$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetInterfaceNumber)(void *, UInt8 *) + * } + */ + public final static class GetInterfaceNumber { + + private GetInterfaceNumber() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetInterfaceNumber$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetInterfaceNumber")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetInterfaceNumber)(void *, UInt8 *) + * } + */ + public static final AddressLayout GetInterfaceNumber$layout() { + return GetInterfaceNumber$LAYOUT; + } + + private static final long GetInterfaceNumber$OFFSET = $LAYOUT.byteOffset(groupElement("GetInterfaceNumber")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetInterfaceNumber)(void *, UInt8 *) + * } + */ + public static final long GetInterfaceNumber$offset() { + return GetInterfaceNumber$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetInterfaceNumber)(void *, UInt8 *) + * } + */ + public static MemorySegment GetInterfaceNumber(MemorySegment struct) { + return struct.get(GetInterfaceNumber$LAYOUT, GetInterfaceNumber$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetInterfaceNumber)(void *, UInt8 *) + * } + */ + public static void GetInterfaceNumber(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetInterfaceNumber$LAYOUT, GetInterfaceNumber$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetAlternateSetting)(void *, UInt8 *) + * } + */ + public final static class GetAlternateSetting { + + private GetAlternateSetting() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetAlternateSetting$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetAlternateSetting")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetAlternateSetting)(void *, UInt8 *) + * } + */ + public static final AddressLayout GetAlternateSetting$layout() { + return GetAlternateSetting$LAYOUT; + } + + private static final long GetAlternateSetting$OFFSET = $LAYOUT.byteOffset(groupElement("GetAlternateSetting")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetAlternateSetting)(void *, UInt8 *) + * } + */ + public static final long GetAlternateSetting$offset() { + return GetAlternateSetting$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetAlternateSetting)(void *, UInt8 *) + * } + */ + public static MemorySegment GetAlternateSetting(MemorySegment struct) { + return struct.get(GetAlternateSetting$LAYOUT, GetAlternateSetting$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetAlternateSetting)(void *, UInt8 *) + * } + */ + public static void GetAlternateSetting(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetAlternateSetting$LAYOUT, GetAlternateSetting$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetNumEndpoints)(void *, UInt8 *) + * } + */ + public final static class GetNumEndpoints { + + private GetNumEndpoints() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetNumEndpoints$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetNumEndpoints")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetNumEndpoints)(void *, UInt8 *) + * } + */ + public static final AddressLayout GetNumEndpoints$layout() { + return GetNumEndpoints$LAYOUT; + } + + private static final long GetNumEndpoints$OFFSET = $LAYOUT.byteOffset(groupElement("GetNumEndpoints")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetNumEndpoints)(void *, UInt8 *) + * } + */ + public static final long GetNumEndpoints$offset() { + return GetNumEndpoints$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetNumEndpoints)(void *, UInt8 *) + * } + */ + public static MemorySegment GetNumEndpoints(MemorySegment struct) { + return struct.get(GetNumEndpoints$LAYOUT, GetNumEndpoints$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetNumEndpoints)(void *, UInt8 *) + * } + */ + public static void GetNumEndpoints(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetNumEndpoints$LAYOUT, GetNumEndpoints$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetLocationID)(void *, UInt32 *) + * } + */ + public final static class GetLocationID { + + private GetLocationID() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetLocationID$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetLocationID")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetLocationID)(void *, UInt32 *) + * } + */ + public static final AddressLayout GetLocationID$layout() { + return GetLocationID$LAYOUT; + } + + private static final long GetLocationID$OFFSET = $LAYOUT.byteOffset(groupElement("GetLocationID")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetLocationID)(void *, UInt32 *) + * } + */ + public static final long GetLocationID$offset() { + return GetLocationID$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetLocationID)(void *, UInt32 *) + * } + */ + public static MemorySegment GetLocationID(MemorySegment struct) { + return struct.get(GetLocationID$LAYOUT, GetLocationID$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetLocationID)(void *, UInt32 *) + * } + */ + public static void GetLocationID(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetLocationID$LAYOUT, GetLocationID$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetDevice)(void *, io_service_t *) + * } + */ + public final static class GetDevice { + + private GetDevice() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetDevice$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetDevice")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetDevice)(void *, io_service_t *) + * } + */ + public static final AddressLayout GetDevice$layout() { + return GetDevice$LAYOUT; + } + + private static final long GetDevice$OFFSET = $LAYOUT.byteOffset(groupElement("GetDevice")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetDevice)(void *, io_service_t *) + * } + */ + public static final long GetDevice$offset() { + return GetDevice$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetDevice)(void *, io_service_t *) + * } + */ + public static MemorySegment GetDevice(MemorySegment struct) { + return struct.get(GetDevice$LAYOUT, GetDevice$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetDevice)(void *, io_service_t *) + * } + */ + public static void GetDevice(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetDevice$LAYOUT, GetDevice$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*SetAlternateInterface)(void *, UInt8) + * } + */ + public final static class SetAlternateInterface { + + private SetAlternateInterface() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout SetAlternateInterface$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("SetAlternateInterface")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*SetAlternateInterface)(void *, UInt8) + * } + */ + public static final AddressLayout SetAlternateInterface$layout() { + return SetAlternateInterface$LAYOUT; + } + + private static final long SetAlternateInterface$OFFSET = $LAYOUT.byteOffset(groupElement("SetAlternateInterface")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*SetAlternateInterface)(void *, UInt8) + * } + */ + public static final long SetAlternateInterface$offset() { + return SetAlternateInterface$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*SetAlternateInterface)(void *, UInt8) + * } + */ + public static MemorySegment SetAlternateInterface(MemorySegment struct) { + return struct.get(SetAlternateInterface$LAYOUT, SetAlternateInterface$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*SetAlternateInterface)(void *, UInt8) + * } + */ + public static void SetAlternateInterface(MemorySegment struct, MemorySegment fieldValue) { + struct.set(SetAlternateInterface$LAYOUT, SetAlternateInterface$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetBusFrameNumber)(void *, UInt64 *, AbsoluteTime *) + * } + */ + public final static class GetBusFrameNumber { + + private GetBusFrameNumber() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1, MemorySegment _x2) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetBusFrameNumber$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetBusFrameNumber")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetBusFrameNumber)(void *, UInt64 *, AbsoluteTime *) + * } + */ + public static final AddressLayout GetBusFrameNumber$layout() { + return GetBusFrameNumber$LAYOUT; + } + + private static final long GetBusFrameNumber$OFFSET = $LAYOUT.byteOffset(groupElement("GetBusFrameNumber")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetBusFrameNumber)(void *, UInt64 *, AbsoluteTime *) + * } + */ + public static final long GetBusFrameNumber$offset() { + return GetBusFrameNumber$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetBusFrameNumber)(void *, UInt64 *, AbsoluteTime *) + * } + */ + public static MemorySegment GetBusFrameNumber(MemorySegment struct) { + return struct.get(GetBusFrameNumber$LAYOUT, GetBusFrameNumber$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetBusFrameNumber)(void *, UInt64 *, AbsoluteTime *) + * } + */ + public static void GetBusFrameNumber(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetBusFrameNumber$LAYOUT, GetBusFrameNumber$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*ControlRequest)(void *, UInt8, IOUSBDevRequest *) + * } + */ + public final static class ControlRequest { + + private ControlRequest() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1, MemorySegment _x2) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout ControlRequest$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("ControlRequest")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*ControlRequest)(void *, UInt8, IOUSBDevRequest *) + * } + */ + public static final AddressLayout ControlRequest$layout() { + return ControlRequest$LAYOUT; + } + + private static final long ControlRequest$OFFSET = $LAYOUT.byteOffset(groupElement("ControlRequest")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*ControlRequest)(void *, UInt8, IOUSBDevRequest *) + * } + */ + public static final long ControlRequest$offset() { + return ControlRequest$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*ControlRequest)(void *, UInt8, IOUSBDevRequest *) + * } + */ + public static MemorySegment ControlRequest(MemorySegment struct) { + return struct.get(ControlRequest$LAYOUT, ControlRequest$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*ControlRequest)(void *, UInt8, IOUSBDevRequest *) + * } + */ + public static void ControlRequest(MemorySegment struct, MemorySegment fieldValue) { + struct.set(ControlRequest$LAYOUT, ControlRequest$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*ControlRequestAsync)(void *, UInt8, IOUSBDevRequest *, IOAsyncCallback1, void *) + * } + */ + public final static class ControlRequestAsync { + + private ControlRequestAsync() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1, MemorySegment _x2, MemorySegment _x3, MemorySegment _x4) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2, _x3, _x4); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout ControlRequestAsync$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("ControlRequestAsync")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*ControlRequestAsync)(void *, UInt8, IOUSBDevRequest *, IOAsyncCallback1, void *) + * } + */ + public static final AddressLayout ControlRequestAsync$layout() { + return ControlRequestAsync$LAYOUT; + } + + private static final long ControlRequestAsync$OFFSET = $LAYOUT.byteOffset(groupElement("ControlRequestAsync")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*ControlRequestAsync)(void *, UInt8, IOUSBDevRequest *, IOAsyncCallback1, void *) + * } + */ + public static final long ControlRequestAsync$offset() { + return ControlRequestAsync$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*ControlRequestAsync)(void *, UInt8, IOUSBDevRequest *, IOAsyncCallback1, void *) + * } + */ + public static MemorySegment ControlRequestAsync(MemorySegment struct) { + return struct.get(ControlRequestAsync$LAYOUT, ControlRequestAsync$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*ControlRequestAsync)(void *, UInt8, IOUSBDevRequest *, IOAsyncCallback1, void *) + * } + */ + public static void ControlRequestAsync(MemorySegment struct, MemorySegment fieldValue) { + struct.set(ControlRequestAsync$LAYOUT, ControlRequestAsync$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetPipeProperties)(void *, UInt8, UInt8 *, UInt8 *, UInt8 *, UInt16 *, UInt8 *) + * } + */ + public final static class GetPipeProperties { + + private GetPipeProperties() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1, MemorySegment _x2, MemorySegment _x3, MemorySegment _x4, MemorySegment _x5, MemorySegment _x6) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2, _x3, _x4, _x5, _x6); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetPipeProperties$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetPipeProperties")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetPipeProperties)(void *, UInt8, UInt8 *, UInt8 *, UInt8 *, UInt16 *, UInt8 *) + * } + */ + public static final AddressLayout GetPipeProperties$layout() { + return GetPipeProperties$LAYOUT; + } + + private static final long GetPipeProperties$OFFSET = $LAYOUT.byteOffset(groupElement("GetPipeProperties")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetPipeProperties)(void *, UInt8, UInt8 *, UInt8 *, UInt8 *, UInt16 *, UInt8 *) + * } + */ + public static final long GetPipeProperties$offset() { + return GetPipeProperties$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetPipeProperties)(void *, UInt8, UInt8 *, UInt8 *, UInt8 *, UInt16 *, UInt8 *) + * } + */ + public static MemorySegment GetPipeProperties(MemorySegment struct) { + return struct.get(GetPipeProperties$LAYOUT, GetPipeProperties$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetPipeProperties)(void *, UInt8, UInt8 *, UInt8 *, UInt8 *, UInt16 *, UInt8 *) + * } + */ + public static void GetPipeProperties(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetPipeProperties$LAYOUT, GetPipeProperties$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetPipeStatus)(void *, UInt8) + * } + */ + public final static class GetPipeStatus { + + private GetPipeStatus() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetPipeStatus$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetPipeStatus")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetPipeStatus)(void *, UInt8) + * } + */ + public static final AddressLayout GetPipeStatus$layout() { + return GetPipeStatus$LAYOUT; + } + + private static final long GetPipeStatus$OFFSET = $LAYOUT.byteOffset(groupElement("GetPipeStatus")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetPipeStatus)(void *, UInt8) + * } + */ + public static final long GetPipeStatus$offset() { + return GetPipeStatus$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetPipeStatus)(void *, UInt8) + * } + */ + public static MemorySegment GetPipeStatus(MemorySegment struct) { + return struct.get(GetPipeStatus$LAYOUT, GetPipeStatus$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetPipeStatus)(void *, UInt8) + * } + */ + public static void GetPipeStatus(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetPipeStatus$LAYOUT, GetPipeStatus$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*AbortPipe)(void *, UInt8) + * } + */ + public final static class AbortPipe { + + private AbortPipe() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout AbortPipe$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("AbortPipe")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*AbortPipe)(void *, UInt8) + * } + */ + public static final AddressLayout AbortPipe$layout() { + return AbortPipe$LAYOUT; + } + + private static final long AbortPipe$OFFSET = $LAYOUT.byteOffset(groupElement("AbortPipe")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*AbortPipe)(void *, UInt8) + * } + */ + public static final long AbortPipe$offset() { + return AbortPipe$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*AbortPipe)(void *, UInt8) + * } + */ + public static MemorySegment AbortPipe(MemorySegment struct) { + return struct.get(AbortPipe$LAYOUT, AbortPipe$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*AbortPipe)(void *, UInt8) + * } + */ + public static void AbortPipe(MemorySegment struct, MemorySegment fieldValue) { + struct.set(AbortPipe$LAYOUT, AbortPipe$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*ResetPipe)(void *, UInt8) + * } + */ + public final static class ResetPipe { + + private ResetPipe() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout ResetPipe$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("ResetPipe")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*ResetPipe)(void *, UInt8) + * } + */ + public static final AddressLayout ResetPipe$layout() { + return ResetPipe$LAYOUT; + } + + private static final long ResetPipe$OFFSET = $LAYOUT.byteOffset(groupElement("ResetPipe")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*ResetPipe)(void *, UInt8) + * } + */ + public static final long ResetPipe$offset() { + return ResetPipe$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*ResetPipe)(void *, UInt8) + * } + */ + public static MemorySegment ResetPipe(MemorySegment struct) { + return struct.get(ResetPipe$LAYOUT, ResetPipe$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*ResetPipe)(void *, UInt8) + * } + */ + public static void ResetPipe(MemorySegment struct, MemorySegment fieldValue) { + struct.set(ResetPipe$LAYOUT, ResetPipe$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*ClearPipeStall)(void *, UInt8) + * } + */ + public final static class ClearPipeStall { + + private ClearPipeStall() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout ClearPipeStall$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("ClearPipeStall")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*ClearPipeStall)(void *, UInt8) + * } + */ + public static final AddressLayout ClearPipeStall$layout() { + return ClearPipeStall$LAYOUT; + } + + private static final long ClearPipeStall$OFFSET = $LAYOUT.byteOffset(groupElement("ClearPipeStall")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*ClearPipeStall)(void *, UInt8) + * } + */ + public static final long ClearPipeStall$offset() { + return ClearPipeStall$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*ClearPipeStall)(void *, UInt8) + * } + */ + public static MemorySegment ClearPipeStall(MemorySegment struct) { + return struct.get(ClearPipeStall$LAYOUT, ClearPipeStall$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*ClearPipeStall)(void *, UInt8) + * } + */ + public static void ClearPipeStall(MemorySegment struct, MemorySegment fieldValue) { + struct.set(ClearPipeStall$LAYOUT, ClearPipeStall$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*ReadPipe)(void *, UInt8, void *, UInt32 *) + * } + */ + public final static class ReadPipe { + + private ReadPipe() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1, MemorySegment _x2, MemorySegment _x3) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2, _x3); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout ReadPipe$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("ReadPipe")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*ReadPipe)(void *, UInt8, void *, UInt32 *) + * } + */ + public static final AddressLayout ReadPipe$layout() { + return ReadPipe$LAYOUT; + } + + private static final long ReadPipe$OFFSET = $LAYOUT.byteOffset(groupElement("ReadPipe")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*ReadPipe)(void *, UInt8, void *, UInt32 *) + * } + */ + public static final long ReadPipe$offset() { + return ReadPipe$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*ReadPipe)(void *, UInt8, void *, UInt32 *) + * } + */ + public static MemorySegment ReadPipe(MemorySegment struct) { + return struct.get(ReadPipe$LAYOUT, ReadPipe$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*ReadPipe)(void *, UInt8, void *, UInt32 *) + * } + */ + public static void ReadPipe(MemorySegment struct, MemorySegment fieldValue) { + struct.set(ReadPipe$LAYOUT, ReadPipe$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*WritePipe)(void *, UInt8, void *, UInt32) + * } + */ + public final static class WritePipe { + + private WritePipe() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR, + IOKit.C_POINTER, + IOKit.C_INT + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1, MemorySegment _x2, int _x3) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2, _x3); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout WritePipe$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("WritePipe")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*WritePipe)(void *, UInt8, void *, UInt32) + * } + */ + public static final AddressLayout WritePipe$layout() { + return WritePipe$LAYOUT; + } + + private static final long WritePipe$OFFSET = $LAYOUT.byteOffset(groupElement("WritePipe")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*WritePipe)(void *, UInt8, void *, UInt32) + * } + */ + public static final long WritePipe$offset() { + return WritePipe$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*WritePipe)(void *, UInt8, void *, UInt32) + * } + */ + public static MemorySegment WritePipe(MemorySegment struct) { + return struct.get(WritePipe$LAYOUT, WritePipe$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*WritePipe)(void *, UInt8, void *, UInt32) + * } + */ + public static void WritePipe(MemorySegment struct, MemorySegment fieldValue) { + struct.set(WritePipe$LAYOUT, WritePipe$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*ReadPipeAsync)(void *, UInt8, void *, UInt32, IOAsyncCallback1, void *) + * } + */ + public final static class ReadPipeAsync { + + private ReadPipeAsync() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR, + IOKit.C_POINTER, + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1, MemorySegment _x2, int _x3, MemorySegment _x4, MemorySegment _x5) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2, _x3, _x4, _x5); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout ReadPipeAsync$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("ReadPipeAsync")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*ReadPipeAsync)(void *, UInt8, void *, UInt32, IOAsyncCallback1, void *) + * } + */ + public static final AddressLayout ReadPipeAsync$layout() { + return ReadPipeAsync$LAYOUT; + } + + private static final long ReadPipeAsync$OFFSET = $LAYOUT.byteOffset(groupElement("ReadPipeAsync")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*ReadPipeAsync)(void *, UInt8, void *, UInt32, IOAsyncCallback1, void *) + * } + */ + public static final long ReadPipeAsync$offset() { + return ReadPipeAsync$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*ReadPipeAsync)(void *, UInt8, void *, UInt32, IOAsyncCallback1, void *) + * } + */ + public static MemorySegment ReadPipeAsync(MemorySegment struct) { + return struct.get(ReadPipeAsync$LAYOUT, ReadPipeAsync$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*ReadPipeAsync)(void *, UInt8, void *, UInt32, IOAsyncCallback1, void *) + * } + */ + public static void ReadPipeAsync(MemorySegment struct, MemorySegment fieldValue) { + struct.set(ReadPipeAsync$LAYOUT, ReadPipeAsync$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*WritePipeAsync)(void *, UInt8, void *, UInt32, IOAsyncCallback1, void *) + * } + */ + public final static class WritePipeAsync { + + private WritePipeAsync() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR, + IOKit.C_POINTER, + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1, MemorySegment _x2, int _x3, MemorySegment _x4, MemorySegment _x5) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2, _x3, _x4, _x5); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout WritePipeAsync$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("WritePipeAsync")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*WritePipeAsync)(void *, UInt8, void *, UInt32, IOAsyncCallback1, void *) + * } + */ + public static final AddressLayout WritePipeAsync$layout() { + return WritePipeAsync$LAYOUT; + } + + private static final long WritePipeAsync$OFFSET = $LAYOUT.byteOffset(groupElement("WritePipeAsync")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*WritePipeAsync)(void *, UInt8, void *, UInt32, IOAsyncCallback1, void *) + * } + */ + public static final long WritePipeAsync$offset() { + return WritePipeAsync$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*WritePipeAsync)(void *, UInt8, void *, UInt32, IOAsyncCallback1, void *) + * } + */ + public static MemorySegment WritePipeAsync(MemorySegment struct) { + return struct.get(WritePipeAsync$LAYOUT, WritePipeAsync$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*WritePipeAsync)(void *, UInt8, void *, UInt32, IOAsyncCallback1, void *) + * } + */ + public static void WritePipeAsync(MemorySegment struct, MemorySegment fieldValue) { + struct.set(WritePipeAsync$LAYOUT, WritePipeAsync$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*ReadIsochPipeAsync)(void *, UInt8, void *, UInt64, UInt32, IOUSBIsocFrame *, IOAsyncCallback1, void *) + * } + */ + public final static class ReadIsochPipeAsync { + + private ReadIsochPipeAsync() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR, + IOKit.C_POINTER, + IOKit.C_LONG_LONG, + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1, MemorySegment _x2, long _x3, int _x4, MemorySegment _x5, MemorySegment _x6, MemorySegment _x7) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2, _x3, _x4, _x5, _x6, _x7); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout ReadIsochPipeAsync$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("ReadIsochPipeAsync")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*ReadIsochPipeAsync)(void *, UInt8, void *, UInt64, UInt32, IOUSBIsocFrame *, IOAsyncCallback1, void *) + * } + */ + public static final AddressLayout ReadIsochPipeAsync$layout() { + return ReadIsochPipeAsync$LAYOUT; + } + + private static final long ReadIsochPipeAsync$OFFSET = $LAYOUT.byteOffset(groupElement("ReadIsochPipeAsync")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*ReadIsochPipeAsync)(void *, UInt8, void *, UInt64, UInt32, IOUSBIsocFrame *, IOAsyncCallback1, void *) + * } + */ + public static final long ReadIsochPipeAsync$offset() { + return ReadIsochPipeAsync$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*ReadIsochPipeAsync)(void *, UInt8, void *, UInt64, UInt32, IOUSBIsocFrame *, IOAsyncCallback1, void *) + * } + */ + public static MemorySegment ReadIsochPipeAsync(MemorySegment struct) { + return struct.get(ReadIsochPipeAsync$LAYOUT, ReadIsochPipeAsync$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*ReadIsochPipeAsync)(void *, UInt8, void *, UInt64, UInt32, IOUSBIsocFrame *, IOAsyncCallback1, void *) + * } + */ + public static void ReadIsochPipeAsync(MemorySegment struct, MemorySegment fieldValue) { + struct.set(ReadIsochPipeAsync$LAYOUT, ReadIsochPipeAsync$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*WriteIsochPipeAsync)(void *, UInt8, void *, UInt64, UInt32, IOUSBIsocFrame *, IOAsyncCallback1, void *) + * } + */ + public final static class WriteIsochPipeAsync { + + private WriteIsochPipeAsync() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR, + IOKit.C_POINTER, + IOKit.C_LONG_LONG, + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1, MemorySegment _x2, long _x3, int _x4, MemorySegment _x5, MemorySegment _x6, MemorySegment _x7) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2, _x3, _x4, _x5, _x6, _x7); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout WriteIsochPipeAsync$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("WriteIsochPipeAsync")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*WriteIsochPipeAsync)(void *, UInt8, void *, UInt64, UInt32, IOUSBIsocFrame *, IOAsyncCallback1, void *) + * } + */ + public static final AddressLayout WriteIsochPipeAsync$layout() { + return WriteIsochPipeAsync$LAYOUT; + } + + private static final long WriteIsochPipeAsync$OFFSET = $LAYOUT.byteOffset(groupElement("WriteIsochPipeAsync")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*WriteIsochPipeAsync)(void *, UInt8, void *, UInt64, UInt32, IOUSBIsocFrame *, IOAsyncCallback1, void *) + * } + */ + public static final long WriteIsochPipeAsync$offset() { + return WriteIsochPipeAsync$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*WriteIsochPipeAsync)(void *, UInt8, void *, UInt64, UInt32, IOUSBIsocFrame *, IOAsyncCallback1, void *) + * } + */ + public static MemorySegment WriteIsochPipeAsync(MemorySegment struct) { + return struct.get(WriteIsochPipeAsync$LAYOUT, WriteIsochPipeAsync$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*WriteIsochPipeAsync)(void *, UInt8, void *, UInt64, UInt32, IOUSBIsocFrame *, IOAsyncCallback1, void *) + * } + */ + public static void WriteIsochPipeAsync(MemorySegment struct, MemorySegment fieldValue) { + struct.set(WriteIsochPipeAsync$LAYOUT, WriteIsochPipeAsync$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*ControlRequestTO)(void *, UInt8, IOUSBDevRequestTO *) + * } + */ + public final static class ControlRequestTO { + + private ControlRequestTO() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1, MemorySegment _x2) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout ControlRequestTO$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("ControlRequestTO")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*ControlRequestTO)(void *, UInt8, IOUSBDevRequestTO *) + * } + */ + public static final AddressLayout ControlRequestTO$layout() { + return ControlRequestTO$LAYOUT; + } + + private static final long ControlRequestTO$OFFSET = $LAYOUT.byteOffset(groupElement("ControlRequestTO")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*ControlRequestTO)(void *, UInt8, IOUSBDevRequestTO *) + * } + */ + public static final long ControlRequestTO$offset() { + return ControlRequestTO$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*ControlRequestTO)(void *, UInt8, IOUSBDevRequestTO *) + * } + */ + public static MemorySegment ControlRequestTO(MemorySegment struct) { + return struct.get(ControlRequestTO$LAYOUT, ControlRequestTO$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*ControlRequestTO)(void *, UInt8, IOUSBDevRequestTO *) + * } + */ + public static void ControlRequestTO(MemorySegment struct, MemorySegment fieldValue) { + struct.set(ControlRequestTO$LAYOUT, ControlRequestTO$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*ControlRequestAsyncTO)(void *, UInt8, IOUSBDevRequestTO *, IOAsyncCallback1, void *) + * } + */ + public final static class ControlRequestAsyncTO { + + private ControlRequestAsyncTO() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1, MemorySegment _x2, MemorySegment _x3, MemorySegment _x4) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2, _x3, _x4); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout ControlRequestAsyncTO$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("ControlRequestAsyncTO")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*ControlRequestAsyncTO)(void *, UInt8, IOUSBDevRequestTO *, IOAsyncCallback1, void *) + * } + */ + public static final AddressLayout ControlRequestAsyncTO$layout() { + return ControlRequestAsyncTO$LAYOUT; + } + + private static final long ControlRequestAsyncTO$OFFSET = $LAYOUT.byteOffset(groupElement("ControlRequestAsyncTO")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*ControlRequestAsyncTO)(void *, UInt8, IOUSBDevRequestTO *, IOAsyncCallback1, void *) + * } + */ + public static final long ControlRequestAsyncTO$offset() { + return ControlRequestAsyncTO$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*ControlRequestAsyncTO)(void *, UInt8, IOUSBDevRequestTO *, IOAsyncCallback1, void *) + * } + */ + public static MemorySegment ControlRequestAsyncTO(MemorySegment struct) { + return struct.get(ControlRequestAsyncTO$LAYOUT, ControlRequestAsyncTO$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*ControlRequestAsyncTO)(void *, UInt8, IOUSBDevRequestTO *, IOAsyncCallback1, void *) + * } + */ + public static void ControlRequestAsyncTO(MemorySegment struct, MemorySegment fieldValue) { + struct.set(ControlRequestAsyncTO$LAYOUT, ControlRequestAsyncTO$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*ReadPipeTO)(void *, UInt8, void *, UInt32 *, UInt32, UInt32) + * } + */ + public final static class ReadPipeTO { + + private ReadPipeTO() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_INT, + IOKit.C_INT + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1, MemorySegment _x2, MemorySegment _x3, int _x4, int _x5) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2, _x3, _x4, _x5); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout ReadPipeTO$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("ReadPipeTO")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*ReadPipeTO)(void *, UInt8, void *, UInt32 *, UInt32, UInt32) + * } + */ + public static final AddressLayout ReadPipeTO$layout() { + return ReadPipeTO$LAYOUT; + } + + private static final long ReadPipeTO$OFFSET = $LAYOUT.byteOffset(groupElement("ReadPipeTO")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*ReadPipeTO)(void *, UInt8, void *, UInt32 *, UInt32, UInt32) + * } + */ + public static final long ReadPipeTO$offset() { + return ReadPipeTO$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*ReadPipeTO)(void *, UInt8, void *, UInt32 *, UInt32, UInt32) + * } + */ + public static MemorySegment ReadPipeTO(MemorySegment struct) { + return struct.get(ReadPipeTO$LAYOUT, ReadPipeTO$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*ReadPipeTO)(void *, UInt8, void *, UInt32 *, UInt32, UInt32) + * } + */ + public static void ReadPipeTO(MemorySegment struct, MemorySegment fieldValue) { + struct.set(ReadPipeTO$LAYOUT, ReadPipeTO$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*WritePipeTO)(void *, UInt8, void *, UInt32, UInt32, UInt32) + * } + */ + public final static class WritePipeTO { + + private WritePipeTO() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR, + IOKit.C_POINTER, + IOKit.C_INT, + IOKit.C_INT, + IOKit.C_INT + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1, MemorySegment _x2, int _x3, int _x4, int _x5) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2, _x3, _x4, _x5); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout WritePipeTO$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("WritePipeTO")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*WritePipeTO)(void *, UInt8, void *, UInt32, UInt32, UInt32) + * } + */ + public static final AddressLayout WritePipeTO$layout() { + return WritePipeTO$LAYOUT; + } + + private static final long WritePipeTO$OFFSET = $LAYOUT.byteOffset(groupElement("WritePipeTO")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*WritePipeTO)(void *, UInt8, void *, UInt32, UInt32, UInt32) + * } + */ + public static final long WritePipeTO$offset() { + return WritePipeTO$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*WritePipeTO)(void *, UInt8, void *, UInt32, UInt32, UInt32) + * } + */ + public static MemorySegment WritePipeTO(MemorySegment struct) { + return struct.get(WritePipeTO$LAYOUT, WritePipeTO$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*WritePipeTO)(void *, UInt8, void *, UInt32, UInt32, UInt32) + * } + */ + public static void WritePipeTO(MemorySegment struct, MemorySegment fieldValue) { + struct.set(WritePipeTO$LAYOUT, WritePipeTO$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*ReadPipeAsyncTO)(void *, UInt8, void *, UInt32, UInt32, UInt32, IOAsyncCallback1, void *) + * } + */ + public final static class ReadPipeAsyncTO { + + private ReadPipeAsyncTO() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR, + IOKit.C_POINTER, + IOKit.C_INT, + IOKit.C_INT, + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1, MemorySegment _x2, int _x3, int _x4, int _x5, MemorySegment _x6, MemorySegment _x7) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2, _x3, _x4, _x5, _x6, _x7); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout ReadPipeAsyncTO$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("ReadPipeAsyncTO")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*ReadPipeAsyncTO)(void *, UInt8, void *, UInt32, UInt32, UInt32, IOAsyncCallback1, void *) + * } + */ + public static final AddressLayout ReadPipeAsyncTO$layout() { + return ReadPipeAsyncTO$LAYOUT; + } + + private static final long ReadPipeAsyncTO$OFFSET = $LAYOUT.byteOffset(groupElement("ReadPipeAsyncTO")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*ReadPipeAsyncTO)(void *, UInt8, void *, UInt32, UInt32, UInt32, IOAsyncCallback1, void *) + * } + */ + public static final long ReadPipeAsyncTO$offset() { + return ReadPipeAsyncTO$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*ReadPipeAsyncTO)(void *, UInt8, void *, UInt32, UInt32, UInt32, IOAsyncCallback1, void *) + * } + */ + public static MemorySegment ReadPipeAsyncTO(MemorySegment struct) { + return struct.get(ReadPipeAsyncTO$LAYOUT, ReadPipeAsyncTO$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*ReadPipeAsyncTO)(void *, UInt8, void *, UInt32, UInt32, UInt32, IOAsyncCallback1, void *) + * } + */ + public static void ReadPipeAsyncTO(MemorySegment struct, MemorySegment fieldValue) { + struct.set(ReadPipeAsyncTO$LAYOUT, ReadPipeAsyncTO$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*WritePipeAsyncTO)(void *, UInt8, void *, UInt32, UInt32, UInt32, IOAsyncCallback1, void *) + * } + */ + public final static class WritePipeAsyncTO { + + private WritePipeAsyncTO() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR, + IOKit.C_POINTER, + IOKit.C_INT, + IOKit.C_INT, + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1, MemorySegment _x2, int _x3, int _x4, int _x5, MemorySegment _x6, MemorySegment _x7) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2, _x3, _x4, _x5, _x6, _x7); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout WritePipeAsyncTO$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("WritePipeAsyncTO")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*WritePipeAsyncTO)(void *, UInt8, void *, UInt32, UInt32, UInt32, IOAsyncCallback1, void *) + * } + */ + public static final AddressLayout WritePipeAsyncTO$layout() { + return WritePipeAsyncTO$LAYOUT; + } + + private static final long WritePipeAsyncTO$OFFSET = $LAYOUT.byteOffset(groupElement("WritePipeAsyncTO")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*WritePipeAsyncTO)(void *, UInt8, void *, UInt32, UInt32, UInt32, IOAsyncCallback1, void *) + * } + */ + public static final long WritePipeAsyncTO$offset() { + return WritePipeAsyncTO$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*WritePipeAsyncTO)(void *, UInt8, void *, UInt32, UInt32, UInt32, IOAsyncCallback1, void *) + * } + */ + public static MemorySegment WritePipeAsyncTO(MemorySegment struct) { + return struct.get(WritePipeAsyncTO$LAYOUT, WritePipeAsyncTO$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*WritePipeAsyncTO)(void *, UInt8, void *, UInt32, UInt32, UInt32, IOAsyncCallback1, void *) + * } + */ + public static void WritePipeAsyncTO(MemorySegment struct, MemorySegment fieldValue) { + struct.set(WritePipeAsyncTO$LAYOUT, WritePipeAsyncTO$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*USBInterfaceGetStringIndex)(void *, UInt8 *) + * } + */ + public final static class USBInterfaceGetStringIndex { + + private USBInterfaceGetStringIndex() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout USBInterfaceGetStringIndex$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("USBInterfaceGetStringIndex")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*USBInterfaceGetStringIndex)(void *, UInt8 *) + * } + */ + public static final AddressLayout USBInterfaceGetStringIndex$layout() { + return USBInterfaceGetStringIndex$LAYOUT; + } + + private static final long USBInterfaceGetStringIndex$OFFSET = $LAYOUT.byteOffset(groupElement("USBInterfaceGetStringIndex")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*USBInterfaceGetStringIndex)(void *, UInt8 *) + * } + */ + public static final long USBInterfaceGetStringIndex$offset() { + return USBInterfaceGetStringIndex$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*USBInterfaceGetStringIndex)(void *, UInt8 *) + * } + */ + public static MemorySegment USBInterfaceGetStringIndex(MemorySegment struct) { + return struct.get(USBInterfaceGetStringIndex$LAYOUT, USBInterfaceGetStringIndex$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*USBInterfaceGetStringIndex)(void *, UInt8 *) + * } + */ + public static void USBInterfaceGetStringIndex(MemorySegment struct, MemorySegment fieldValue) { + struct.set(USBInterfaceGetStringIndex$LAYOUT, USBInterfaceGetStringIndex$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*USBInterfaceOpenSeize)(void *) + * } + */ + public final static class USBInterfaceOpenSeize { + + private USBInterfaceOpenSeize() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout USBInterfaceOpenSeize$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("USBInterfaceOpenSeize")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*USBInterfaceOpenSeize)(void *) + * } + */ + public static final AddressLayout USBInterfaceOpenSeize$layout() { + return USBInterfaceOpenSeize$LAYOUT; + } + + private static final long USBInterfaceOpenSeize$OFFSET = $LAYOUT.byteOffset(groupElement("USBInterfaceOpenSeize")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*USBInterfaceOpenSeize)(void *) + * } + */ + public static final long USBInterfaceOpenSeize$offset() { + return USBInterfaceOpenSeize$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*USBInterfaceOpenSeize)(void *) + * } + */ + public static MemorySegment USBInterfaceOpenSeize(MemorySegment struct) { + return struct.get(USBInterfaceOpenSeize$LAYOUT, USBInterfaceOpenSeize$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*USBInterfaceOpenSeize)(void *) + * } + */ + public static void USBInterfaceOpenSeize(MemorySegment struct, MemorySegment fieldValue) { + struct.set(USBInterfaceOpenSeize$LAYOUT, USBInterfaceOpenSeize$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*ClearPipeStallBothEnds)(void *, UInt8) + * } + */ + public final static class ClearPipeStallBothEnds { + + private ClearPipeStallBothEnds() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout ClearPipeStallBothEnds$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("ClearPipeStallBothEnds")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*ClearPipeStallBothEnds)(void *, UInt8) + * } + */ + public static final AddressLayout ClearPipeStallBothEnds$layout() { + return ClearPipeStallBothEnds$LAYOUT; + } + + private static final long ClearPipeStallBothEnds$OFFSET = $LAYOUT.byteOffset(groupElement("ClearPipeStallBothEnds")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*ClearPipeStallBothEnds)(void *, UInt8) + * } + */ + public static final long ClearPipeStallBothEnds$offset() { + return ClearPipeStallBothEnds$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*ClearPipeStallBothEnds)(void *, UInt8) + * } + */ + public static MemorySegment ClearPipeStallBothEnds(MemorySegment struct) { + return struct.get(ClearPipeStallBothEnds$LAYOUT, ClearPipeStallBothEnds$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*ClearPipeStallBothEnds)(void *, UInt8) + * } + */ + public static void ClearPipeStallBothEnds(MemorySegment struct, MemorySegment fieldValue) { + struct.set(ClearPipeStallBothEnds$LAYOUT, ClearPipeStallBothEnds$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*SetPipePolicy)(void *, UInt8, UInt16, UInt8) + * } + */ + public final static class SetPipePolicy { + + private SetPipePolicy() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR, + IOKit.C_SHORT, + IOKit.C_CHAR + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1, short _x2, byte _x3) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2, _x3); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout SetPipePolicy$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("SetPipePolicy")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*SetPipePolicy)(void *, UInt8, UInt16, UInt8) + * } + */ + public static final AddressLayout SetPipePolicy$layout() { + return SetPipePolicy$LAYOUT; + } + + private static final long SetPipePolicy$OFFSET = $LAYOUT.byteOffset(groupElement("SetPipePolicy")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*SetPipePolicy)(void *, UInt8, UInt16, UInt8) + * } + */ + public static final long SetPipePolicy$offset() { + return SetPipePolicy$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*SetPipePolicy)(void *, UInt8, UInt16, UInt8) + * } + */ + public static MemorySegment SetPipePolicy(MemorySegment struct) { + return struct.get(SetPipePolicy$LAYOUT, SetPipePolicy$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*SetPipePolicy)(void *, UInt8, UInt16, UInt8) + * } + */ + public static void SetPipePolicy(MemorySegment struct, MemorySegment fieldValue) { + struct.set(SetPipePolicy$LAYOUT, SetPipePolicy$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetBandwidthAvailable)(void *, UInt32 *) + * } + */ + public final static class GetBandwidthAvailable { + + private GetBandwidthAvailable() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetBandwidthAvailable$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetBandwidthAvailable")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetBandwidthAvailable)(void *, UInt32 *) + * } + */ + public static final AddressLayout GetBandwidthAvailable$layout() { + return GetBandwidthAvailable$LAYOUT; + } + + private static final long GetBandwidthAvailable$OFFSET = $LAYOUT.byteOffset(groupElement("GetBandwidthAvailable")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetBandwidthAvailable)(void *, UInt32 *) + * } + */ + public static final long GetBandwidthAvailable$offset() { + return GetBandwidthAvailable$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetBandwidthAvailable)(void *, UInt32 *) + * } + */ + public static MemorySegment GetBandwidthAvailable(MemorySegment struct) { + return struct.get(GetBandwidthAvailable$LAYOUT, GetBandwidthAvailable$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetBandwidthAvailable)(void *, UInt32 *) + * } + */ + public static void GetBandwidthAvailable(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetBandwidthAvailable$LAYOUT, GetBandwidthAvailable$OFFSET, fieldValue); + } + + /** + * {@snippet lang=c : + * IOReturn (*GetEndpointProperties)(void *, UInt8, UInt8, UInt8, UInt8 *, UInt16 *, UInt8 *) + * } + */ + public final static class GetEndpointProperties { + + private GetEndpointProperties() { + // Should not be called directly + } + + /** + */ + + private static final FunctionDescriptor $DESC = FunctionDescriptor.of( + IOKit.C_INT, + IOKit.C_POINTER, + IOKit.C_CHAR, + IOKit.C_CHAR, + IOKit.C_CHAR, + IOKit.C_POINTER, + IOKit.C_POINTER, + IOKit.C_POINTER + ); + + /** + * The descriptor of this function pointer + */ + public static FunctionDescriptor descriptor() { + return $DESC; + } + + + private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); + + /** + * Invoke the upcall stub {@code funcPtr}, with given parameters + */ + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, byte _x1, byte _x2, byte _x3, MemorySegment _x4, MemorySegment _x5, MemorySegment _x6) { + try { + return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2, _x3, _x4, _x5, _x6); + } catch (Error | RuntimeException ex) { + throw ex; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + private static final AddressLayout GetEndpointProperties$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("GetEndpointProperties")); + + /** + * Layout for field: + * {@snippet lang=c : + * IOReturn (*GetEndpointProperties)(void *, UInt8, UInt8, UInt8, UInt8 *, UInt16 *, UInt8 *) + * } + */ + public static final AddressLayout GetEndpointProperties$layout() { + return GetEndpointProperties$LAYOUT; + } + + private static final long GetEndpointProperties$OFFSET = $LAYOUT.byteOffset(groupElement("GetEndpointProperties")); + + /** + * Offset for field: + * {@snippet lang=c : + * IOReturn (*GetEndpointProperties)(void *, UInt8, UInt8, UInt8, UInt8 *, UInt16 *, UInt8 *) + * } + */ + public static final long GetEndpointProperties$offset() { + return GetEndpointProperties$OFFSET; + } + + /** + * Getter for field: + * {@snippet lang=c : + * IOReturn (*GetEndpointProperties)(void *, UInt8, UInt8, UInt8, UInt8 *, UInt16 *, UInt8 *) + * } + */ + public static MemorySegment GetEndpointProperties(MemorySegment struct) { + return struct.get(GetEndpointProperties$LAYOUT, GetEndpointProperties$OFFSET); + } + + /** + * Setter for field: + * {@snippet lang=c : + * IOReturn (*GetEndpointProperties)(void *, UInt8, UInt8, UInt8, UInt8 *, UInt16 *, UInt8 *) + * } + */ + public static void GetEndpointProperties(MemorySegment struct, MemorySegment fieldValue) { + struct.set(GetEndpointProperties$LAYOUT, GetEndpointProperties$OFFSET, fieldValue); + } + + /** + * Obtains a slice of {@code arrayParam} which selects the array element at {@code index}. + * The returned segment has address {@code arrayParam.address() + index * layout().byteSize()} + */ + public static MemorySegment asSlice(MemorySegment array, long index) { + return array.asSlice(layout().byteSize() * index); + } + + /** + * The size (in bytes) of this struct + */ + public static long sizeof() { return layout().byteSize(); } + + /** + * Allocate a segment of size {@code layout().byteSize()} using {@code allocator} + */ + public static MemorySegment allocate(SegmentAllocator allocator) { + return allocator.allocate(layout()); + } + + /** + * Allocate an array of size {@code elementCount} using {@code allocator}. + * The returned segment has size {@code elementCount * layout().byteSize()}. + */ + public static MemorySegment allocateArray(long elementCount, SegmentAllocator allocator) { + return allocator.allocate(MemoryLayout.sequenceLayout(elementCount, layout())); + } + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, Arena arena, Consumer cleanup) { + return reinterpret(addr, 1, arena, cleanup); + } + + /** + * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). + * The returned segment has size {@code elementCount * layout().byteSize()} + */ + public static MemorySegment reinterpret(MemorySegment addr, long elementCount, Arena arena, Consumer cleanup) { + return addr.reinterpret(layout().byteSize() * elementCount, arena, cleanup); + } +} + diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBInterfaceStruct942.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBInterfaceStruct942.java deleted file mode 100644 index 26284e6f..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/IOUSBInterfaceStruct942.java +++ /dev/null @@ -1,3596 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.macos.gen.iokit; - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.VarHandle; -public class IOUSBInterfaceStruct942 { - - static final GroupLayout $struct$LAYOUT = MemoryLayout.structLayout( - Constants$root.C_POINTER$LAYOUT.withName("_reserved"), - Constants$root.C_POINTER$LAYOUT.withName("QueryInterface"), - Constants$root.C_POINTER$LAYOUT.withName("AddRef"), - Constants$root.C_POINTER$LAYOUT.withName("Release"), - Constants$root.C_POINTER$LAYOUT.withName("CreateInterfaceAsyncEventSource"), - Constants$root.C_POINTER$LAYOUT.withName("GetInterfaceAsyncEventSource"), - Constants$root.C_POINTER$LAYOUT.withName("CreateInterfaceAsyncPort"), - Constants$root.C_POINTER$LAYOUT.withName("GetInterfaceAsyncPort"), - Constants$root.C_POINTER$LAYOUT.withName("USBInterfaceOpen"), - Constants$root.C_POINTER$LAYOUT.withName("USBInterfaceClose"), - Constants$root.C_POINTER$LAYOUT.withName("GetInterfaceClass"), - Constants$root.C_POINTER$LAYOUT.withName("GetInterfaceSubClass"), - Constants$root.C_POINTER$LAYOUT.withName("GetInterfaceProtocol"), - Constants$root.C_POINTER$LAYOUT.withName("GetDeviceVendor"), - Constants$root.C_POINTER$LAYOUT.withName("GetDeviceProduct"), - Constants$root.C_POINTER$LAYOUT.withName("GetDeviceReleaseNumber"), - Constants$root.C_POINTER$LAYOUT.withName("GetConfigurationValue"), - Constants$root.C_POINTER$LAYOUT.withName("GetInterfaceNumber"), - Constants$root.C_POINTER$LAYOUT.withName("GetAlternateSetting"), - Constants$root.C_POINTER$LAYOUT.withName("GetNumEndpoints"), - Constants$root.C_POINTER$LAYOUT.withName("GetLocationID"), - Constants$root.C_POINTER$LAYOUT.withName("GetDevice"), - Constants$root.C_POINTER$LAYOUT.withName("SetAlternateInterface"), - Constants$root.C_POINTER$LAYOUT.withName("GetBusFrameNumber"), - Constants$root.C_POINTER$LAYOUT.withName("ControlRequest"), - Constants$root.C_POINTER$LAYOUT.withName("ControlRequestAsync"), - Constants$root.C_POINTER$LAYOUT.withName("GetPipeProperties"), - Constants$root.C_POINTER$LAYOUT.withName("GetPipeStatus"), - Constants$root.C_POINTER$LAYOUT.withName("AbortPipe"), - Constants$root.C_POINTER$LAYOUT.withName("ResetPipe"), - Constants$root.C_POINTER$LAYOUT.withName("ClearPipeStall"), - Constants$root.C_POINTER$LAYOUT.withName("ReadPipe"), - Constants$root.C_POINTER$LAYOUT.withName("WritePipe"), - Constants$root.C_POINTER$LAYOUT.withName("ReadPipeAsync"), - Constants$root.C_POINTER$LAYOUT.withName("WritePipeAsync"), - Constants$root.C_POINTER$LAYOUT.withName("ReadIsochPipeAsync"), - Constants$root.C_POINTER$LAYOUT.withName("WriteIsochPipeAsync"), - Constants$root.C_POINTER$LAYOUT.withName("ControlRequestTO"), - Constants$root.C_POINTER$LAYOUT.withName("ControlRequestAsyncTO"), - Constants$root.C_POINTER$LAYOUT.withName("ReadPipeTO"), - Constants$root.C_POINTER$LAYOUT.withName("WritePipeTO"), - Constants$root.C_POINTER$LAYOUT.withName("ReadPipeAsyncTO"), - Constants$root.C_POINTER$LAYOUT.withName("WritePipeAsyncTO"), - Constants$root.C_POINTER$LAYOUT.withName("USBInterfaceGetStringIndex"), - Constants$root.C_POINTER$LAYOUT.withName("USBInterfaceOpenSeize"), - Constants$root.C_POINTER$LAYOUT.withName("ClearPipeStallBothEnds"), - Constants$root.C_POINTER$LAYOUT.withName("SetPipePolicy"), - Constants$root.C_POINTER$LAYOUT.withName("GetBandwidthAvailable"), - Constants$root.C_POINTER$LAYOUT.withName("GetEndpointProperties"), - Constants$root.C_POINTER$LAYOUT.withName("LowLatencyReadIsochPipeAsync"), - Constants$root.C_POINTER$LAYOUT.withName("LowLatencyWriteIsochPipeAsync"), - Constants$root.C_POINTER$LAYOUT.withName("LowLatencyCreateBuffer"), - Constants$root.C_POINTER$LAYOUT.withName("LowLatencyDestroyBuffer"), - Constants$root.C_POINTER$LAYOUT.withName("GetBusMicroFrameNumber"), - Constants$root.C_POINTER$LAYOUT.withName("GetFrameListTime"), - Constants$root.C_POINTER$LAYOUT.withName("GetIOUSBLibVersion"), - Constants$root.C_POINTER$LAYOUT.withName("FindNextAssociatedDescriptor"), - Constants$root.C_POINTER$LAYOUT.withName("FindNextAltInterface"), - Constants$root.C_POINTER$LAYOUT.withName("GetBusFrameNumberWithTime"), - Constants$root.C_POINTER$LAYOUT.withName("GetPipePropertiesV2"), - Constants$root.C_POINTER$LAYOUT.withName("GetPipePropertiesV3"), - Constants$root.C_POINTER$LAYOUT.withName("GetEndpointPropertiesV3"), - Constants$root.C_POINTER$LAYOUT.withName("SupportsStreams"), - Constants$root.C_POINTER$LAYOUT.withName("CreateStreams"), - Constants$root.C_POINTER$LAYOUT.withName("GetConfiguredStreams"), - Constants$root.C_POINTER$LAYOUT.withName("ReadStreamsPipeTO"), - Constants$root.C_POINTER$LAYOUT.withName("WriteStreamsPipeTO"), - Constants$root.C_POINTER$LAYOUT.withName("ReadStreamsPipeAsyncTO"), - Constants$root.C_POINTER$LAYOUT.withName("WriteStreamsPipeAsyncTO"), - Constants$root.C_POINTER$LAYOUT.withName("AbortStreamsPipe"), - Constants$root.C_POINTER$LAYOUT.withName("RegisterForNotification"), - Constants$root.C_POINTER$LAYOUT.withName("UnregisterNotification"), - Constants$root.C_POINTER$LAYOUT.withName("AcknowledgeNotification"), - Constants$root.C_POINTER$LAYOUT.withName("RegisterDriver"), - Constants$root.C_POINTER$LAYOUT.withName("SetDeviceIdlePolicy"), - Constants$root.C_POINTER$LAYOUT.withName("SetPipeIdlePolicy"), - Constants$root.C_POINTER$LAYOUT.withName("GetInterfaceAsyncNotificationPort") - ).withName("IOUSBInterfaceStruct942"); - public static MemoryLayout $LAYOUT() { - return IOUSBInterfaceStruct942.$struct$LAYOUT; - } - static final VarHandle _reserved$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("_reserved")); - public static VarHandle _reserved$VH() { - return IOUSBInterfaceStruct942._reserved$VH; - } - public static MemoryAddress _reserved$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942._reserved$VH.get(seg); - } - public static void _reserved$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942._reserved$VH.set(seg, x); - } - public static MemoryAddress _reserved$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942._reserved$VH.get(seg.asSlice(index*sizeof())); - } - public static void _reserved$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942._reserved$VH.set(seg.asSlice(index*sizeof()), x); - } - static final FunctionDescriptor QueryInterface$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - MemoryLayout.structLayout( - Constants$root.C_CHAR$LAYOUT.withName("byte0"), - Constants$root.C_CHAR$LAYOUT.withName("byte1"), - Constants$root.C_CHAR$LAYOUT.withName("byte2"), - Constants$root.C_CHAR$LAYOUT.withName("byte3"), - Constants$root.C_CHAR$LAYOUT.withName("byte4"), - Constants$root.C_CHAR$LAYOUT.withName("byte5"), - Constants$root.C_CHAR$LAYOUT.withName("byte6"), - Constants$root.C_CHAR$LAYOUT.withName("byte7"), - Constants$root.C_CHAR$LAYOUT.withName("byte8"), - Constants$root.C_CHAR$LAYOUT.withName("byte9"), - Constants$root.C_CHAR$LAYOUT.withName("byte10"), - Constants$root.C_CHAR$LAYOUT.withName("byte11"), - Constants$root.C_CHAR$LAYOUT.withName("byte12"), - Constants$root.C_CHAR$LAYOUT.withName("byte13"), - Constants$root.C_CHAR$LAYOUT.withName("byte14"), - Constants$root.C_CHAR$LAYOUT.withName("byte15") - ), - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle QueryInterface$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.QueryInterface$FUNC - ); - public interface QueryInterface { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemorySegment _x1, java.lang.foreign.MemoryAddress _x2); - static MemorySegment allocate(QueryInterface fi, MemorySession session) { - return RuntimeHelper.upcallStub(QueryInterface.class, fi, IOUSBInterfaceStruct942.QueryInterface$FUNC, session); - } - static QueryInterface ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemorySegment __x1, java.lang.foreign.MemoryAddress __x2) -> { - try { - return (int)IOUSBInterfaceStruct942.QueryInterface$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle QueryInterface$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("QueryInterface")); - public static VarHandle QueryInterface$VH() { - return IOUSBInterfaceStruct942.QueryInterface$VH; - } - public static MemoryAddress QueryInterface$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.QueryInterface$VH.get(seg); - } - public static void QueryInterface$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.QueryInterface$VH.set(seg, x); - } - public static MemoryAddress QueryInterface$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.QueryInterface$VH.get(seg.asSlice(index*sizeof())); - } - public static void QueryInterface$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.QueryInterface$VH.set(seg.asSlice(index*sizeof()), x); - } - public static QueryInterface QueryInterface (MemorySegment segment, MemorySession session) { - return QueryInterface.ofAddress(QueryInterface$get(segment), session); - } - static final FunctionDescriptor AddRef$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle AddRef$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.AddRef$FUNC - ); - public interface AddRef { - - int apply(java.lang.foreign.MemoryAddress _x0); - static MemorySegment allocate(AddRef fi, MemorySession session) { - return RuntimeHelper.upcallStub(AddRef.class, fi, IOUSBInterfaceStruct942.AddRef$FUNC, session); - } - static AddRef ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0) -> { - try { - return (int)IOUSBInterfaceStruct942.AddRef$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle AddRef$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("AddRef")); - public static VarHandle AddRef$VH() { - return IOUSBInterfaceStruct942.AddRef$VH; - } - public static MemoryAddress AddRef$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.AddRef$VH.get(seg); - } - public static void AddRef$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.AddRef$VH.set(seg, x); - } - public static MemoryAddress AddRef$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.AddRef$VH.get(seg.asSlice(index*sizeof())); - } - public static void AddRef$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.AddRef$VH.set(seg.asSlice(index*sizeof()), x); - } - public static AddRef AddRef (MemorySegment segment, MemorySession session) { - return AddRef.ofAddress(AddRef$get(segment), session); - } - static final FunctionDescriptor Release$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle Release$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.Release$FUNC - ); - public interface Release { - - int apply(java.lang.foreign.MemoryAddress _x0); - static MemorySegment allocate(Release fi, MemorySession session) { - return RuntimeHelper.upcallStub(Release.class, fi, IOUSBInterfaceStruct942.Release$FUNC, session); - } - static Release ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0) -> { - try { - return (int)IOUSBInterfaceStruct942.Release$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle Release$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("Release")); - public static VarHandle Release$VH() { - return IOUSBInterfaceStruct942.Release$VH; - } - public static MemoryAddress Release$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.Release$VH.get(seg); - } - public static void Release$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.Release$VH.set(seg, x); - } - public static MemoryAddress Release$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.Release$VH.get(seg.asSlice(index*sizeof())); - } - public static void Release$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.Release$VH.set(seg.asSlice(index*sizeof()), x); - } - public static Release Release (MemorySegment segment, MemorySession session) { - return Release.ofAddress(Release$get(segment), session); - } - static final FunctionDescriptor CreateInterfaceAsyncEventSource$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle CreateInterfaceAsyncEventSource$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.CreateInterfaceAsyncEventSource$FUNC - ); - public interface CreateInterfaceAsyncEventSource { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(CreateInterfaceAsyncEventSource fi, MemorySession session) { - return RuntimeHelper.upcallStub(CreateInterfaceAsyncEventSource.class, fi, IOUSBInterfaceStruct942.CreateInterfaceAsyncEventSource$FUNC, session); - } - static CreateInterfaceAsyncEventSource ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.CreateInterfaceAsyncEventSource$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle CreateInterfaceAsyncEventSource$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("CreateInterfaceAsyncEventSource")); - public static VarHandle CreateInterfaceAsyncEventSource$VH() { - return IOUSBInterfaceStruct942.CreateInterfaceAsyncEventSource$VH; - } - public static MemoryAddress CreateInterfaceAsyncEventSource$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.CreateInterfaceAsyncEventSource$VH.get(seg); - } - public static void CreateInterfaceAsyncEventSource$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.CreateInterfaceAsyncEventSource$VH.set(seg, x); - } - public static MemoryAddress CreateInterfaceAsyncEventSource$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.CreateInterfaceAsyncEventSource$VH.get(seg.asSlice(index*sizeof())); - } - public static void CreateInterfaceAsyncEventSource$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.CreateInterfaceAsyncEventSource$VH.set(seg.asSlice(index*sizeof()), x); - } - public static CreateInterfaceAsyncEventSource CreateInterfaceAsyncEventSource (MemorySegment segment, MemorySession session) { - return CreateInterfaceAsyncEventSource.ofAddress(CreateInterfaceAsyncEventSource$get(segment), session); - } - static final FunctionDescriptor GetInterfaceAsyncEventSource$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetInterfaceAsyncEventSource$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetInterfaceAsyncEventSource$FUNC - ); - public interface GetInterfaceAsyncEventSource { - - java.lang.foreign.Addressable apply(java.lang.foreign.MemoryAddress _x0); - static MemorySegment allocate(GetInterfaceAsyncEventSource fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetInterfaceAsyncEventSource.class, fi, IOUSBInterfaceStruct942.GetInterfaceAsyncEventSource$FUNC, session); - } - static GetInterfaceAsyncEventSource ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0) -> { - try { - return (java.lang.foreign.Addressable)(java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetInterfaceAsyncEventSource$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetInterfaceAsyncEventSource$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetInterfaceAsyncEventSource")); - public static VarHandle GetInterfaceAsyncEventSource$VH() { - return IOUSBInterfaceStruct942.GetInterfaceAsyncEventSource$VH; - } - public static MemoryAddress GetInterfaceAsyncEventSource$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetInterfaceAsyncEventSource$VH.get(seg); - } - public static void GetInterfaceAsyncEventSource$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetInterfaceAsyncEventSource$VH.set(seg, x); - } - public static MemoryAddress GetInterfaceAsyncEventSource$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetInterfaceAsyncEventSource$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetInterfaceAsyncEventSource$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetInterfaceAsyncEventSource$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetInterfaceAsyncEventSource GetInterfaceAsyncEventSource (MemorySegment segment, MemorySession session) { - return GetInterfaceAsyncEventSource.ofAddress(GetInterfaceAsyncEventSource$get(segment), session); - } - static final FunctionDescriptor CreateInterfaceAsyncPort$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle CreateInterfaceAsyncPort$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.CreateInterfaceAsyncPort$FUNC - ); - public interface CreateInterfaceAsyncPort { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(CreateInterfaceAsyncPort fi, MemorySession session) { - return RuntimeHelper.upcallStub(CreateInterfaceAsyncPort.class, fi, IOUSBInterfaceStruct942.CreateInterfaceAsyncPort$FUNC, session); - } - static CreateInterfaceAsyncPort ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.CreateInterfaceAsyncPort$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle CreateInterfaceAsyncPort$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("CreateInterfaceAsyncPort")); - public static VarHandle CreateInterfaceAsyncPort$VH() { - return IOUSBInterfaceStruct942.CreateInterfaceAsyncPort$VH; - } - public static MemoryAddress CreateInterfaceAsyncPort$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.CreateInterfaceAsyncPort$VH.get(seg); - } - public static void CreateInterfaceAsyncPort$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.CreateInterfaceAsyncPort$VH.set(seg, x); - } - public static MemoryAddress CreateInterfaceAsyncPort$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.CreateInterfaceAsyncPort$VH.get(seg.asSlice(index*sizeof())); - } - public static void CreateInterfaceAsyncPort$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.CreateInterfaceAsyncPort$VH.set(seg.asSlice(index*sizeof()), x); - } - public static CreateInterfaceAsyncPort CreateInterfaceAsyncPort (MemorySegment segment, MemorySession session) { - return CreateInterfaceAsyncPort.ofAddress(CreateInterfaceAsyncPort$get(segment), session); - } - static final FunctionDescriptor GetInterfaceAsyncPort$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetInterfaceAsyncPort$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetInterfaceAsyncPort$FUNC - ); - public interface GetInterfaceAsyncPort { - - int apply(java.lang.foreign.MemoryAddress _x0); - static MemorySegment allocate(GetInterfaceAsyncPort fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetInterfaceAsyncPort.class, fi, IOUSBInterfaceStruct942.GetInterfaceAsyncPort$FUNC, session); - } - static GetInterfaceAsyncPort ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0) -> { - try { - return (int)IOUSBInterfaceStruct942.GetInterfaceAsyncPort$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetInterfaceAsyncPort$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetInterfaceAsyncPort")); - public static VarHandle GetInterfaceAsyncPort$VH() { - return IOUSBInterfaceStruct942.GetInterfaceAsyncPort$VH; - } - public static MemoryAddress GetInterfaceAsyncPort$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetInterfaceAsyncPort$VH.get(seg); - } - public static void GetInterfaceAsyncPort$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetInterfaceAsyncPort$VH.set(seg, x); - } - public static MemoryAddress GetInterfaceAsyncPort$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetInterfaceAsyncPort$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetInterfaceAsyncPort$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetInterfaceAsyncPort$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetInterfaceAsyncPort GetInterfaceAsyncPort (MemorySegment segment, MemorySession session) { - return GetInterfaceAsyncPort.ofAddress(GetInterfaceAsyncPort$get(segment), session); - } - static final FunctionDescriptor USBInterfaceOpen$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle USBInterfaceOpen$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.USBInterfaceOpen$FUNC - ); - public interface USBInterfaceOpen { - - int apply(java.lang.foreign.MemoryAddress _x0); - static MemorySegment allocate(USBInterfaceOpen fi, MemorySession session) { - return RuntimeHelper.upcallStub(USBInterfaceOpen.class, fi, IOUSBInterfaceStruct942.USBInterfaceOpen$FUNC, session); - } - static USBInterfaceOpen ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0) -> { - try { - return (int)IOUSBInterfaceStruct942.USBInterfaceOpen$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle USBInterfaceOpen$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("USBInterfaceOpen")); - public static VarHandle USBInterfaceOpen$VH() { - return IOUSBInterfaceStruct942.USBInterfaceOpen$VH; - } - public static MemoryAddress USBInterfaceOpen$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.USBInterfaceOpen$VH.get(seg); - } - public static void USBInterfaceOpen$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.USBInterfaceOpen$VH.set(seg, x); - } - public static MemoryAddress USBInterfaceOpen$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.USBInterfaceOpen$VH.get(seg.asSlice(index*sizeof())); - } - public static void USBInterfaceOpen$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.USBInterfaceOpen$VH.set(seg.asSlice(index*sizeof()), x); - } - public static USBInterfaceOpen USBInterfaceOpen (MemorySegment segment, MemorySession session) { - return USBInterfaceOpen.ofAddress(USBInterfaceOpen$get(segment), session); - } - static final FunctionDescriptor USBInterfaceClose$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle USBInterfaceClose$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.USBInterfaceClose$FUNC - ); - public interface USBInterfaceClose { - - int apply(java.lang.foreign.MemoryAddress _x0); - static MemorySegment allocate(USBInterfaceClose fi, MemorySession session) { - return RuntimeHelper.upcallStub(USBInterfaceClose.class, fi, IOUSBInterfaceStruct942.USBInterfaceClose$FUNC, session); - } - static USBInterfaceClose ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0) -> { - try { - return (int)IOUSBInterfaceStruct942.USBInterfaceClose$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle USBInterfaceClose$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("USBInterfaceClose")); - public static VarHandle USBInterfaceClose$VH() { - return IOUSBInterfaceStruct942.USBInterfaceClose$VH; - } - public static MemoryAddress USBInterfaceClose$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.USBInterfaceClose$VH.get(seg); - } - public static void USBInterfaceClose$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.USBInterfaceClose$VH.set(seg, x); - } - public static MemoryAddress USBInterfaceClose$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.USBInterfaceClose$VH.get(seg.asSlice(index*sizeof())); - } - public static void USBInterfaceClose$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.USBInterfaceClose$VH.set(seg.asSlice(index*sizeof()), x); - } - public static USBInterfaceClose USBInterfaceClose (MemorySegment segment, MemorySession session) { - return USBInterfaceClose.ofAddress(USBInterfaceClose$get(segment), session); - } - static final FunctionDescriptor GetInterfaceClass$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetInterfaceClass$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetInterfaceClass$FUNC - ); - public interface GetInterfaceClass { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetInterfaceClass fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetInterfaceClass.class, fi, IOUSBInterfaceStruct942.GetInterfaceClass$FUNC, session); - } - static GetInterfaceClass ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.GetInterfaceClass$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetInterfaceClass$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetInterfaceClass")); - public static VarHandle GetInterfaceClass$VH() { - return IOUSBInterfaceStruct942.GetInterfaceClass$VH; - } - public static MemoryAddress GetInterfaceClass$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetInterfaceClass$VH.get(seg); - } - public static void GetInterfaceClass$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetInterfaceClass$VH.set(seg, x); - } - public static MemoryAddress GetInterfaceClass$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetInterfaceClass$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetInterfaceClass$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetInterfaceClass$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetInterfaceClass GetInterfaceClass (MemorySegment segment, MemorySession session) { - return GetInterfaceClass.ofAddress(GetInterfaceClass$get(segment), session); - } - static final FunctionDescriptor GetInterfaceSubClass$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetInterfaceSubClass$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetInterfaceSubClass$FUNC - ); - public interface GetInterfaceSubClass { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetInterfaceSubClass fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetInterfaceSubClass.class, fi, IOUSBInterfaceStruct942.GetInterfaceSubClass$FUNC, session); - } - static GetInterfaceSubClass ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.GetInterfaceSubClass$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetInterfaceSubClass$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetInterfaceSubClass")); - public static VarHandle GetInterfaceSubClass$VH() { - return IOUSBInterfaceStruct942.GetInterfaceSubClass$VH; - } - public static MemoryAddress GetInterfaceSubClass$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetInterfaceSubClass$VH.get(seg); - } - public static void GetInterfaceSubClass$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetInterfaceSubClass$VH.set(seg, x); - } - public static MemoryAddress GetInterfaceSubClass$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetInterfaceSubClass$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetInterfaceSubClass$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetInterfaceSubClass$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetInterfaceSubClass GetInterfaceSubClass (MemorySegment segment, MemorySession session) { - return GetInterfaceSubClass.ofAddress(GetInterfaceSubClass$get(segment), session); - } - static final FunctionDescriptor GetInterfaceProtocol$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetInterfaceProtocol$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetInterfaceProtocol$FUNC - ); - public interface GetInterfaceProtocol { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetInterfaceProtocol fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetInterfaceProtocol.class, fi, IOUSBInterfaceStruct942.GetInterfaceProtocol$FUNC, session); - } - static GetInterfaceProtocol ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.GetInterfaceProtocol$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetInterfaceProtocol$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetInterfaceProtocol")); - public static VarHandle GetInterfaceProtocol$VH() { - return IOUSBInterfaceStruct942.GetInterfaceProtocol$VH; - } - public static MemoryAddress GetInterfaceProtocol$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetInterfaceProtocol$VH.get(seg); - } - public static void GetInterfaceProtocol$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetInterfaceProtocol$VH.set(seg, x); - } - public static MemoryAddress GetInterfaceProtocol$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetInterfaceProtocol$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetInterfaceProtocol$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetInterfaceProtocol$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetInterfaceProtocol GetInterfaceProtocol (MemorySegment segment, MemorySession session) { - return GetInterfaceProtocol.ofAddress(GetInterfaceProtocol$get(segment), session); - } - static final FunctionDescriptor GetDeviceVendor$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetDeviceVendor$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetDeviceVendor$FUNC - ); - public interface GetDeviceVendor { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetDeviceVendor fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetDeviceVendor.class, fi, IOUSBInterfaceStruct942.GetDeviceVendor$FUNC, session); - } - static GetDeviceVendor ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.GetDeviceVendor$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetDeviceVendor$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetDeviceVendor")); - public static VarHandle GetDeviceVendor$VH() { - return IOUSBInterfaceStruct942.GetDeviceVendor$VH; - } - public static MemoryAddress GetDeviceVendor$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetDeviceVendor$VH.get(seg); - } - public static void GetDeviceVendor$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetDeviceVendor$VH.set(seg, x); - } - public static MemoryAddress GetDeviceVendor$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetDeviceVendor$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetDeviceVendor$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetDeviceVendor$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetDeviceVendor GetDeviceVendor (MemorySegment segment, MemorySession session) { - return GetDeviceVendor.ofAddress(GetDeviceVendor$get(segment), session); - } - static final FunctionDescriptor GetDeviceProduct$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetDeviceProduct$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetDeviceProduct$FUNC - ); - public interface GetDeviceProduct { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetDeviceProduct fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetDeviceProduct.class, fi, IOUSBInterfaceStruct942.GetDeviceProduct$FUNC, session); - } - static GetDeviceProduct ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.GetDeviceProduct$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetDeviceProduct$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetDeviceProduct")); - public static VarHandle GetDeviceProduct$VH() { - return IOUSBInterfaceStruct942.GetDeviceProduct$VH; - } - public static MemoryAddress GetDeviceProduct$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetDeviceProduct$VH.get(seg); - } - public static void GetDeviceProduct$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetDeviceProduct$VH.set(seg, x); - } - public static MemoryAddress GetDeviceProduct$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetDeviceProduct$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetDeviceProduct$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetDeviceProduct$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetDeviceProduct GetDeviceProduct (MemorySegment segment, MemorySession session) { - return GetDeviceProduct.ofAddress(GetDeviceProduct$get(segment), session); - } - static final FunctionDescriptor GetDeviceReleaseNumber$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetDeviceReleaseNumber$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetDeviceReleaseNumber$FUNC - ); - public interface GetDeviceReleaseNumber { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetDeviceReleaseNumber fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetDeviceReleaseNumber.class, fi, IOUSBInterfaceStruct942.GetDeviceReleaseNumber$FUNC, session); - } - static GetDeviceReleaseNumber ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.GetDeviceReleaseNumber$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetDeviceReleaseNumber$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetDeviceReleaseNumber")); - public static VarHandle GetDeviceReleaseNumber$VH() { - return IOUSBInterfaceStruct942.GetDeviceReleaseNumber$VH; - } - public static MemoryAddress GetDeviceReleaseNumber$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetDeviceReleaseNumber$VH.get(seg); - } - public static void GetDeviceReleaseNumber$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetDeviceReleaseNumber$VH.set(seg, x); - } - public static MemoryAddress GetDeviceReleaseNumber$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetDeviceReleaseNumber$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetDeviceReleaseNumber$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetDeviceReleaseNumber$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetDeviceReleaseNumber GetDeviceReleaseNumber (MemorySegment segment, MemorySession session) { - return GetDeviceReleaseNumber.ofAddress(GetDeviceReleaseNumber$get(segment), session); - } - static final FunctionDescriptor GetConfigurationValue$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetConfigurationValue$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetConfigurationValue$FUNC - ); - public interface GetConfigurationValue { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetConfigurationValue fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetConfigurationValue.class, fi, IOUSBInterfaceStruct942.GetConfigurationValue$FUNC, session); - } - static GetConfigurationValue ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.GetConfigurationValue$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetConfigurationValue$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetConfigurationValue")); - public static VarHandle GetConfigurationValue$VH() { - return IOUSBInterfaceStruct942.GetConfigurationValue$VH; - } - public static MemoryAddress GetConfigurationValue$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetConfigurationValue$VH.get(seg); - } - public static void GetConfigurationValue$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetConfigurationValue$VH.set(seg, x); - } - public static MemoryAddress GetConfigurationValue$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetConfigurationValue$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetConfigurationValue$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetConfigurationValue$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetConfigurationValue GetConfigurationValue (MemorySegment segment, MemorySession session) { - return GetConfigurationValue.ofAddress(GetConfigurationValue$get(segment), session); - } - static final FunctionDescriptor GetInterfaceNumber$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetInterfaceNumber$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetInterfaceNumber$FUNC - ); - public interface GetInterfaceNumber { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetInterfaceNumber fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetInterfaceNumber.class, fi, IOUSBInterfaceStruct942.GetInterfaceNumber$FUNC, session); - } - static GetInterfaceNumber ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.GetInterfaceNumber$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetInterfaceNumber$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetInterfaceNumber")); - public static VarHandle GetInterfaceNumber$VH() { - return IOUSBInterfaceStruct942.GetInterfaceNumber$VH; - } - public static MemoryAddress GetInterfaceNumber$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetInterfaceNumber$VH.get(seg); - } - public static void GetInterfaceNumber$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetInterfaceNumber$VH.set(seg, x); - } - public static MemoryAddress GetInterfaceNumber$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetInterfaceNumber$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetInterfaceNumber$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetInterfaceNumber$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetInterfaceNumber GetInterfaceNumber (MemorySegment segment, MemorySession session) { - return GetInterfaceNumber.ofAddress(GetInterfaceNumber$get(segment), session); - } - static final FunctionDescriptor GetAlternateSetting$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetAlternateSetting$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetAlternateSetting$FUNC - ); - public interface GetAlternateSetting { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetAlternateSetting fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetAlternateSetting.class, fi, IOUSBInterfaceStruct942.GetAlternateSetting$FUNC, session); - } - static GetAlternateSetting ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.GetAlternateSetting$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetAlternateSetting$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetAlternateSetting")); - public static VarHandle GetAlternateSetting$VH() { - return IOUSBInterfaceStruct942.GetAlternateSetting$VH; - } - public static MemoryAddress GetAlternateSetting$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetAlternateSetting$VH.get(seg); - } - public static void GetAlternateSetting$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetAlternateSetting$VH.set(seg, x); - } - public static MemoryAddress GetAlternateSetting$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetAlternateSetting$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetAlternateSetting$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetAlternateSetting$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetAlternateSetting GetAlternateSetting (MemorySegment segment, MemorySession session) { - return GetAlternateSetting.ofAddress(GetAlternateSetting$get(segment), session); - } - static final FunctionDescriptor GetNumEndpoints$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetNumEndpoints$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetNumEndpoints$FUNC - ); - public interface GetNumEndpoints { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetNumEndpoints fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetNumEndpoints.class, fi, IOUSBInterfaceStruct942.GetNumEndpoints$FUNC, session); - } - static GetNumEndpoints ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.GetNumEndpoints$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetNumEndpoints$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetNumEndpoints")); - public static VarHandle GetNumEndpoints$VH() { - return IOUSBInterfaceStruct942.GetNumEndpoints$VH; - } - public static MemoryAddress GetNumEndpoints$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetNumEndpoints$VH.get(seg); - } - public static void GetNumEndpoints$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetNumEndpoints$VH.set(seg, x); - } - public static MemoryAddress GetNumEndpoints$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetNumEndpoints$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetNumEndpoints$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetNumEndpoints$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetNumEndpoints GetNumEndpoints (MemorySegment segment, MemorySession session) { - return GetNumEndpoints.ofAddress(GetNumEndpoints$get(segment), session); - } - static final FunctionDescriptor GetLocationID$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetLocationID$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetLocationID$FUNC - ); - public interface GetLocationID { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetLocationID fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetLocationID.class, fi, IOUSBInterfaceStruct942.GetLocationID$FUNC, session); - } - static GetLocationID ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.GetLocationID$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetLocationID$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetLocationID")); - public static VarHandle GetLocationID$VH() { - return IOUSBInterfaceStruct942.GetLocationID$VH; - } - public static MemoryAddress GetLocationID$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetLocationID$VH.get(seg); - } - public static void GetLocationID$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetLocationID$VH.set(seg, x); - } - public static MemoryAddress GetLocationID$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetLocationID$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetLocationID$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetLocationID$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetLocationID GetLocationID (MemorySegment segment, MemorySession session) { - return GetLocationID.ofAddress(GetLocationID$get(segment), session); - } - static final FunctionDescriptor GetDevice$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetDevice$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetDevice$FUNC - ); - public interface GetDevice { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetDevice fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetDevice.class, fi, IOUSBInterfaceStruct942.GetDevice$FUNC, session); - } - static GetDevice ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.GetDevice$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetDevice$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetDevice")); - public static VarHandle GetDevice$VH() { - return IOUSBInterfaceStruct942.GetDevice$VH; - } - public static MemoryAddress GetDevice$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetDevice$VH.get(seg); - } - public static void GetDevice$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetDevice$VH.set(seg, x); - } - public static MemoryAddress GetDevice$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetDevice$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetDevice$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetDevice$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetDevice GetDevice (MemorySegment segment, MemorySession session) { - return GetDevice.ofAddress(GetDevice$get(segment), session); - } - static final FunctionDescriptor SetAlternateInterface$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT - ); - static final MethodHandle SetAlternateInterface$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.SetAlternateInterface$FUNC - ); - public interface SetAlternateInterface { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1); - static MemorySegment allocate(SetAlternateInterface fi, MemorySession session) { - return RuntimeHelper.upcallStub(SetAlternateInterface.class, fi, IOUSBInterfaceStruct942.SetAlternateInterface$FUNC, session); - } - static SetAlternateInterface ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.SetAlternateInterface$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle SetAlternateInterface$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("SetAlternateInterface")); - public static VarHandle SetAlternateInterface$VH() { - return IOUSBInterfaceStruct942.SetAlternateInterface$VH; - } - public static MemoryAddress SetAlternateInterface$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.SetAlternateInterface$VH.get(seg); - } - public static void SetAlternateInterface$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.SetAlternateInterface$VH.set(seg, x); - } - public static MemoryAddress SetAlternateInterface$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.SetAlternateInterface$VH.get(seg.asSlice(index*sizeof())); - } - public static void SetAlternateInterface$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.SetAlternateInterface$VH.set(seg.asSlice(index*sizeof()), x); - } - public static SetAlternateInterface SetAlternateInterface (MemorySegment segment, MemorySession session) { - return SetAlternateInterface.ofAddress(SetAlternateInterface$get(segment), session); - } - static final FunctionDescriptor GetBusFrameNumber$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetBusFrameNumber$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetBusFrameNumber$FUNC - ); - public interface GetBusFrameNumber { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1, java.lang.foreign.MemoryAddress _x2); - static MemorySegment allocate(GetBusFrameNumber fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetBusFrameNumber.class, fi, IOUSBInterfaceStruct942.GetBusFrameNumber$FUNC, session); - } - static GetBusFrameNumber ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1, java.lang.foreign.MemoryAddress __x2) -> { - try { - return (int)IOUSBInterfaceStruct942.GetBusFrameNumber$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1, (java.lang.foreign.Addressable)__x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetBusFrameNumber$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetBusFrameNumber")); - public static VarHandle GetBusFrameNumber$VH() { - return IOUSBInterfaceStruct942.GetBusFrameNumber$VH; - } - public static MemoryAddress GetBusFrameNumber$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetBusFrameNumber$VH.get(seg); - } - public static void GetBusFrameNumber$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetBusFrameNumber$VH.set(seg, x); - } - public static MemoryAddress GetBusFrameNumber$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetBusFrameNumber$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetBusFrameNumber$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetBusFrameNumber$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetBusFrameNumber GetBusFrameNumber (MemorySegment segment, MemorySession session) { - return GetBusFrameNumber.ofAddress(GetBusFrameNumber$get(segment), session); - } - static final FunctionDescriptor ControlRequest$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle ControlRequest$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.ControlRequest$FUNC - ); - public interface ControlRequest { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, java.lang.foreign.MemoryAddress _x2); - static MemorySegment allocate(ControlRequest fi, MemorySession session) { - return RuntimeHelper.upcallStub(ControlRequest.class, fi, IOUSBInterfaceStruct942.ControlRequest$FUNC, session); - } - static ControlRequest ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, java.lang.foreign.MemoryAddress __x2) -> { - try { - return (int)IOUSBInterfaceStruct942.ControlRequest$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle ControlRequest$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("ControlRequest")); - public static VarHandle ControlRequest$VH() { - return IOUSBInterfaceStruct942.ControlRequest$VH; - } - public static MemoryAddress ControlRequest$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ControlRequest$VH.get(seg); - } - public static void ControlRequest$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.ControlRequest$VH.set(seg, x); - } - public static MemoryAddress ControlRequest$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ControlRequest$VH.get(seg.asSlice(index*sizeof())); - } - public static void ControlRequest$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.ControlRequest$VH.set(seg.asSlice(index*sizeof()), x); - } - public static ControlRequest ControlRequest (MemorySegment segment, MemorySession session) { - return ControlRequest.ofAddress(ControlRequest$get(segment), session); - } - static final FunctionDescriptor ControlRequestAsync$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle ControlRequestAsync$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.ControlRequestAsync$FUNC - ); - public interface ControlRequestAsync { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, java.lang.foreign.MemoryAddress _x2, java.lang.foreign.MemoryAddress _x3, java.lang.foreign.MemoryAddress _x4); - static MemorySegment allocate(ControlRequestAsync fi, MemorySession session) { - return RuntimeHelper.upcallStub(ControlRequestAsync.class, fi, IOUSBInterfaceStruct942.ControlRequestAsync$FUNC, session); - } - static ControlRequestAsync ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, java.lang.foreign.MemoryAddress __x2, java.lang.foreign.MemoryAddress __x3, java.lang.foreign.MemoryAddress __x4) -> { - try { - return (int)IOUSBInterfaceStruct942.ControlRequestAsync$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2, (java.lang.foreign.Addressable)__x3, (java.lang.foreign.Addressable)__x4); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle ControlRequestAsync$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("ControlRequestAsync")); - public static VarHandle ControlRequestAsync$VH() { - return IOUSBInterfaceStruct942.ControlRequestAsync$VH; - } - public static MemoryAddress ControlRequestAsync$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ControlRequestAsync$VH.get(seg); - } - public static void ControlRequestAsync$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.ControlRequestAsync$VH.set(seg, x); - } - public static MemoryAddress ControlRequestAsync$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ControlRequestAsync$VH.get(seg.asSlice(index*sizeof())); - } - public static void ControlRequestAsync$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.ControlRequestAsync$VH.set(seg.asSlice(index*sizeof()), x); - } - public static ControlRequestAsync ControlRequestAsync (MemorySegment segment, MemorySession session) { - return ControlRequestAsync.ofAddress(ControlRequestAsync$get(segment), session); - } - static final FunctionDescriptor GetPipeProperties$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetPipeProperties$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetPipeProperties$FUNC - ); - public interface GetPipeProperties { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, java.lang.foreign.MemoryAddress _x2, java.lang.foreign.MemoryAddress _x3, java.lang.foreign.MemoryAddress _x4, java.lang.foreign.MemoryAddress _x5, java.lang.foreign.MemoryAddress _x6); - static MemorySegment allocate(GetPipeProperties fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetPipeProperties.class, fi, IOUSBInterfaceStruct942.GetPipeProperties$FUNC, session); - } - static GetPipeProperties ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, java.lang.foreign.MemoryAddress __x2, java.lang.foreign.MemoryAddress __x3, java.lang.foreign.MemoryAddress __x4, java.lang.foreign.MemoryAddress __x5, java.lang.foreign.MemoryAddress __x6) -> { - try { - return (int)IOUSBInterfaceStruct942.GetPipeProperties$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2, (java.lang.foreign.Addressable)__x3, (java.lang.foreign.Addressable)__x4, (java.lang.foreign.Addressable)__x5, (java.lang.foreign.Addressable)__x6); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetPipeProperties$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetPipeProperties")); - public static VarHandle GetPipeProperties$VH() { - return IOUSBInterfaceStruct942.GetPipeProperties$VH; - } - public static MemoryAddress GetPipeProperties$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetPipeProperties$VH.get(seg); - } - public static void GetPipeProperties$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetPipeProperties$VH.set(seg, x); - } - public static MemoryAddress GetPipeProperties$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetPipeProperties$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetPipeProperties$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetPipeProperties$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetPipeProperties GetPipeProperties (MemorySegment segment, MemorySession session) { - return GetPipeProperties.ofAddress(GetPipeProperties$get(segment), session); - } - static final FunctionDescriptor GetPipeStatus$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT - ); - static final MethodHandle GetPipeStatus$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetPipeStatus$FUNC - ); - public interface GetPipeStatus { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1); - static MemorySegment allocate(GetPipeStatus fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetPipeStatus.class, fi, IOUSBInterfaceStruct942.GetPipeStatus$FUNC, session); - } - static GetPipeStatus ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.GetPipeStatus$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetPipeStatus$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetPipeStatus")); - public static VarHandle GetPipeStatus$VH() { - return IOUSBInterfaceStruct942.GetPipeStatus$VH; - } - public static MemoryAddress GetPipeStatus$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetPipeStatus$VH.get(seg); - } - public static void GetPipeStatus$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetPipeStatus$VH.set(seg, x); - } - public static MemoryAddress GetPipeStatus$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetPipeStatus$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetPipeStatus$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetPipeStatus$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetPipeStatus GetPipeStatus (MemorySegment segment, MemorySession session) { - return GetPipeStatus.ofAddress(GetPipeStatus$get(segment), session); - } - static final FunctionDescriptor AbortPipe$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT - ); - static final MethodHandle AbortPipe$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.AbortPipe$FUNC - ); - public interface AbortPipe { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1); - static MemorySegment allocate(AbortPipe fi, MemorySession session) { - return RuntimeHelper.upcallStub(AbortPipe.class, fi, IOUSBInterfaceStruct942.AbortPipe$FUNC, session); - } - static AbortPipe ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.AbortPipe$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle AbortPipe$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("AbortPipe")); - public static VarHandle AbortPipe$VH() { - return IOUSBInterfaceStruct942.AbortPipe$VH; - } - public static MemoryAddress AbortPipe$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.AbortPipe$VH.get(seg); - } - public static void AbortPipe$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.AbortPipe$VH.set(seg, x); - } - public static MemoryAddress AbortPipe$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.AbortPipe$VH.get(seg.asSlice(index*sizeof())); - } - public static void AbortPipe$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.AbortPipe$VH.set(seg.asSlice(index*sizeof()), x); - } - public static AbortPipe AbortPipe (MemorySegment segment, MemorySession session) { - return AbortPipe.ofAddress(AbortPipe$get(segment), session); - } - static final FunctionDescriptor ResetPipe$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT - ); - static final MethodHandle ResetPipe$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.ResetPipe$FUNC - ); - public interface ResetPipe { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1); - static MemorySegment allocate(ResetPipe fi, MemorySession session) { - return RuntimeHelper.upcallStub(ResetPipe.class, fi, IOUSBInterfaceStruct942.ResetPipe$FUNC, session); - } - static ResetPipe ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.ResetPipe$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle ResetPipe$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("ResetPipe")); - public static VarHandle ResetPipe$VH() { - return IOUSBInterfaceStruct942.ResetPipe$VH; - } - public static MemoryAddress ResetPipe$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ResetPipe$VH.get(seg); - } - public static void ResetPipe$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.ResetPipe$VH.set(seg, x); - } - public static MemoryAddress ResetPipe$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ResetPipe$VH.get(seg.asSlice(index*sizeof())); - } - public static void ResetPipe$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.ResetPipe$VH.set(seg.asSlice(index*sizeof()), x); - } - public static ResetPipe ResetPipe (MemorySegment segment, MemorySession session) { - return ResetPipe.ofAddress(ResetPipe$get(segment), session); - } - static final FunctionDescriptor ClearPipeStall$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT - ); - static final MethodHandle ClearPipeStall$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.ClearPipeStall$FUNC - ); - public interface ClearPipeStall { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1); - static MemorySegment allocate(ClearPipeStall fi, MemorySession session) { - return RuntimeHelper.upcallStub(ClearPipeStall.class, fi, IOUSBInterfaceStruct942.ClearPipeStall$FUNC, session); - } - static ClearPipeStall ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.ClearPipeStall$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle ClearPipeStall$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("ClearPipeStall")); - public static VarHandle ClearPipeStall$VH() { - return IOUSBInterfaceStruct942.ClearPipeStall$VH; - } - public static MemoryAddress ClearPipeStall$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ClearPipeStall$VH.get(seg); - } - public static void ClearPipeStall$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.ClearPipeStall$VH.set(seg, x); - } - public static MemoryAddress ClearPipeStall$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ClearPipeStall$VH.get(seg.asSlice(index*sizeof())); - } - public static void ClearPipeStall$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.ClearPipeStall$VH.set(seg.asSlice(index*sizeof()), x); - } - public static ClearPipeStall ClearPipeStall (MemorySegment segment, MemorySession session) { - return ClearPipeStall.ofAddress(ClearPipeStall$get(segment), session); - } - static final FunctionDescriptor ReadPipe$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle ReadPipe$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.ReadPipe$FUNC - ); - public interface ReadPipe { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, java.lang.foreign.MemoryAddress _x2, java.lang.foreign.MemoryAddress _x3); - static MemorySegment allocate(ReadPipe fi, MemorySession session) { - return RuntimeHelper.upcallStub(ReadPipe.class, fi, IOUSBInterfaceStruct942.ReadPipe$FUNC, session); - } - static ReadPipe ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, java.lang.foreign.MemoryAddress __x2, java.lang.foreign.MemoryAddress __x3) -> { - try { - return (int)IOUSBInterfaceStruct942.ReadPipe$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2, (java.lang.foreign.Addressable)__x3); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle ReadPipe$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("ReadPipe")); - public static VarHandle ReadPipe$VH() { - return IOUSBInterfaceStruct942.ReadPipe$VH; - } - public static MemoryAddress ReadPipe$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ReadPipe$VH.get(seg); - } - public static void ReadPipe$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.ReadPipe$VH.set(seg, x); - } - public static MemoryAddress ReadPipe$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ReadPipe$VH.get(seg.asSlice(index*sizeof())); - } - public static void ReadPipe$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.ReadPipe$VH.set(seg.asSlice(index*sizeof()), x); - } - public static ReadPipe ReadPipe (MemorySegment segment, MemorySession session) { - return ReadPipe.ofAddress(ReadPipe$get(segment), session); - } - static final FunctionDescriptor WritePipe$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT - ); - static final MethodHandle WritePipe$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.WritePipe$FUNC - ); - public interface WritePipe { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, java.lang.foreign.MemoryAddress _x2, int _x3); - static MemorySegment allocate(WritePipe fi, MemorySession session) { - return RuntimeHelper.upcallStub(WritePipe.class, fi, IOUSBInterfaceStruct942.WritePipe$FUNC, session); - } - static WritePipe ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, java.lang.foreign.MemoryAddress __x2, int __x3) -> { - try { - return (int)IOUSBInterfaceStruct942.WritePipe$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2, __x3); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle WritePipe$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("WritePipe")); - public static VarHandle WritePipe$VH() { - return IOUSBInterfaceStruct942.WritePipe$VH; - } - public static MemoryAddress WritePipe$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.WritePipe$VH.get(seg); - } - public static void WritePipe$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.WritePipe$VH.set(seg, x); - } - public static MemoryAddress WritePipe$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.WritePipe$VH.get(seg.asSlice(index*sizeof())); - } - public static void WritePipe$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.WritePipe$VH.set(seg.asSlice(index*sizeof()), x); - } - public static WritePipe WritePipe (MemorySegment segment, MemorySession session) { - return WritePipe.ofAddress(WritePipe$get(segment), session); - } - static final FunctionDescriptor ReadPipeAsync$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle ReadPipeAsync$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.ReadPipeAsync$FUNC - ); - public interface ReadPipeAsync { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, java.lang.foreign.MemoryAddress _x2, int _x3, java.lang.foreign.MemoryAddress _x4, java.lang.foreign.MemoryAddress _x5); - static MemorySegment allocate(ReadPipeAsync fi, MemorySession session) { - return RuntimeHelper.upcallStub(ReadPipeAsync.class, fi, IOUSBInterfaceStruct942.ReadPipeAsync$FUNC, session); - } - static ReadPipeAsync ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, java.lang.foreign.MemoryAddress __x2, int __x3, java.lang.foreign.MemoryAddress __x4, java.lang.foreign.MemoryAddress __x5) -> { - try { - return (int)IOUSBInterfaceStruct942.ReadPipeAsync$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2, __x3, (java.lang.foreign.Addressable)__x4, (java.lang.foreign.Addressable)__x5); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle ReadPipeAsync$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("ReadPipeAsync")); - public static VarHandle ReadPipeAsync$VH() { - return IOUSBInterfaceStruct942.ReadPipeAsync$VH; - } - public static MemoryAddress ReadPipeAsync$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ReadPipeAsync$VH.get(seg); - } - public static void ReadPipeAsync$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.ReadPipeAsync$VH.set(seg, x); - } - public static MemoryAddress ReadPipeAsync$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ReadPipeAsync$VH.get(seg.asSlice(index*sizeof())); - } - public static void ReadPipeAsync$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.ReadPipeAsync$VH.set(seg.asSlice(index*sizeof()), x); - } - public static ReadPipeAsync ReadPipeAsync (MemorySegment segment, MemorySession session) { - return ReadPipeAsync.ofAddress(ReadPipeAsync$get(segment), session); - } - static final FunctionDescriptor WritePipeAsync$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle WritePipeAsync$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.WritePipeAsync$FUNC - ); - public interface WritePipeAsync { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, java.lang.foreign.MemoryAddress _x2, int _x3, java.lang.foreign.MemoryAddress _x4, java.lang.foreign.MemoryAddress _x5); - static MemorySegment allocate(WritePipeAsync fi, MemorySession session) { - return RuntimeHelper.upcallStub(WritePipeAsync.class, fi, IOUSBInterfaceStruct942.WritePipeAsync$FUNC, session); - } - static WritePipeAsync ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, java.lang.foreign.MemoryAddress __x2, int __x3, java.lang.foreign.MemoryAddress __x4, java.lang.foreign.MemoryAddress __x5) -> { - try { - return (int)IOUSBInterfaceStruct942.WritePipeAsync$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2, __x3, (java.lang.foreign.Addressable)__x4, (java.lang.foreign.Addressable)__x5); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle WritePipeAsync$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("WritePipeAsync")); - public static VarHandle WritePipeAsync$VH() { - return IOUSBInterfaceStruct942.WritePipeAsync$VH; - } - public static MemoryAddress WritePipeAsync$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.WritePipeAsync$VH.get(seg); - } - public static void WritePipeAsync$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.WritePipeAsync$VH.set(seg, x); - } - public static MemoryAddress WritePipeAsync$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.WritePipeAsync$VH.get(seg.asSlice(index*sizeof())); - } - public static void WritePipeAsync$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.WritePipeAsync$VH.set(seg.asSlice(index*sizeof()), x); - } - public static WritePipeAsync WritePipeAsync (MemorySegment segment, MemorySession session) { - return WritePipeAsync.ofAddress(WritePipeAsync$get(segment), session); - } - static final FunctionDescriptor ReadIsochPipeAsync$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG_LONG$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle ReadIsochPipeAsync$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.ReadIsochPipeAsync$FUNC - ); - public interface ReadIsochPipeAsync { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, java.lang.foreign.MemoryAddress _x2, long _x3, int _x4, java.lang.foreign.MemoryAddress _x5, java.lang.foreign.MemoryAddress _x6, java.lang.foreign.MemoryAddress _x7); - static MemorySegment allocate(ReadIsochPipeAsync fi, MemorySession session) { - return RuntimeHelper.upcallStub(ReadIsochPipeAsync.class, fi, IOUSBInterfaceStruct942.ReadIsochPipeAsync$FUNC, session); - } - static ReadIsochPipeAsync ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, java.lang.foreign.MemoryAddress __x2, long __x3, int __x4, java.lang.foreign.MemoryAddress __x5, java.lang.foreign.MemoryAddress __x6, java.lang.foreign.MemoryAddress __x7) -> { - try { - return (int)IOUSBInterfaceStruct942.ReadIsochPipeAsync$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2, __x3, __x4, (java.lang.foreign.Addressable)__x5, (java.lang.foreign.Addressable)__x6, (java.lang.foreign.Addressable)__x7); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle ReadIsochPipeAsync$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("ReadIsochPipeAsync")); - public static VarHandle ReadIsochPipeAsync$VH() { - return IOUSBInterfaceStruct942.ReadIsochPipeAsync$VH; - } - public static MemoryAddress ReadIsochPipeAsync$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ReadIsochPipeAsync$VH.get(seg); - } - public static void ReadIsochPipeAsync$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.ReadIsochPipeAsync$VH.set(seg, x); - } - public static MemoryAddress ReadIsochPipeAsync$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ReadIsochPipeAsync$VH.get(seg.asSlice(index*sizeof())); - } - public static void ReadIsochPipeAsync$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.ReadIsochPipeAsync$VH.set(seg.asSlice(index*sizeof()), x); - } - public static ReadIsochPipeAsync ReadIsochPipeAsync (MemorySegment segment, MemorySession session) { - return ReadIsochPipeAsync.ofAddress(ReadIsochPipeAsync$get(segment), session); - } - static final FunctionDescriptor WriteIsochPipeAsync$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG_LONG$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle WriteIsochPipeAsync$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.WriteIsochPipeAsync$FUNC - ); - public interface WriteIsochPipeAsync { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, java.lang.foreign.MemoryAddress _x2, long _x3, int _x4, java.lang.foreign.MemoryAddress _x5, java.lang.foreign.MemoryAddress _x6, java.lang.foreign.MemoryAddress _x7); - static MemorySegment allocate(WriteIsochPipeAsync fi, MemorySession session) { - return RuntimeHelper.upcallStub(WriteIsochPipeAsync.class, fi, IOUSBInterfaceStruct942.WriteIsochPipeAsync$FUNC, session); - } - static WriteIsochPipeAsync ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, java.lang.foreign.MemoryAddress __x2, long __x3, int __x4, java.lang.foreign.MemoryAddress __x5, java.lang.foreign.MemoryAddress __x6, java.lang.foreign.MemoryAddress __x7) -> { - try { - return (int)IOUSBInterfaceStruct942.WriteIsochPipeAsync$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2, __x3, __x4, (java.lang.foreign.Addressable)__x5, (java.lang.foreign.Addressable)__x6, (java.lang.foreign.Addressable)__x7); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle WriteIsochPipeAsync$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("WriteIsochPipeAsync")); - public static VarHandle WriteIsochPipeAsync$VH() { - return IOUSBInterfaceStruct942.WriteIsochPipeAsync$VH; - } - public static MemoryAddress WriteIsochPipeAsync$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.WriteIsochPipeAsync$VH.get(seg); - } - public static void WriteIsochPipeAsync$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.WriteIsochPipeAsync$VH.set(seg, x); - } - public static MemoryAddress WriteIsochPipeAsync$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.WriteIsochPipeAsync$VH.get(seg.asSlice(index*sizeof())); - } - public static void WriteIsochPipeAsync$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.WriteIsochPipeAsync$VH.set(seg.asSlice(index*sizeof()), x); - } - public static WriteIsochPipeAsync WriteIsochPipeAsync (MemorySegment segment, MemorySession session) { - return WriteIsochPipeAsync.ofAddress(WriteIsochPipeAsync$get(segment), session); - } - static final FunctionDescriptor ControlRequestTO$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle ControlRequestTO$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.ControlRequestTO$FUNC - ); - public interface ControlRequestTO { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, java.lang.foreign.MemoryAddress _x2); - static MemorySegment allocate(ControlRequestTO fi, MemorySession session) { - return RuntimeHelper.upcallStub(ControlRequestTO.class, fi, IOUSBInterfaceStruct942.ControlRequestTO$FUNC, session); - } - static ControlRequestTO ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, java.lang.foreign.MemoryAddress __x2) -> { - try { - return (int)IOUSBInterfaceStruct942.ControlRequestTO$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle ControlRequestTO$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("ControlRequestTO")); - public static VarHandle ControlRequestTO$VH() { - return IOUSBInterfaceStruct942.ControlRequestTO$VH; - } - public static MemoryAddress ControlRequestTO$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ControlRequestTO$VH.get(seg); - } - public static void ControlRequestTO$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.ControlRequestTO$VH.set(seg, x); - } - public static MemoryAddress ControlRequestTO$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ControlRequestTO$VH.get(seg.asSlice(index*sizeof())); - } - public static void ControlRequestTO$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.ControlRequestTO$VH.set(seg.asSlice(index*sizeof()), x); - } - public static ControlRequestTO ControlRequestTO (MemorySegment segment, MemorySession session) { - return ControlRequestTO.ofAddress(ControlRequestTO$get(segment), session); - } - static final FunctionDescriptor ControlRequestAsyncTO$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle ControlRequestAsyncTO$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.ControlRequestAsyncTO$FUNC - ); - public interface ControlRequestAsyncTO { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, java.lang.foreign.MemoryAddress _x2, java.lang.foreign.MemoryAddress _x3, java.lang.foreign.MemoryAddress _x4); - static MemorySegment allocate(ControlRequestAsyncTO fi, MemorySession session) { - return RuntimeHelper.upcallStub(ControlRequestAsyncTO.class, fi, IOUSBInterfaceStruct942.ControlRequestAsyncTO$FUNC, session); - } - static ControlRequestAsyncTO ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, java.lang.foreign.MemoryAddress __x2, java.lang.foreign.MemoryAddress __x3, java.lang.foreign.MemoryAddress __x4) -> { - try { - return (int)IOUSBInterfaceStruct942.ControlRequestAsyncTO$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2, (java.lang.foreign.Addressable)__x3, (java.lang.foreign.Addressable)__x4); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle ControlRequestAsyncTO$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("ControlRequestAsyncTO")); - public static VarHandle ControlRequestAsyncTO$VH() { - return IOUSBInterfaceStruct942.ControlRequestAsyncTO$VH; - } - public static MemoryAddress ControlRequestAsyncTO$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ControlRequestAsyncTO$VH.get(seg); - } - public static void ControlRequestAsyncTO$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.ControlRequestAsyncTO$VH.set(seg, x); - } - public static MemoryAddress ControlRequestAsyncTO$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ControlRequestAsyncTO$VH.get(seg.asSlice(index*sizeof())); - } - public static void ControlRequestAsyncTO$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.ControlRequestAsyncTO$VH.set(seg.asSlice(index*sizeof()), x); - } - public static ControlRequestAsyncTO ControlRequestAsyncTO (MemorySegment segment, MemorySession session) { - return ControlRequestAsyncTO.ofAddress(ControlRequestAsyncTO$get(segment), session); - } - static final FunctionDescriptor ReadPipeTO$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT - ); - static final MethodHandle ReadPipeTO$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.ReadPipeTO$FUNC - ); - public interface ReadPipeTO { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, java.lang.foreign.MemoryAddress _x2, java.lang.foreign.MemoryAddress _x3, int _x4, int _x5); - static MemorySegment allocate(ReadPipeTO fi, MemorySession session) { - return RuntimeHelper.upcallStub(ReadPipeTO.class, fi, IOUSBInterfaceStruct942.ReadPipeTO$FUNC, session); - } - static ReadPipeTO ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, java.lang.foreign.MemoryAddress __x2, java.lang.foreign.MemoryAddress __x3, int __x4, int __x5) -> { - try { - return (int)IOUSBInterfaceStruct942.ReadPipeTO$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2, (java.lang.foreign.Addressable)__x3, __x4, __x5); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle ReadPipeTO$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("ReadPipeTO")); - public static VarHandle ReadPipeTO$VH() { - return IOUSBInterfaceStruct942.ReadPipeTO$VH; - } - public static MemoryAddress ReadPipeTO$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ReadPipeTO$VH.get(seg); - } - public static void ReadPipeTO$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.ReadPipeTO$VH.set(seg, x); - } - public static MemoryAddress ReadPipeTO$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ReadPipeTO$VH.get(seg.asSlice(index*sizeof())); - } - public static void ReadPipeTO$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.ReadPipeTO$VH.set(seg.asSlice(index*sizeof()), x); - } - public static ReadPipeTO ReadPipeTO (MemorySegment segment, MemorySession session) { - return ReadPipeTO.ofAddress(ReadPipeTO$get(segment), session); - } - static final FunctionDescriptor WritePipeTO$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT - ); - static final MethodHandle WritePipeTO$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.WritePipeTO$FUNC - ); - public interface WritePipeTO { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, java.lang.foreign.MemoryAddress _x2, int _x3, int _x4, int _x5); - static MemorySegment allocate(WritePipeTO fi, MemorySession session) { - return RuntimeHelper.upcallStub(WritePipeTO.class, fi, IOUSBInterfaceStruct942.WritePipeTO$FUNC, session); - } - static WritePipeTO ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, java.lang.foreign.MemoryAddress __x2, int __x3, int __x4, int __x5) -> { - try { - return (int)IOUSBInterfaceStruct942.WritePipeTO$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2, __x3, __x4, __x5); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle WritePipeTO$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("WritePipeTO")); - public static VarHandle WritePipeTO$VH() { - return IOUSBInterfaceStruct942.WritePipeTO$VH; - } - public static MemoryAddress WritePipeTO$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.WritePipeTO$VH.get(seg); - } - public static void WritePipeTO$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.WritePipeTO$VH.set(seg, x); - } - public static MemoryAddress WritePipeTO$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.WritePipeTO$VH.get(seg.asSlice(index*sizeof())); - } - public static void WritePipeTO$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.WritePipeTO$VH.set(seg.asSlice(index*sizeof()), x); - } - public static WritePipeTO WritePipeTO (MemorySegment segment, MemorySession session) { - return WritePipeTO.ofAddress(WritePipeTO$get(segment), session); - } - static final FunctionDescriptor ReadPipeAsyncTO$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle ReadPipeAsyncTO$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.ReadPipeAsyncTO$FUNC - ); - public interface ReadPipeAsyncTO { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, java.lang.foreign.MemoryAddress _x2, int _x3, int _x4, int _x5, java.lang.foreign.MemoryAddress _x6, java.lang.foreign.MemoryAddress _x7); - static MemorySegment allocate(ReadPipeAsyncTO fi, MemorySession session) { - return RuntimeHelper.upcallStub(ReadPipeAsyncTO.class, fi, IOUSBInterfaceStruct942.ReadPipeAsyncTO$FUNC, session); - } - static ReadPipeAsyncTO ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, java.lang.foreign.MemoryAddress __x2, int __x3, int __x4, int __x5, java.lang.foreign.MemoryAddress __x6, java.lang.foreign.MemoryAddress __x7) -> { - try { - return (int)IOUSBInterfaceStruct942.ReadPipeAsyncTO$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2, __x3, __x4, __x5, (java.lang.foreign.Addressable)__x6, (java.lang.foreign.Addressable)__x7); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle ReadPipeAsyncTO$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("ReadPipeAsyncTO")); - public static VarHandle ReadPipeAsyncTO$VH() { - return IOUSBInterfaceStruct942.ReadPipeAsyncTO$VH; - } - public static MemoryAddress ReadPipeAsyncTO$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ReadPipeAsyncTO$VH.get(seg); - } - public static void ReadPipeAsyncTO$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.ReadPipeAsyncTO$VH.set(seg, x); - } - public static MemoryAddress ReadPipeAsyncTO$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ReadPipeAsyncTO$VH.get(seg.asSlice(index*sizeof())); - } - public static void ReadPipeAsyncTO$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.ReadPipeAsyncTO$VH.set(seg.asSlice(index*sizeof()), x); - } - public static ReadPipeAsyncTO ReadPipeAsyncTO (MemorySegment segment, MemorySession session) { - return ReadPipeAsyncTO.ofAddress(ReadPipeAsyncTO$get(segment), session); - } - static final FunctionDescriptor WritePipeAsyncTO$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle WritePipeAsyncTO$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.WritePipeAsyncTO$FUNC - ); - public interface WritePipeAsyncTO { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, java.lang.foreign.MemoryAddress _x2, int _x3, int _x4, int _x5, java.lang.foreign.MemoryAddress _x6, java.lang.foreign.MemoryAddress _x7); - static MemorySegment allocate(WritePipeAsyncTO fi, MemorySession session) { - return RuntimeHelper.upcallStub(WritePipeAsyncTO.class, fi, IOUSBInterfaceStruct942.WritePipeAsyncTO$FUNC, session); - } - static WritePipeAsyncTO ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, java.lang.foreign.MemoryAddress __x2, int __x3, int __x4, int __x5, java.lang.foreign.MemoryAddress __x6, java.lang.foreign.MemoryAddress __x7) -> { - try { - return (int)IOUSBInterfaceStruct942.WritePipeAsyncTO$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2, __x3, __x4, __x5, (java.lang.foreign.Addressable)__x6, (java.lang.foreign.Addressable)__x7); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle WritePipeAsyncTO$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("WritePipeAsyncTO")); - public static VarHandle WritePipeAsyncTO$VH() { - return IOUSBInterfaceStruct942.WritePipeAsyncTO$VH; - } - public static MemoryAddress WritePipeAsyncTO$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.WritePipeAsyncTO$VH.get(seg); - } - public static void WritePipeAsyncTO$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.WritePipeAsyncTO$VH.set(seg, x); - } - public static MemoryAddress WritePipeAsyncTO$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.WritePipeAsyncTO$VH.get(seg.asSlice(index*sizeof())); - } - public static void WritePipeAsyncTO$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.WritePipeAsyncTO$VH.set(seg.asSlice(index*sizeof()), x); - } - public static WritePipeAsyncTO WritePipeAsyncTO (MemorySegment segment, MemorySession session) { - return WritePipeAsyncTO.ofAddress(WritePipeAsyncTO$get(segment), session); - } - static final FunctionDescriptor USBInterfaceGetStringIndex$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle USBInterfaceGetStringIndex$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.USBInterfaceGetStringIndex$FUNC - ); - public interface USBInterfaceGetStringIndex { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(USBInterfaceGetStringIndex fi, MemorySession session) { - return RuntimeHelper.upcallStub(USBInterfaceGetStringIndex.class, fi, IOUSBInterfaceStruct942.USBInterfaceGetStringIndex$FUNC, session); - } - static USBInterfaceGetStringIndex ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.USBInterfaceGetStringIndex$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle USBInterfaceGetStringIndex$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("USBInterfaceGetStringIndex")); - public static VarHandle USBInterfaceGetStringIndex$VH() { - return IOUSBInterfaceStruct942.USBInterfaceGetStringIndex$VH; - } - public static MemoryAddress USBInterfaceGetStringIndex$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.USBInterfaceGetStringIndex$VH.get(seg); - } - public static void USBInterfaceGetStringIndex$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.USBInterfaceGetStringIndex$VH.set(seg, x); - } - public static MemoryAddress USBInterfaceGetStringIndex$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.USBInterfaceGetStringIndex$VH.get(seg.asSlice(index*sizeof())); - } - public static void USBInterfaceGetStringIndex$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.USBInterfaceGetStringIndex$VH.set(seg.asSlice(index*sizeof()), x); - } - public static USBInterfaceGetStringIndex USBInterfaceGetStringIndex (MemorySegment segment, MemorySession session) { - return USBInterfaceGetStringIndex.ofAddress(USBInterfaceGetStringIndex$get(segment), session); - } - static final FunctionDescriptor USBInterfaceOpenSeize$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle USBInterfaceOpenSeize$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.USBInterfaceOpenSeize$FUNC - ); - public interface USBInterfaceOpenSeize { - - int apply(java.lang.foreign.MemoryAddress _x0); - static MemorySegment allocate(USBInterfaceOpenSeize fi, MemorySession session) { - return RuntimeHelper.upcallStub(USBInterfaceOpenSeize.class, fi, IOUSBInterfaceStruct942.USBInterfaceOpenSeize$FUNC, session); - } - static USBInterfaceOpenSeize ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0) -> { - try { - return (int)IOUSBInterfaceStruct942.USBInterfaceOpenSeize$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle USBInterfaceOpenSeize$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("USBInterfaceOpenSeize")); - public static VarHandle USBInterfaceOpenSeize$VH() { - return IOUSBInterfaceStruct942.USBInterfaceOpenSeize$VH; - } - public static MemoryAddress USBInterfaceOpenSeize$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.USBInterfaceOpenSeize$VH.get(seg); - } - public static void USBInterfaceOpenSeize$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.USBInterfaceOpenSeize$VH.set(seg, x); - } - public static MemoryAddress USBInterfaceOpenSeize$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.USBInterfaceOpenSeize$VH.get(seg.asSlice(index*sizeof())); - } - public static void USBInterfaceOpenSeize$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.USBInterfaceOpenSeize$VH.set(seg.asSlice(index*sizeof()), x); - } - public static USBInterfaceOpenSeize USBInterfaceOpenSeize (MemorySegment segment, MemorySession session) { - return USBInterfaceOpenSeize.ofAddress(USBInterfaceOpenSeize$get(segment), session); - } - static final FunctionDescriptor ClearPipeStallBothEnds$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT - ); - static final MethodHandle ClearPipeStallBothEnds$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.ClearPipeStallBothEnds$FUNC - ); - public interface ClearPipeStallBothEnds { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1); - static MemorySegment allocate(ClearPipeStallBothEnds fi, MemorySession session) { - return RuntimeHelper.upcallStub(ClearPipeStallBothEnds.class, fi, IOUSBInterfaceStruct942.ClearPipeStallBothEnds$FUNC, session); - } - static ClearPipeStallBothEnds ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.ClearPipeStallBothEnds$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle ClearPipeStallBothEnds$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("ClearPipeStallBothEnds")); - public static VarHandle ClearPipeStallBothEnds$VH() { - return IOUSBInterfaceStruct942.ClearPipeStallBothEnds$VH; - } - public static MemoryAddress ClearPipeStallBothEnds$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ClearPipeStallBothEnds$VH.get(seg); - } - public static void ClearPipeStallBothEnds$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.ClearPipeStallBothEnds$VH.set(seg, x); - } - public static MemoryAddress ClearPipeStallBothEnds$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ClearPipeStallBothEnds$VH.get(seg.asSlice(index*sizeof())); - } - public static void ClearPipeStallBothEnds$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.ClearPipeStallBothEnds$VH.set(seg.asSlice(index*sizeof()), x); - } - public static ClearPipeStallBothEnds ClearPipeStallBothEnds (MemorySegment segment, MemorySession session) { - return ClearPipeStallBothEnds.ofAddress(ClearPipeStallBothEnds$get(segment), session); - } - static final FunctionDescriptor SetPipePolicy$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_SHORT$LAYOUT, - Constants$root.C_CHAR$LAYOUT - ); - static final MethodHandle SetPipePolicy$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.SetPipePolicy$FUNC - ); - public interface SetPipePolicy { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, short _x2, byte _x3); - static MemorySegment allocate(SetPipePolicy fi, MemorySession session) { - return RuntimeHelper.upcallStub(SetPipePolicy.class, fi, IOUSBInterfaceStruct942.SetPipePolicy$FUNC, session); - } - static SetPipePolicy ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, short __x2, byte __x3) -> { - try { - return (int)IOUSBInterfaceStruct942.SetPipePolicy$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, __x2, __x3); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle SetPipePolicy$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("SetPipePolicy")); - public static VarHandle SetPipePolicy$VH() { - return IOUSBInterfaceStruct942.SetPipePolicy$VH; - } - public static MemoryAddress SetPipePolicy$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.SetPipePolicy$VH.get(seg); - } - public static void SetPipePolicy$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.SetPipePolicy$VH.set(seg, x); - } - public static MemoryAddress SetPipePolicy$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.SetPipePolicy$VH.get(seg.asSlice(index*sizeof())); - } - public static void SetPipePolicy$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.SetPipePolicy$VH.set(seg.asSlice(index*sizeof()), x); - } - public static SetPipePolicy SetPipePolicy (MemorySegment segment, MemorySession session) { - return SetPipePolicy.ofAddress(SetPipePolicy$get(segment), session); - } - static final FunctionDescriptor GetBandwidthAvailable$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetBandwidthAvailable$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetBandwidthAvailable$FUNC - ); - public interface GetBandwidthAvailable { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetBandwidthAvailable fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetBandwidthAvailable.class, fi, IOUSBInterfaceStruct942.GetBandwidthAvailable$FUNC, session); - } - static GetBandwidthAvailable ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.GetBandwidthAvailable$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetBandwidthAvailable$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetBandwidthAvailable")); - public static VarHandle GetBandwidthAvailable$VH() { - return IOUSBInterfaceStruct942.GetBandwidthAvailable$VH; - } - public static MemoryAddress GetBandwidthAvailable$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetBandwidthAvailable$VH.get(seg); - } - public static void GetBandwidthAvailable$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetBandwidthAvailable$VH.set(seg, x); - } - public static MemoryAddress GetBandwidthAvailable$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetBandwidthAvailable$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetBandwidthAvailable$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetBandwidthAvailable$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetBandwidthAvailable GetBandwidthAvailable (MemorySegment segment, MemorySession session) { - return GetBandwidthAvailable.ofAddress(GetBandwidthAvailable$get(segment), session); - } - static final FunctionDescriptor GetEndpointProperties$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetEndpointProperties$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetEndpointProperties$FUNC - ); - public interface GetEndpointProperties { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, byte _x2, byte _x3, java.lang.foreign.MemoryAddress _x4, java.lang.foreign.MemoryAddress _x5, java.lang.foreign.MemoryAddress _x6); - static MemorySegment allocate(GetEndpointProperties fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetEndpointProperties.class, fi, IOUSBInterfaceStruct942.GetEndpointProperties$FUNC, session); - } - static GetEndpointProperties ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, byte __x2, byte __x3, java.lang.foreign.MemoryAddress __x4, java.lang.foreign.MemoryAddress __x5, java.lang.foreign.MemoryAddress __x6) -> { - try { - return (int)IOUSBInterfaceStruct942.GetEndpointProperties$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, __x2, __x3, (java.lang.foreign.Addressable)__x4, (java.lang.foreign.Addressable)__x5, (java.lang.foreign.Addressable)__x6); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetEndpointProperties$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetEndpointProperties")); - public static VarHandle GetEndpointProperties$VH() { - return IOUSBInterfaceStruct942.GetEndpointProperties$VH; - } - public static MemoryAddress GetEndpointProperties$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetEndpointProperties$VH.get(seg); - } - public static void GetEndpointProperties$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetEndpointProperties$VH.set(seg, x); - } - public static MemoryAddress GetEndpointProperties$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetEndpointProperties$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetEndpointProperties$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetEndpointProperties$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetEndpointProperties GetEndpointProperties (MemorySegment segment, MemorySession session) { - return GetEndpointProperties.ofAddress(GetEndpointProperties$get(segment), session); - } - static final FunctionDescriptor LowLatencyReadIsochPipeAsync$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG_LONG$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle LowLatencyReadIsochPipeAsync$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.LowLatencyReadIsochPipeAsync$FUNC - ); - public interface LowLatencyReadIsochPipeAsync { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, java.lang.foreign.MemoryAddress _x2, long _x3, int _x4, int _x5, java.lang.foreign.MemoryAddress _x6, java.lang.foreign.MemoryAddress _x7, java.lang.foreign.MemoryAddress _x8); - static MemorySegment allocate(LowLatencyReadIsochPipeAsync fi, MemorySession session) { - return RuntimeHelper.upcallStub(LowLatencyReadIsochPipeAsync.class, fi, IOUSBInterfaceStruct942.LowLatencyReadIsochPipeAsync$FUNC, session); - } - static LowLatencyReadIsochPipeAsync ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, java.lang.foreign.MemoryAddress __x2, long __x3, int __x4, int __x5, java.lang.foreign.MemoryAddress __x6, java.lang.foreign.MemoryAddress __x7, java.lang.foreign.MemoryAddress __x8) -> { - try { - return (int)IOUSBInterfaceStruct942.LowLatencyReadIsochPipeAsync$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2, __x3, __x4, __x5, (java.lang.foreign.Addressable)__x6, (java.lang.foreign.Addressable)__x7, (java.lang.foreign.Addressable)__x8); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle LowLatencyReadIsochPipeAsync$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("LowLatencyReadIsochPipeAsync")); - public static VarHandle LowLatencyReadIsochPipeAsync$VH() { - return IOUSBInterfaceStruct942.LowLatencyReadIsochPipeAsync$VH; - } - public static MemoryAddress LowLatencyReadIsochPipeAsync$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.LowLatencyReadIsochPipeAsync$VH.get(seg); - } - public static void LowLatencyReadIsochPipeAsync$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.LowLatencyReadIsochPipeAsync$VH.set(seg, x); - } - public static MemoryAddress LowLatencyReadIsochPipeAsync$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.LowLatencyReadIsochPipeAsync$VH.get(seg.asSlice(index*sizeof())); - } - public static void LowLatencyReadIsochPipeAsync$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.LowLatencyReadIsochPipeAsync$VH.set(seg.asSlice(index*sizeof()), x); - } - public static LowLatencyReadIsochPipeAsync LowLatencyReadIsochPipeAsync (MemorySegment segment, MemorySession session) { - return LowLatencyReadIsochPipeAsync.ofAddress(LowLatencyReadIsochPipeAsync$get(segment), session); - } - static final FunctionDescriptor LowLatencyWriteIsochPipeAsync$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG_LONG$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle LowLatencyWriteIsochPipeAsync$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.LowLatencyWriteIsochPipeAsync$FUNC - ); - public interface LowLatencyWriteIsochPipeAsync { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, java.lang.foreign.MemoryAddress _x2, long _x3, int _x4, int _x5, java.lang.foreign.MemoryAddress _x6, java.lang.foreign.MemoryAddress _x7, java.lang.foreign.MemoryAddress _x8); - static MemorySegment allocate(LowLatencyWriteIsochPipeAsync fi, MemorySession session) { - return RuntimeHelper.upcallStub(LowLatencyWriteIsochPipeAsync.class, fi, IOUSBInterfaceStruct942.LowLatencyWriteIsochPipeAsync$FUNC, session); - } - static LowLatencyWriteIsochPipeAsync ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, java.lang.foreign.MemoryAddress __x2, long __x3, int __x4, int __x5, java.lang.foreign.MemoryAddress __x6, java.lang.foreign.MemoryAddress __x7, java.lang.foreign.MemoryAddress __x8) -> { - try { - return (int)IOUSBInterfaceStruct942.LowLatencyWriteIsochPipeAsync$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2, __x3, __x4, __x5, (java.lang.foreign.Addressable)__x6, (java.lang.foreign.Addressable)__x7, (java.lang.foreign.Addressable)__x8); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle LowLatencyWriteIsochPipeAsync$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("LowLatencyWriteIsochPipeAsync")); - public static VarHandle LowLatencyWriteIsochPipeAsync$VH() { - return IOUSBInterfaceStruct942.LowLatencyWriteIsochPipeAsync$VH; - } - public static MemoryAddress LowLatencyWriteIsochPipeAsync$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.LowLatencyWriteIsochPipeAsync$VH.get(seg); - } - public static void LowLatencyWriteIsochPipeAsync$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.LowLatencyWriteIsochPipeAsync$VH.set(seg, x); - } - public static MemoryAddress LowLatencyWriteIsochPipeAsync$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.LowLatencyWriteIsochPipeAsync$VH.get(seg.asSlice(index*sizeof())); - } - public static void LowLatencyWriteIsochPipeAsync$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.LowLatencyWriteIsochPipeAsync$VH.set(seg.asSlice(index*sizeof()), x); - } - public static LowLatencyWriteIsochPipeAsync LowLatencyWriteIsochPipeAsync (MemorySegment segment, MemorySession session) { - return LowLatencyWriteIsochPipeAsync.ofAddress(LowLatencyWriteIsochPipeAsync$get(segment), session); - } - static final FunctionDescriptor LowLatencyCreateBuffer$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT - ); - static final MethodHandle LowLatencyCreateBuffer$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.LowLatencyCreateBuffer$FUNC - ); - public interface LowLatencyCreateBuffer { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1, int _x2, int _x3); - static MemorySegment allocate(LowLatencyCreateBuffer fi, MemorySession session) { - return RuntimeHelper.upcallStub(LowLatencyCreateBuffer.class, fi, IOUSBInterfaceStruct942.LowLatencyCreateBuffer$FUNC, session); - } - static LowLatencyCreateBuffer ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1, int __x2, int __x3) -> { - try { - return (int)IOUSBInterfaceStruct942.LowLatencyCreateBuffer$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1, __x2, __x3); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle LowLatencyCreateBuffer$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("LowLatencyCreateBuffer")); - public static VarHandle LowLatencyCreateBuffer$VH() { - return IOUSBInterfaceStruct942.LowLatencyCreateBuffer$VH; - } - public static MemoryAddress LowLatencyCreateBuffer$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.LowLatencyCreateBuffer$VH.get(seg); - } - public static void LowLatencyCreateBuffer$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.LowLatencyCreateBuffer$VH.set(seg, x); - } - public static MemoryAddress LowLatencyCreateBuffer$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.LowLatencyCreateBuffer$VH.get(seg.asSlice(index*sizeof())); - } - public static void LowLatencyCreateBuffer$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.LowLatencyCreateBuffer$VH.set(seg.asSlice(index*sizeof()), x); - } - public static LowLatencyCreateBuffer LowLatencyCreateBuffer (MemorySegment segment, MemorySession session) { - return LowLatencyCreateBuffer.ofAddress(LowLatencyCreateBuffer$get(segment), session); - } - static final FunctionDescriptor LowLatencyDestroyBuffer$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle LowLatencyDestroyBuffer$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.LowLatencyDestroyBuffer$FUNC - ); - public interface LowLatencyDestroyBuffer { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(LowLatencyDestroyBuffer fi, MemorySession session) { - return RuntimeHelper.upcallStub(LowLatencyDestroyBuffer.class, fi, IOUSBInterfaceStruct942.LowLatencyDestroyBuffer$FUNC, session); - } - static LowLatencyDestroyBuffer ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.LowLatencyDestroyBuffer$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle LowLatencyDestroyBuffer$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("LowLatencyDestroyBuffer")); - public static VarHandle LowLatencyDestroyBuffer$VH() { - return IOUSBInterfaceStruct942.LowLatencyDestroyBuffer$VH; - } - public static MemoryAddress LowLatencyDestroyBuffer$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.LowLatencyDestroyBuffer$VH.get(seg); - } - public static void LowLatencyDestroyBuffer$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.LowLatencyDestroyBuffer$VH.set(seg, x); - } - public static MemoryAddress LowLatencyDestroyBuffer$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.LowLatencyDestroyBuffer$VH.get(seg.asSlice(index*sizeof())); - } - public static void LowLatencyDestroyBuffer$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.LowLatencyDestroyBuffer$VH.set(seg.asSlice(index*sizeof()), x); - } - public static LowLatencyDestroyBuffer LowLatencyDestroyBuffer (MemorySegment segment, MemorySession session) { - return LowLatencyDestroyBuffer.ofAddress(LowLatencyDestroyBuffer$get(segment), session); - } - static final FunctionDescriptor GetBusMicroFrameNumber$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetBusMicroFrameNumber$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetBusMicroFrameNumber$FUNC - ); - public interface GetBusMicroFrameNumber { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1, java.lang.foreign.MemoryAddress _x2); - static MemorySegment allocate(GetBusMicroFrameNumber fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetBusMicroFrameNumber.class, fi, IOUSBInterfaceStruct942.GetBusMicroFrameNumber$FUNC, session); - } - static GetBusMicroFrameNumber ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1, java.lang.foreign.MemoryAddress __x2) -> { - try { - return (int)IOUSBInterfaceStruct942.GetBusMicroFrameNumber$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1, (java.lang.foreign.Addressable)__x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetBusMicroFrameNumber$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetBusMicroFrameNumber")); - public static VarHandle GetBusMicroFrameNumber$VH() { - return IOUSBInterfaceStruct942.GetBusMicroFrameNumber$VH; - } - public static MemoryAddress GetBusMicroFrameNumber$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetBusMicroFrameNumber$VH.get(seg); - } - public static void GetBusMicroFrameNumber$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetBusMicroFrameNumber$VH.set(seg, x); - } - public static MemoryAddress GetBusMicroFrameNumber$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetBusMicroFrameNumber$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetBusMicroFrameNumber$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetBusMicroFrameNumber$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetBusMicroFrameNumber GetBusMicroFrameNumber (MemorySegment segment, MemorySession session) { - return GetBusMicroFrameNumber.ofAddress(GetBusMicroFrameNumber$get(segment), session); - } - static final FunctionDescriptor GetFrameListTime$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetFrameListTime$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetFrameListTime$FUNC - ); - public interface GetFrameListTime { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetFrameListTime fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetFrameListTime.class, fi, IOUSBInterfaceStruct942.GetFrameListTime$FUNC, session); - } - static GetFrameListTime ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.GetFrameListTime$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetFrameListTime$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetFrameListTime")); - public static VarHandle GetFrameListTime$VH() { - return IOUSBInterfaceStruct942.GetFrameListTime$VH; - } - public static MemoryAddress GetFrameListTime$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetFrameListTime$VH.get(seg); - } - public static void GetFrameListTime$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetFrameListTime$VH.set(seg, x); - } - public static MemoryAddress GetFrameListTime$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetFrameListTime$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetFrameListTime$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetFrameListTime$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetFrameListTime GetFrameListTime (MemorySegment segment, MemorySession session) { - return GetFrameListTime.ofAddress(GetFrameListTime$get(segment), session); - } - static final FunctionDescriptor GetIOUSBLibVersion$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetIOUSBLibVersion$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetIOUSBLibVersion$FUNC - ); - public interface GetIOUSBLibVersion { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1, java.lang.foreign.MemoryAddress _x2); - static MemorySegment allocate(GetIOUSBLibVersion fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetIOUSBLibVersion.class, fi, IOUSBInterfaceStruct942.GetIOUSBLibVersion$FUNC, session); - } - static GetIOUSBLibVersion ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1, java.lang.foreign.MemoryAddress __x2) -> { - try { - return (int)IOUSBInterfaceStruct942.GetIOUSBLibVersion$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1, (java.lang.foreign.Addressable)__x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetIOUSBLibVersion$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetIOUSBLibVersion")); - public static VarHandle GetIOUSBLibVersion$VH() { - return IOUSBInterfaceStruct942.GetIOUSBLibVersion$VH; - } - public static MemoryAddress GetIOUSBLibVersion$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetIOUSBLibVersion$VH.get(seg); - } - public static void GetIOUSBLibVersion$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetIOUSBLibVersion$VH.set(seg, x); - } - public static MemoryAddress GetIOUSBLibVersion$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetIOUSBLibVersion$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetIOUSBLibVersion$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetIOUSBLibVersion$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetIOUSBLibVersion GetIOUSBLibVersion (MemorySegment segment, MemorySession session) { - return GetIOUSBLibVersion.ofAddress(GetIOUSBLibVersion$get(segment), session); - } - static final FunctionDescriptor FindNextAssociatedDescriptor$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT - ); - static final MethodHandle FindNextAssociatedDescriptor$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.FindNextAssociatedDescriptor$FUNC - ); - public interface FindNextAssociatedDescriptor { - - java.lang.foreign.Addressable apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1, byte _x2); - static MemorySegment allocate(FindNextAssociatedDescriptor fi, MemorySession session) { - return RuntimeHelper.upcallStub(FindNextAssociatedDescriptor.class, fi, IOUSBInterfaceStruct942.FindNextAssociatedDescriptor$FUNC, session); - } - static FindNextAssociatedDescriptor ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1, byte __x2) -> { - try { - return (java.lang.foreign.Addressable)(java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.FindNextAssociatedDescriptor$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1, __x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle FindNextAssociatedDescriptor$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("FindNextAssociatedDescriptor")); - public static VarHandle FindNextAssociatedDescriptor$VH() { - return IOUSBInterfaceStruct942.FindNextAssociatedDescriptor$VH; - } - public static MemoryAddress FindNextAssociatedDescriptor$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.FindNextAssociatedDescriptor$VH.get(seg); - } - public static void FindNextAssociatedDescriptor$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.FindNextAssociatedDescriptor$VH.set(seg, x); - } - public static MemoryAddress FindNextAssociatedDescriptor$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.FindNextAssociatedDescriptor$VH.get(seg.asSlice(index*sizeof())); - } - public static void FindNextAssociatedDescriptor$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.FindNextAssociatedDescriptor$VH.set(seg.asSlice(index*sizeof()), x); - } - public static FindNextAssociatedDescriptor FindNextAssociatedDescriptor (MemorySegment segment, MemorySession session) { - return FindNextAssociatedDescriptor.ofAddress(FindNextAssociatedDescriptor$get(segment), session); - } - static final FunctionDescriptor FindNextAltInterface$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle FindNextAltInterface$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.FindNextAltInterface$FUNC - ); - public interface FindNextAltInterface { - - java.lang.foreign.Addressable apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1, java.lang.foreign.MemoryAddress _x2); - static MemorySegment allocate(FindNextAltInterface fi, MemorySession session) { - return RuntimeHelper.upcallStub(FindNextAltInterface.class, fi, IOUSBInterfaceStruct942.FindNextAltInterface$FUNC, session); - } - static FindNextAltInterface ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1, java.lang.foreign.MemoryAddress __x2) -> { - try { - return (java.lang.foreign.Addressable)(java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.FindNextAltInterface$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1, (java.lang.foreign.Addressable)__x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle FindNextAltInterface$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("FindNextAltInterface")); - public static VarHandle FindNextAltInterface$VH() { - return IOUSBInterfaceStruct942.FindNextAltInterface$VH; - } - public static MemoryAddress FindNextAltInterface$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.FindNextAltInterface$VH.get(seg); - } - public static void FindNextAltInterface$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.FindNextAltInterface$VH.set(seg, x); - } - public static MemoryAddress FindNextAltInterface$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.FindNextAltInterface$VH.get(seg.asSlice(index*sizeof())); - } - public static void FindNextAltInterface$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.FindNextAltInterface$VH.set(seg.asSlice(index*sizeof()), x); - } - public static FindNextAltInterface FindNextAltInterface (MemorySegment segment, MemorySession session) { - return FindNextAltInterface.ofAddress(FindNextAltInterface$get(segment), session); - } - static final FunctionDescriptor GetBusFrameNumberWithTime$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetBusFrameNumberWithTime$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetBusFrameNumberWithTime$FUNC - ); - public interface GetBusFrameNumberWithTime { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1, java.lang.foreign.MemoryAddress _x2); - static MemorySegment allocate(GetBusFrameNumberWithTime fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetBusFrameNumberWithTime.class, fi, IOUSBInterfaceStruct942.GetBusFrameNumberWithTime$FUNC, session); - } - static GetBusFrameNumberWithTime ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1, java.lang.foreign.MemoryAddress __x2) -> { - try { - return (int)IOUSBInterfaceStruct942.GetBusFrameNumberWithTime$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1, (java.lang.foreign.Addressable)__x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetBusFrameNumberWithTime$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetBusFrameNumberWithTime")); - public static VarHandle GetBusFrameNumberWithTime$VH() { - return IOUSBInterfaceStruct942.GetBusFrameNumberWithTime$VH; - } - public static MemoryAddress GetBusFrameNumberWithTime$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetBusFrameNumberWithTime$VH.get(seg); - } - public static void GetBusFrameNumberWithTime$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetBusFrameNumberWithTime$VH.set(seg, x); - } - public static MemoryAddress GetBusFrameNumberWithTime$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetBusFrameNumberWithTime$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetBusFrameNumberWithTime$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetBusFrameNumberWithTime$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetBusFrameNumberWithTime GetBusFrameNumberWithTime (MemorySegment segment, MemorySession session) { - return GetBusFrameNumberWithTime.ofAddress(GetBusFrameNumberWithTime$get(segment), session); - } - static final FunctionDescriptor GetPipePropertiesV2$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetPipePropertiesV2$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetPipePropertiesV2$FUNC - ); - public interface GetPipePropertiesV2 { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, java.lang.foreign.MemoryAddress _x2, java.lang.foreign.MemoryAddress _x3, java.lang.foreign.MemoryAddress _x4, java.lang.foreign.MemoryAddress _x5, java.lang.foreign.MemoryAddress _x6, java.lang.foreign.MemoryAddress _x7, java.lang.foreign.MemoryAddress _x8, java.lang.foreign.MemoryAddress _x9); - static MemorySegment allocate(GetPipePropertiesV2 fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetPipePropertiesV2.class, fi, IOUSBInterfaceStruct942.GetPipePropertiesV2$FUNC, session); - } - static GetPipePropertiesV2 ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, java.lang.foreign.MemoryAddress __x2, java.lang.foreign.MemoryAddress __x3, java.lang.foreign.MemoryAddress __x4, java.lang.foreign.MemoryAddress __x5, java.lang.foreign.MemoryAddress __x6, java.lang.foreign.MemoryAddress __x7, java.lang.foreign.MemoryAddress __x8, java.lang.foreign.MemoryAddress __x9) -> { - try { - return (int)IOUSBInterfaceStruct942.GetPipePropertiesV2$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2, (java.lang.foreign.Addressable)__x3, (java.lang.foreign.Addressable)__x4, (java.lang.foreign.Addressable)__x5, (java.lang.foreign.Addressable)__x6, (java.lang.foreign.Addressable)__x7, (java.lang.foreign.Addressable)__x8, (java.lang.foreign.Addressable)__x9); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetPipePropertiesV2$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetPipePropertiesV2")); - public static VarHandle GetPipePropertiesV2$VH() { - return IOUSBInterfaceStruct942.GetPipePropertiesV2$VH; - } - public static MemoryAddress GetPipePropertiesV2$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetPipePropertiesV2$VH.get(seg); - } - public static void GetPipePropertiesV2$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetPipePropertiesV2$VH.set(seg, x); - } - public static MemoryAddress GetPipePropertiesV2$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetPipePropertiesV2$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetPipePropertiesV2$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetPipePropertiesV2$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetPipePropertiesV2 GetPipePropertiesV2 (MemorySegment segment, MemorySession session) { - return GetPipePropertiesV2.ofAddress(GetPipePropertiesV2$get(segment), session); - } - static final FunctionDescriptor GetPipePropertiesV3$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetPipePropertiesV3$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetPipePropertiesV3$FUNC - ); - public interface GetPipePropertiesV3 { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, java.lang.foreign.MemoryAddress _x2); - static MemorySegment allocate(GetPipePropertiesV3 fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetPipePropertiesV3.class, fi, IOUSBInterfaceStruct942.GetPipePropertiesV3$FUNC, session); - } - static GetPipePropertiesV3 ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, java.lang.foreign.MemoryAddress __x2) -> { - try { - return (int)IOUSBInterfaceStruct942.GetPipePropertiesV3$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetPipePropertiesV3$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetPipePropertiesV3")); - public static VarHandle GetPipePropertiesV3$VH() { - return IOUSBInterfaceStruct942.GetPipePropertiesV3$VH; - } - public static MemoryAddress GetPipePropertiesV3$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetPipePropertiesV3$VH.get(seg); - } - public static void GetPipePropertiesV3$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetPipePropertiesV3$VH.set(seg, x); - } - public static MemoryAddress GetPipePropertiesV3$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetPipePropertiesV3$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetPipePropertiesV3$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetPipePropertiesV3$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetPipePropertiesV3 GetPipePropertiesV3 (MemorySegment segment, MemorySession session) { - return GetPipePropertiesV3.ofAddress(GetPipePropertiesV3$get(segment), session); - } - static final FunctionDescriptor GetEndpointPropertiesV3$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetEndpointPropertiesV3$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetEndpointPropertiesV3$FUNC - ); - public interface GetEndpointPropertiesV3 { - - int apply(java.lang.foreign.MemoryAddress _x0, java.lang.foreign.MemoryAddress _x1); - static MemorySegment allocate(GetEndpointPropertiesV3 fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetEndpointPropertiesV3.class, fi, IOUSBInterfaceStruct942.GetEndpointPropertiesV3$FUNC, session); - } - static GetEndpointPropertiesV3 ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, java.lang.foreign.MemoryAddress __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.GetEndpointPropertiesV3$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, (java.lang.foreign.Addressable)__x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetEndpointPropertiesV3$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetEndpointPropertiesV3")); - public static VarHandle GetEndpointPropertiesV3$VH() { - return IOUSBInterfaceStruct942.GetEndpointPropertiesV3$VH; - } - public static MemoryAddress GetEndpointPropertiesV3$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetEndpointPropertiesV3$VH.get(seg); - } - public static void GetEndpointPropertiesV3$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetEndpointPropertiesV3$VH.set(seg, x); - } - public static MemoryAddress GetEndpointPropertiesV3$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetEndpointPropertiesV3$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetEndpointPropertiesV3$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetEndpointPropertiesV3$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetEndpointPropertiesV3 GetEndpointPropertiesV3 (MemorySegment segment, MemorySession session) { - return GetEndpointPropertiesV3.ofAddress(GetEndpointPropertiesV3$get(segment), session); - } - static final FunctionDescriptor SupportsStreams$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle SupportsStreams$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.SupportsStreams$FUNC - ); - public interface SupportsStreams { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, java.lang.foreign.MemoryAddress _x2); - static MemorySegment allocate(SupportsStreams fi, MemorySession session) { - return RuntimeHelper.upcallStub(SupportsStreams.class, fi, IOUSBInterfaceStruct942.SupportsStreams$FUNC, session); - } - static SupportsStreams ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, java.lang.foreign.MemoryAddress __x2) -> { - try { - return (int)IOUSBInterfaceStruct942.SupportsStreams$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle SupportsStreams$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("SupportsStreams")); - public static VarHandle SupportsStreams$VH() { - return IOUSBInterfaceStruct942.SupportsStreams$VH; - } - public static MemoryAddress SupportsStreams$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.SupportsStreams$VH.get(seg); - } - public static void SupportsStreams$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.SupportsStreams$VH.set(seg, x); - } - public static MemoryAddress SupportsStreams$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.SupportsStreams$VH.get(seg.asSlice(index*sizeof())); - } - public static void SupportsStreams$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.SupportsStreams$VH.set(seg.asSlice(index*sizeof()), x); - } - public static SupportsStreams SupportsStreams (MemorySegment segment, MemorySession session) { - return SupportsStreams.ofAddress(SupportsStreams$get(segment), session); - } - static final FunctionDescriptor CreateStreams$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_INT$LAYOUT - ); - static final MethodHandle CreateStreams$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.CreateStreams$FUNC - ); - public interface CreateStreams { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, int _x2); - static MemorySegment allocate(CreateStreams fi, MemorySession session) { - return RuntimeHelper.upcallStub(CreateStreams.class, fi, IOUSBInterfaceStruct942.CreateStreams$FUNC, session); - } - static CreateStreams ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, int __x2) -> { - try { - return (int)IOUSBInterfaceStruct942.CreateStreams$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, __x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle CreateStreams$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("CreateStreams")); - public static VarHandle CreateStreams$VH() { - return IOUSBInterfaceStruct942.CreateStreams$VH; - } - public static MemoryAddress CreateStreams$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.CreateStreams$VH.get(seg); - } - public static void CreateStreams$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.CreateStreams$VH.set(seg, x); - } - public static MemoryAddress CreateStreams$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.CreateStreams$VH.get(seg.asSlice(index*sizeof())); - } - public static void CreateStreams$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.CreateStreams$VH.set(seg.asSlice(index*sizeof()), x); - } - public static CreateStreams CreateStreams (MemorySegment segment, MemorySession session) { - return CreateStreams.ofAddress(CreateStreams$get(segment), session); - } - static final FunctionDescriptor GetConfiguredStreams$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetConfiguredStreams$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetConfiguredStreams$FUNC - ); - public interface GetConfiguredStreams { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, java.lang.foreign.MemoryAddress _x2); - static MemorySegment allocate(GetConfiguredStreams fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetConfiguredStreams.class, fi, IOUSBInterfaceStruct942.GetConfiguredStreams$FUNC, session); - } - static GetConfiguredStreams ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, java.lang.foreign.MemoryAddress __x2) -> { - try { - return (int)IOUSBInterfaceStruct942.GetConfiguredStreams$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetConfiguredStreams$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetConfiguredStreams")); - public static VarHandle GetConfiguredStreams$VH() { - return IOUSBInterfaceStruct942.GetConfiguredStreams$VH; - } - public static MemoryAddress GetConfiguredStreams$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetConfiguredStreams$VH.get(seg); - } - public static void GetConfiguredStreams$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetConfiguredStreams$VH.set(seg, x); - } - public static MemoryAddress GetConfiguredStreams$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetConfiguredStreams$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetConfiguredStreams$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetConfiguredStreams$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetConfiguredStreams GetConfiguredStreams (MemorySegment segment, MemorySession session) { - return GetConfiguredStreams.ofAddress(GetConfiguredStreams$get(segment), session); - } - static final FunctionDescriptor ReadStreamsPipeTO$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT - ); - static final MethodHandle ReadStreamsPipeTO$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.ReadStreamsPipeTO$FUNC - ); - public interface ReadStreamsPipeTO { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, int _x2, java.lang.foreign.MemoryAddress _x3, java.lang.foreign.MemoryAddress _x4, int _x5, int _x6); - static MemorySegment allocate(ReadStreamsPipeTO fi, MemorySession session) { - return RuntimeHelper.upcallStub(ReadStreamsPipeTO.class, fi, IOUSBInterfaceStruct942.ReadStreamsPipeTO$FUNC, session); - } - static ReadStreamsPipeTO ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, int __x2, java.lang.foreign.MemoryAddress __x3, java.lang.foreign.MemoryAddress __x4, int __x5, int __x6) -> { - try { - return (int)IOUSBInterfaceStruct942.ReadStreamsPipeTO$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, __x2, (java.lang.foreign.Addressable)__x3, (java.lang.foreign.Addressable)__x4, __x5, __x6); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle ReadStreamsPipeTO$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("ReadStreamsPipeTO")); - public static VarHandle ReadStreamsPipeTO$VH() { - return IOUSBInterfaceStruct942.ReadStreamsPipeTO$VH; - } - public static MemoryAddress ReadStreamsPipeTO$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ReadStreamsPipeTO$VH.get(seg); - } - public static void ReadStreamsPipeTO$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.ReadStreamsPipeTO$VH.set(seg, x); - } - public static MemoryAddress ReadStreamsPipeTO$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ReadStreamsPipeTO$VH.get(seg.asSlice(index*sizeof())); - } - public static void ReadStreamsPipeTO$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.ReadStreamsPipeTO$VH.set(seg.asSlice(index*sizeof()), x); - } - public static ReadStreamsPipeTO ReadStreamsPipeTO (MemorySegment segment, MemorySession session) { - return ReadStreamsPipeTO.ofAddress(ReadStreamsPipeTO$get(segment), session); - } - static final FunctionDescriptor WriteStreamsPipeTO$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT - ); - static final MethodHandle WriteStreamsPipeTO$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.WriteStreamsPipeTO$FUNC - ); - public interface WriteStreamsPipeTO { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, int _x2, java.lang.foreign.MemoryAddress _x3, int _x4, int _x5, int _x6); - static MemorySegment allocate(WriteStreamsPipeTO fi, MemorySession session) { - return RuntimeHelper.upcallStub(WriteStreamsPipeTO.class, fi, IOUSBInterfaceStruct942.WriteStreamsPipeTO$FUNC, session); - } - static WriteStreamsPipeTO ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, int __x2, java.lang.foreign.MemoryAddress __x3, int __x4, int __x5, int __x6) -> { - try { - return (int)IOUSBInterfaceStruct942.WriteStreamsPipeTO$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, __x2, (java.lang.foreign.Addressable)__x3, __x4, __x5, __x6); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle WriteStreamsPipeTO$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("WriteStreamsPipeTO")); - public static VarHandle WriteStreamsPipeTO$VH() { - return IOUSBInterfaceStruct942.WriteStreamsPipeTO$VH; - } - public static MemoryAddress WriteStreamsPipeTO$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.WriteStreamsPipeTO$VH.get(seg); - } - public static void WriteStreamsPipeTO$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.WriteStreamsPipeTO$VH.set(seg, x); - } - public static MemoryAddress WriteStreamsPipeTO$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.WriteStreamsPipeTO$VH.get(seg.asSlice(index*sizeof())); - } - public static void WriteStreamsPipeTO$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.WriteStreamsPipeTO$VH.set(seg.asSlice(index*sizeof()), x); - } - public static WriteStreamsPipeTO WriteStreamsPipeTO (MemorySegment segment, MemorySession session) { - return WriteStreamsPipeTO.ofAddress(WriteStreamsPipeTO$get(segment), session); - } - static final FunctionDescriptor ReadStreamsPipeAsyncTO$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle ReadStreamsPipeAsyncTO$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.ReadStreamsPipeAsyncTO$FUNC - ); - public interface ReadStreamsPipeAsyncTO { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, int _x2, java.lang.foreign.MemoryAddress _x3, int _x4, int _x5, int _x6, java.lang.foreign.MemoryAddress _x7, java.lang.foreign.MemoryAddress _x8); - static MemorySegment allocate(ReadStreamsPipeAsyncTO fi, MemorySession session) { - return RuntimeHelper.upcallStub(ReadStreamsPipeAsyncTO.class, fi, IOUSBInterfaceStruct942.ReadStreamsPipeAsyncTO$FUNC, session); - } - static ReadStreamsPipeAsyncTO ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, int __x2, java.lang.foreign.MemoryAddress __x3, int __x4, int __x5, int __x6, java.lang.foreign.MemoryAddress __x7, java.lang.foreign.MemoryAddress __x8) -> { - try { - return (int)IOUSBInterfaceStruct942.ReadStreamsPipeAsyncTO$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, __x2, (java.lang.foreign.Addressable)__x3, __x4, __x5, __x6, (java.lang.foreign.Addressable)__x7, (java.lang.foreign.Addressable)__x8); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle ReadStreamsPipeAsyncTO$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("ReadStreamsPipeAsyncTO")); - public static VarHandle ReadStreamsPipeAsyncTO$VH() { - return IOUSBInterfaceStruct942.ReadStreamsPipeAsyncTO$VH; - } - public static MemoryAddress ReadStreamsPipeAsyncTO$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ReadStreamsPipeAsyncTO$VH.get(seg); - } - public static void ReadStreamsPipeAsyncTO$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.ReadStreamsPipeAsyncTO$VH.set(seg, x); - } - public static MemoryAddress ReadStreamsPipeAsyncTO$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.ReadStreamsPipeAsyncTO$VH.get(seg.asSlice(index*sizeof())); - } - public static void ReadStreamsPipeAsyncTO$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.ReadStreamsPipeAsyncTO$VH.set(seg.asSlice(index*sizeof()), x); - } - public static ReadStreamsPipeAsyncTO ReadStreamsPipeAsyncTO (MemorySegment segment, MemorySession session) { - return ReadStreamsPipeAsyncTO.ofAddress(ReadStreamsPipeAsyncTO$get(segment), session); - } - static final FunctionDescriptor WriteStreamsPipeAsyncTO$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle WriteStreamsPipeAsyncTO$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.WriteStreamsPipeAsyncTO$FUNC - ); - public interface WriteStreamsPipeAsyncTO { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, int _x2, java.lang.foreign.MemoryAddress _x3, int _x4, int _x5, int _x6, java.lang.foreign.MemoryAddress _x7, java.lang.foreign.MemoryAddress _x8); - static MemorySegment allocate(WriteStreamsPipeAsyncTO fi, MemorySession session) { - return RuntimeHelper.upcallStub(WriteStreamsPipeAsyncTO.class, fi, IOUSBInterfaceStruct942.WriteStreamsPipeAsyncTO$FUNC, session); - } - static WriteStreamsPipeAsyncTO ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, int __x2, java.lang.foreign.MemoryAddress __x3, int __x4, int __x5, int __x6, java.lang.foreign.MemoryAddress __x7, java.lang.foreign.MemoryAddress __x8) -> { - try { - return (int)IOUSBInterfaceStruct942.WriteStreamsPipeAsyncTO$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, __x2, (java.lang.foreign.Addressable)__x3, __x4, __x5, __x6, (java.lang.foreign.Addressable)__x7, (java.lang.foreign.Addressable)__x8); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle WriteStreamsPipeAsyncTO$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("WriteStreamsPipeAsyncTO")); - public static VarHandle WriteStreamsPipeAsyncTO$VH() { - return IOUSBInterfaceStruct942.WriteStreamsPipeAsyncTO$VH; - } - public static MemoryAddress WriteStreamsPipeAsyncTO$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.WriteStreamsPipeAsyncTO$VH.get(seg); - } - public static void WriteStreamsPipeAsyncTO$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.WriteStreamsPipeAsyncTO$VH.set(seg, x); - } - public static MemoryAddress WriteStreamsPipeAsyncTO$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.WriteStreamsPipeAsyncTO$VH.get(seg.asSlice(index*sizeof())); - } - public static void WriteStreamsPipeAsyncTO$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.WriteStreamsPipeAsyncTO$VH.set(seg.asSlice(index*sizeof()), x); - } - public static WriteStreamsPipeAsyncTO WriteStreamsPipeAsyncTO (MemorySegment segment, MemorySession session) { - return WriteStreamsPipeAsyncTO.ofAddress(WriteStreamsPipeAsyncTO$get(segment), session); - } - static final FunctionDescriptor AbortStreamsPipe$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_INT$LAYOUT - ); - static final MethodHandle AbortStreamsPipe$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.AbortStreamsPipe$FUNC - ); - public interface AbortStreamsPipe { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, int _x2); - static MemorySegment allocate(AbortStreamsPipe fi, MemorySession session) { - return RuntimeHelper.upcallStub(AbortStreamsPipe.class, fi, IOUSBInterfaceStruct942.AbortStreamsPipe$FUNC, session); - } - static AbortStreamsPipe ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, int __x2) -> { - try { - return (int)IOUSBInterfaceStruct942.AbortStreamsPipe$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, __x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle AbortStreamsPipe$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("AbortStreamsPipe")); - public static VarHandle AbortStreamsPipe$VH() { - return IOUSBInterfaceStruct942.AbortStreamsPipe$VH; - } - public static MemoryAddress AbortStreamsPipe$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.AbortStreamsPipe$VH.get(seg); - } - public static void AbortStreamsPipe$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.AbortStreamsPipe$VH.set(seg, x); - } - public static MemoryAddress AbortStreamsPipe$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.AbortStreamsPipe$VH.get(seg.asSlice(index*sizeof())); - } - public static void AbortStreamsPipe$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.AbortStreamsPipe$VH.set(seg.asSlice(index*sizeof()), x); - } - public static AbortStreamsPipe AbortStreamsPipe (MemorySegment segment, MemorySession session) { - return AbortStreamsPipe.ofAddress(AbortStreamsPipe$get(segment), session); - } - static final FunctionDescriptor RegisterForNotification$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle RegisterForNotification$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.RegisterForNotification$FUNC - ); - public interface RegisterForNotification { - - int apply(java.lang.foreign.MemoryAddress _x0, long _x1, java.lang.foreign.MemoryAddress _x2, java.lang.foreign.MemoryAddress _x3, java.lang.foreign.MemoryAddress _x4); - static MemorySegment allocate(RegisterForNotification fi, MemorySession session) { - return RuntimeHelper.upcallStub(RegisterForNotification.class, fi, IOUSBInterfaceStruct942.RegisterForNotification$FUNC, session); - } - static RegisterForNotification ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, long __x1, java.lang.foreign.MemoryAddress __x2, java.lang.foreign.MemoryAddress __x3, java.lang.foreign.MemoryAddress __x4) -> { - try { - return (int)IOUSBInterfaceStruct942.RegisterForNotification$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, (java.lang.foreign.Addressable)__x2, (java.lang.foreign.Addressable)__x3, (java.lang.foreign.Addressable)__x4); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle RegisterForNotification$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("RegisterForNotification")); - public static VarHandle RegisterForNotification$VH() { - return IOUSBInterfaceStruct942.RegisterForNotification$VH; - } - public static MemoryAddress RegisterForNotification$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.RegisterForNotification$VH.get(seg); - } - public static void RegisterForNotification$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.RegisterForNotification$VH.set(seg, x); - } - public static MemoryAddress RegisterForNotification$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.RegisterForNotification$VH.get(seg.asSlice(index*sizeof())); - } - public static void RegisterForNotification$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.RegisterForNotification$VH.set(seg.asSlice(index*sizeof()), x); - } - public static RegisterForNotification RegisterForNotification (MemorySegment segment, MemorySession session) { - return RegisterForNotification.ofAddress(RegisterForNotification$get(segment), session); - } - static final FunctionDescriptor UnregisterNotification$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG_LONG$LAYOUT - ); - static final MethodHandle UnregisterNotification$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.UnregisterNotification$FUNC - ); - public interface UnregisterNotification { - - int apply(java.lang.foreign.MemoryAddress _x0, long _x1); - static MemorySegment allocate(UnregisterNotification fi, MemorySession session) { - return RuntimeHelper.upcallStub(UnregisterNotification.class, fi, IOUSBInterfaceStruct942.UnregisterNotification$FUNC, session); - } - static UnregisterNotification ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, long __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.UnregisterNotification$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle UnregisterNotification$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("UnregisterNotification")); - public static VarHandle UnregisterNotification$VH() { - return IOUSBInterfaceStruct942.UnregisterNotification$VH; - } - public static MemoryAddress UnregisterNotification$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.UnregisterNotification$VH.get(seg); - } - public static void UnregisterNotification$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.UnregisterNotification$VH.set(seg, x); - } - public static MemoryAddress UnregisterNotification$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.UnregisterNotification$VH.get(seg.asSlice(index*sizeof())); - } - public static void UnregisterNotification$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.UnregisterNotification$VH.set(seg.asSlice(index*sizeof()), x); - } - public static UnregisterNotification UnregisterNotification (MemorySegment segment, MemorySession session) { - return UnregisterNotification.ofAddress(UnregisterNotification$get(segment), session); - } - static final FunctionDescriptor AcknowledgeNotification$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG_LONG$LAYOUT - ); - static final MethodHandle AcknowledgeNotification$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.AcknowledgeNotification$FUNC - ); - public interface AcknowledgeNotification { - - int apply(java.lang.foreign.MemoryAddress _x0, long _x1); - static MemorySegment allocate(AcknowledgeNotification fi, MemorySession session) { - return RuntimeHelper.upcallStub(AcknowledgeNotification.class, fi, IOUSBInterfaceStruct942.AcknowledgeNotification$FUNC, session); - } - static AcknowledgeNotification ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, long __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.AcknowledgeNotification$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle AcknowledgeNotification$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("AcknowledgeNotification")); - public static VarHandle AcknowledgeNotification$VH() { - return IOUSBInterfaceStruct942.AcknowledgeNotification$VH; - } - public static MemoryAddress AcknowledgeNotification$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.AcknowledgeNotification$VH.get(seg); - } - public static void AcknowledgeNotification$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.AcknowledgeNotification$VH.set(seg, x); - } - public static MemoryAddress AcknowledgeNotification$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.AcknowledgeNotification$VH.get(seg.asSlice(index*sizeof())); - } - public static void AcknowledgeNotification$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.AcknowledgeNotification$VH.set(seg.asSlice(index*sizeof()), x); - } - public static AcknowledgeNotification AcknowledgeNotification (MemorySegment segment, MemorySession session) { - return AcknowledgeNotification.ofAddress(AcknowledgeNotification$get(segment), session); - } - static final FunctionDescriptor RegisterDriver$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle RegisterDriver$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.RegisterDriver$FUNC - ); - public interface RegisterDriver { - - int apply(java.lang.foreign.MemoryAddress _x0); - static MemorySegment allocate(RegisterDriver fi, MemorySession session) { - return RuntimeHelper.upcallStub(RegisterDriver.class, fi, IOUSBInterfaceStruct942.RegisterDriver$FUNC, session); - } - static RegisterDriver ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0) -> { - try { - return (int)IOUSBInterfaceStruct942.RegisterDriver$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle RegisterDriver$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("RegisterDriver")); - public static VarHandle RegisterDriver$VH() { - return IOUSBInterfaceStruct942.RegisterDriver$VH; - } - public static MemoryAddress RegisterDriver$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.RegisterDriver$VH.get(seg); - } - public static void RegisterDriver$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.RegisterDriver$VH.set(seg, x); - } - public static MemoryAddress RegisterDriver$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.RegisterDriver$VH.get(seg.asSlice(index*sizeof())); - } - public static void RegisterDriver$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.RegisterDriver$VH.set(seg.asSlice(index*sizeof()), x); - } - public static RegisterDriver RegisterDriver (MemorySegment segment, MemorySession session) { - return RegisterDriver.ofAddress(RegisterDriver$get(segment), session); - } - static final FunctionDescriptor SetDeviceIdlePolicy$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT - ); - static final MethodHandle SetDeviceIdlePolicy$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.SetDeviceIdlePolicy$FUNC - ); - public interface SetDeviceIdlePolicy { - - int apply(java.lang.foreign.MemoryAddress _x0, int _x1); - static MemorySegment allocate(SetDeviceIdlePolicy fi, MemorySession session) { - return RuntimeHelper.upcallStub(SetDeviceIdlePolicy.class, fi, IOUSBInterfaceStruct942.SetDeviceIdlePolicy$FUNC, session); - } - static SetDeviceIdlePolicy ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, int __x1) -> { - try { - return (int)IOUSBInterfaceStruct942.SetDeviceIdlePolicy$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle SetDeviceIdlePolicy$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("SetDeviceIdlePolicy")); - public static VarHandle SetDeviceIdlePolicy$VH() { - return IOUSBInterfaceStruct942.SetDeviceIdlePolicy$VH; - } - public static MemoryAddress SetDeviceIdlePolicy$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.SetDeviceIdlePolicy$VH.get(seg); - } - public static void SetDeviceIdlePolicy$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.SetDeviceIdlePolicy$VH.set(seg, x); - } - public static MemoryAddress SetDeviceIdlePolicy$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.SetDeviceIdlePolicy$VH.get(seg.asSlice(index*sizeof())); - } - public static void SetDeviceIdlePolicy$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.SetDeviceIdlePolicy$VH.set(seg.asSlice(index*sizeof()), x); - } - public static SetDeviceIdlePolicy SetDeviceIdlePolicy (MemorySegment segment, MemorySession session) { - return SetDeviceIdlePolicy.ofAddress(SetDeviceIdlePolicy$get(segment), session); - } - static final FunctionDescriptor SetPipeIdlePolicy$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_INT$LAYOUT - ); - static final MethodHandle SetPipeIdlePolicy$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.SetPipeIdlePolicy$FUNC - ); - public interface SetPipeIdlePolicy { - - int apply(java.lang.foreign.MemoryAddress _x0, byte _x1, int _x2); - static MemorySegment allocate(SetPipeIdlePolicy fi, MemorySession session) { - return RuntimeHelper.upcallStub(SetPipeIdlePolicy.class, fi, IOUSBInterfaceStruct942.SetPipeIdlePolicy$FUNC, session); - } - static SetPipeIdlePolicy ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0, byte __x1, int __x2) -> { - try { - return (int)IOUSBInterfaceStruct942.SetPipeIdlePolicy$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0, __x1, __x2); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle SetPipeIdlePolicy$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("SetPipeIdlePolicy")); - public static VarHandle SetPipeIdlePolicy$VH() { - return IOUSBInterfaceStruct942.SetPipeIdlePolicy$VH; - } - public static MemoryAddress SetPipeIdlePolicy$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.SetPipeIdlePolicy$VH.get(seg); - } - public static void SetPipeIdlePolicy$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.SetPipeIdlePolicy$VH.set(seg, x); - } - public static MemoryAddress SetPipeIdlePolicy$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.SetPipeIdlePolicy$VH.get(seg.asSlice(index*sizeof())); - } - public static void SetPipeIdlePolicy$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.SetPipeIdlePolicy$VH.set(seg.asSlice(index*sizeof()), x); - } - public static SetPipeIdlePolicy SetPipeIdlePolicy (MemorySegment segment, MemorySession session) { - return SetPipeIdlePolicy.ofAddress(SetPipeIdlePolicy$get(segment), session); - } - static final FunctionDescriptor GetInterfaceAsyncNotificationPort$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetInterfaceAsyncNotificationPort$MH = RuntimeHelper.downcallHandle( - IOUSBInterfaceStruct942.GetInterfaceAsyncNotificationPort$FUNC - ); - public interface GetInterfaceAsyncNotificationPort { - - java.lang.foreign.Addressable apply(java.lang.foreign.MemoryAddress _x0); - static MemorySegment allocate(GetInterfaceAsyncNotificationPort fi, MemorySession session) { - return RuntimeHelper.upcallStub(GetInterfaceAsyncNotificationPort.class, fi, IOUSBInterfaceStruct942.GetInterfaceAsyncNotificationPort$FUNC, session); - } - static GetInterfaceAsyncNotificationPort ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment symbol = MemorySegment.ofAddress(addr, 0, session); - return (java.lang.foreign.MemoryAddress __x0) -> { - try { - return (java.lang.foreign.Addressable)(java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetInterfaceAsyncNotificationPort$MH.invokeExact((Addressable)symbol, (java.lang.foreign.Addressable)__x0); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - }; - } - } - - static final VarHandle GetInterfaceAsyncNotificationPort$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("GetInterfaceAsyncNotificationPort")); - public static VarHandle GetInterfaceAsyncNotificationPort$VH() { - return IOUSBInterfaceStruct942.GetInterfaceAsyncNotificationPort$VH; - } - public static MemoryAddress GetInterfaceAsyncNotificationPort$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetInterfaceAsyncNotificationPort$VH.get(seg); - } - public static void GetInterfaceAsyncNotificationPort$set( MemorySegment seg, MemoryAddress x) { - IOUSBInterfaceStruct942.GetInterfaceAsyncNotificationPort$VH.set(seg, x); - } - public static MemoryAddress GetInterfaceAsyncNotificationPort$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)IOUSBInterfaceStruct942.GetInterfaceAsyncNotificationPort$VH.get(seg.asSlice(index*sizeof())); - } - public static void GetInterfaceAsyncNotificationPort$set(MemorySegment seg, long index, MemoryAddress x) { - IOUSBInterfaceStruct942.GetInterfaceAsyncNotificationPort$VH.set(seg.asSlice(index*sizeof()), x); - } - public static GetInterfaceAsyncNotificationPort GetInterfaceAsyncNotificationPort (MemorySegment segment, MemorySession session) { - return GetInterfaceAsyncNotificationPort.ofAddress(GetInterfaceAsyncNotificationPort$get(segment), session); - } - public static long sizeof() { return $LAYOUT().byteSize(); } - public static MemorySegment allocate(SegmentAllocator allocator) { return allocator.allocate($LAYOUT()); } - public static MemorySegment allocateArray(int len, SegmentAllocator allocator) { - return allocator.allocate(MemoryLayout.sequenceLayout(len, $LAYOUT())); - } - public static MemorySegment ofAddress(MemoryAddress addr, MemorySession session) { return RuntimeHelper.asArray(addr, $LAYOUT(), 1, session); } -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/RuntimeHelper.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/RuntimeHelper.java deleted file mode 100644 index 18defbd9..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/RuntimeHelper.java +++ /dev/null @@ -1,217 +0,0 @@ -package net.codecrete.usb.macos.gen.iokit; -// Generated by jextract - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import static java.lang.foreign.ValueLayout.*; - -final class RuntimeHelper { - - private RuntimeHelper() {} - private final static Linker LINKER = Linker.nativeLinker(); - private final static ClassLoader LOADER = RuntimeHelper.class.getClassLoader(); - private final static MethodHandles.Lookup MH_LOOKUP = MethodHandles.lookup(); - private final static SymbolLookup SYMBOL_LOOKUP; - - final static SegmentAllocator CONSTANT_ALLOCATOR = - (size, align) -> MemorySegment.allocateNative(size, align, MemorySession.openImplicit()); - - static { -// System.loadLibrary("IOKit.framework"); -// SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); - SymbolLookup loaderLookup = SymbolLookup.libraryLookup("IOKit.framework/IOKit", MemorySession.openImplicit()); - SYMBOL_LOOKUP = name -> loaderLookup.lookup(name).or(() -> LINKER.defaultLookup().lookup(name)); - } - - static T requireNonNull(T obj, String symbolName) { - if (obj == null) { - throw new UnsatisfiedLinkError("unresolved symbol: " + symbolName); - } - return obj; - } - - private final static SegmentAllocator THROWING_ALLOCATOR = (x, y) -> { throw new AssertionError("should not reach here"); }; - - static final MemorySegment lookupGlobalVariable(String name, MemoryLayout layout) { - return SYMBOL_LOOKUP.lookup(name).map(symbol -> MemorySegment.ofAddress(symbol.address(), layout.byteSize(), MemorySession.openShared())).orElse(null); - } - - static final MethodHandle downcallHandle(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> LINKER.downcallHandle(addr, fdesc)). - orElse(null); - } - - static final MethodHandle downcallHandle(FunctionDescriptor fdesc) { - return LINKER.downcallHandle(fdesc); - } - - static final MethodHandle downcallHandleVariadic(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> VarargsInvoker.make(addr, fdesc)). - orElse(null); - } - - static final MemorySegment upcallStub(Class fi, Z z, FunctionDescriptor fdesc, MemorySession session) { - try { - MethodHandle handle = MH_LOOKUP.findVirtual(fi, "apply", Linker.upcallType(fdesc)); - handle = handle.bindTo(z); - return LINKER.upcallStub(handle, fdesc, session); - } catch (Throwable ex) { - throw new AssertionError(ex); - } - } - - static MemorySegment asArray(MemoryAddress addr, MemoryLayout layout, int numElements, MemorySession session) { - return MemorySegment.ofAddress(addr, numElements * layout.byteSize(), session); - } - - // Internals only below this point - - private static class VarargsInvoker { - private static final MethodHandle INVOKE_MH; - private final MemorySegment symbol; - private final FunctionDescriptor function; - - private VarargsInvoker(MemorySegment symbol, FunctionDescriptor function) { - this.symbol = symbol; - this.function = function; - } - - static { - try { - INVOKE_MH = MethodHandles.lookup().findVirtual(VarargsInvoker.class, "invoke", MethodType.methodType(Object.class, SegmentAllocator.class, Object[].class)); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - static MethodHandle make(MemorySegment symbol, FunctionDescriptor function) { - VarargsInvoker invoker = new VarargsInvoker(symbol, function); - MethodHandle handle = INVOKE_MH.bindTo(invoker).asCollector(Object[].class, function.argumentLayouts().size() + 1); - MethodType mtype = MethodType.methodType(function.returnLayout().isPresent() ? carrier(function.returnLayout().get(), true) : void.class); - for (MemoryLayout layout : function.argumentLayouts()) { - mtype = mtype.appendParameterTypes(carrier(layout, false)); - } - mtype = mtype.appendParameterTypes(Object[].class); - if (mtype.returnType().equals(MemorySegment.class)) { - mtype = mtype.insertParameterTypes(0, SegmentAllocator.class); - } else { - handle = MethodHandles.insertArguments(handle, 0, THROWING_ALLOCATOR); - } - return handle.asType(mtype); - } - - static Class carrier(MemoryLayout layout, boolean ret) { - if (layout instanceof ValueLayout valueLayout) { - return (ret || valueLayout.carrier() != MemoryAddress.class) ? - valueLayout.carrier() : Addressable.class; - } else if (layout instanceof GroupLayout) { - return MemorySegment.class; - } else { - throw new AssertionError("Cannot get here!"); - } - } - - private Object invoke(SegmentAllocator allocator, Object[] args) throws Throwable { - // one trailing Object[] - int nNamedArgs = function.argumentLayouts().size(); - assert(args.length == nNamedArgs + 1); - // The last argument is the array of vararg collector - Object[] unnamedArgs = (Object[]) args[args.length - 1]; - - int argsCount = nNamedArgs + unnamedArgs.length; - Class[] argTypes = new Class[argsCount]; - MemoryLayout[] argLayouts = new MemoryLayout[nNamedArgs + unnamedArgs.length]; - - int pos = 0; - for (pos = 0; pos < nNamedArgs; pos++) { - argLayouts[pos] = function.argumentLayouts().get(pos); - } - - assert pos == nNamedArgs; - for (Object o: unnamedArgs) { - argLayouts[pos] = variadicLayout(normalize(o.getClass())); - pos++; - } - assert pos == argsCount; - - FunctionDescriptor f = (function.returnLayout().isEmpty()) ? - FunctionDescriptor.ofVoid(argLayouts) : - FunctionDescriptor.of(function.returnLayout().get(), argLayouts); - MethodHandle mh = LINKER.downcallHandle(symbol, f); - if (mh.type().returnType() == MemorySegment.class) { - mh = mh.bindTo(allocator); - } - // flatten argument list so that it can be passed to an asSpreader MH - Object[] allArgs = new Object[nNamedArgs + unnamedArgs.length]; - System.arraycopy(args, 0, allArgs, 0, nNamedArgs); - System.arraycopy(unnamedArgs, 0, allArgs, nNamedArgs, unnamedArgs.length); - - return mh.asSpreader(Object[].class, argsCount).invoke(allArgs); - } - - private static Class unboxIfNeeded(Class clazz) { - if (clazz == Boolean.class) { - return boolean.class; - } else if (clazz == Void.class) { - return void.class; - } else if (clazz == Byte.class) { - return byte.class; - } else if (clazz == Character.class) { - return char.class; - } else if (clazz == Short.class) { - return short.class; - } else if (clazz == Integer.class) { - return int.class; - } else if (clazz == Long.class) { - return long.class; - } else if (clazz == Float.class) { - return float.class; - } else if (clazz == Double.class) { - return double.class; - } else { - return clazz; - } - } - - private Class promote(Class c) { - if (c == byte.class || c == char.class || c == short.class || c == int.class) { - return long.class; - } else if (c == float.class) { - return double.class; - } else { - return c; - } - } - - private Class normalize(Class c) { - c = unboxIfNeeded(c); - if (c.isPrimitive()) { - return promote(c); - } - if (MemoryAddress.class.isAssignableFrom(c)) { - return MemoryAddress.class; - } - if (MemorySegment.class.isAssignableFrom(c)) { - return MemorySegment.class; - } - throw new IllegalArgumentException("Invalid type for ABI: " + c.getTypeName()); - } - - private MemoryLayout variadicLayout(Class c) { - if (c == long.class) { - return JAVA_LONG; - } else if (c == double.class) { - return JAVA_DOUBLE; - } else if (MemoryAddress.class.isAssignableFrom(c)) { - return ADDRESS; - } else { - throw new IllegalArgumentException("Unhandled variadic argument class: " + c); - } - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/constants$0.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/constants$0.java deleted file mode 100644 index 75a6b3ae..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/constants$0.java +++ /dev/null @@ -1,50 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.macos.gen.iokit; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemorySegment; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.VarHandle; - -import static java.lang.foreign.ValueLayout.OfAddress; -import static java.lang.foreign.ValueLayout.OfInt; -class constants$0 { - - static final OfAddress kCFRunLoopDefaultMode$LAYOUT = Constants$root.C_POINTER$LAYOUT; - static final VarHandle kCFRunLoopDefaultMode$VH = constants$0.kCFRunLoopDefaultMode$LAYOUT.varHandle(); - static final MemorySegment kCFRunLoopDefaultMode$SEGMENT = RuntimeHelper.lookupGlobalVariable("kCFRunLoopDefaultMode", constants$0.kCFRunLoopDefaultMode$LAYOUT); - static final OfInt kIOMasterPortDefault$LAYOUT = Constants$root.C_INT$LAYOUT; - static final VarHandle kIOMasterPortDefault$VH = constants$0.kIOMasterPortDefault$LAYOUT.varHandle(); - static final MemorySegment kIOMasterPortDefault$SEGMENT = RuntimeHelper.lookupGlobalVariable("kIOMasterPortDefault", constants$0.kIOMasterPortDefault$LAYOUT); - static final FunctionDescriptor IONotificationPortCreate$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT - ); - static final MethodHandle IONotificationPortCreate$MH = RuntimeHelper.downcallHandle( - "IONotificationPortCreate", - constants$0.IONotificationPortCreate$FUNC - ); - static final FunctionDescriptor IONotificationPortGetRunLoopSource$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle IONotificationPortGetRunLoopSource$MH = RuntimeHelper.downcallHandle( - "IONotificationPortGetRunLoopSource", - constants$0.IONotificationPortGetRunLoopSource$FUNC - ); - static final FunctionDescriptor IOObjectRelease$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT - ); - static final MethodHandle IOObjectRelease$MH = RuntimeHelper.downcallHandle( - "IOObjectRelease", - constants$0.IOObjectRelease$FUNC - ); - static final FunctionDescriptor IOIteratorNext$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT - ); - static final MethodHandle IOIteratorNext$MH = RuntimeHelper.downcallHandle( - "IOIteratorNext", - constants$0.IOIteratorNext$FUNC - ); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/constants$1.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/constants$1.java deleted file mode 100644 index f6bb1dbf..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/constants$1.java +++ /dev/null @@ -1,61 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.macos.gen.iokit; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemorySegment; -import java.lang.invoke.MethodHandle; -class constants$1 { - - static final FunctionDescriptor IOServiceAddMatchingNotification$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle IOServiceAddMatchingNotification$MH = RuntimeHelper.downcallHandle( - "IOServiceAddMatchingNotification", - constants$1.IOServiceAddMatchingNotification$FUNC - ); - static final FunctionDescriptor IORegistryEntryGetRegistryEntryID$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle IORegistryEntryGetRegistryEntryID$MH = RuntimeHelper.downcallHandle( - "IORegistryEntryGetRegistryEntryID", - constants$1.IORegistryEntryGetRegistryEntryID$FUNC - ); - static final FunctionDescriptor IORegistryEntryCreateCFProperty$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT - ); - static final MethodHandle IORegistryEntryCreateCFProperty$MH = RuntimeHelper.downcallHandle( - "IORegistryEntryCreateCFProperty", - constants$1.IORegistryEntryCreateCFProperty$FUNC - ); - static final FunctionDescriptor IOServiceMatching$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle IOServiceMatching$MH = RuntimeHelper.downcallHandle( - "IOServiceMatching", - constants$1.IOServiceMatching$FUNC - ); - static final FunctionDescriptor IOCreatePlugInInterfaceForService$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT, - Constants$root.C_INT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle IOCreatePlugInInterfaceForService$MH = RuntimeHelper.downcallHandle( - "IOCreatePlugInInterfaceForService", - constants$1.IOCreatePlugInInterfaceForService$FUNC - ); - static final MemorySegment kIOFirstMatchNotification$SEGMENT = RuntimeHelper.CONSTANT_ALLOCATOR.allocateUtf8String("IOServiceFirstMatch"); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/constants$2.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/constants$2.java deleted file mode 100644 index c82ad78e..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/iokit/constants$2.java +++ /dev/null @@ -1,12 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.macos.gen.iokit; - -import java.lang.foreign.MemorySegment; -class constants$2 { - - static final MemorySegment kIOTerminatedNotification$SEGMENT = RuntimeHelper.CONSTANT_ALLOCATOR.allocateUtf8String("IOServiceTerminate"); - static final MemorySegment kIOUSBDeviceClassName$SEGMENT = RuntimeHelper.CONSTANT_ALLOCATOR.allocateUtf8String("IOUSBDevice"); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/mach/Constants$root.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/mach/Constants$root.java deleted file mode 100644 index 7ac0bc7e..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/mach/Constants$root.java +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.macos.gen.mach; - -import static java.lang.foreign.ValueLayout.*; -public class Constants$root { - - static final OfBoolean C_BOOL$LAYOUT = JAVA_BOOLEAN; - static final OfByte C_CHAR$LAYOUT = JAVA_BYTE; - static final OfShort C_SHORT$LAYOUT = JAVA_SHORT.withBitAlignment(16); - static final OfInt C_INT$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfLong C_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfLong C_LONG_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfFloat C_FLOAT$LAYOUT = JAVA_FLOAT.withBitAlignment(32); - static final OfDouble C_DOUBLE$LAYOUT = JAVA_DOUBLE.withBitAlignment(64); - static final OfAddress C_POINTER$LAYOUT = ADDRESS.withBitAlignment(64); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/mach/RuntimeHelper.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/mach/RuntimeHelper.java deleted file mode 100644 index cb43e170..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/mach/RuntimeHelper.java +++ /dev/null @@ -1,216 +0,0 @@ -package net.codecrete.usb.macos.gen.mach; -// Generated by jextract - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import static java.lang.foreign.ValueLayout.*; - -final class RuntimeHelper { - - private RuntimeHelper() {} - private final static Linker LINKER = Linker.nativeLinker(); - private final static ClassLoader LOADER = RuntimeHelper.class.getClassLoader(); - private final static MethodHandles.Lookup MH_LOOKUP = MethodHandles.lookup(); - private final static SymbolLookup SYMBOL_LOOKUP; - - final static SegmentAllocator CONSTANT_ALLOCATOR = - (size, align) -> MemorySegment.allocateNative(size, align, MemorySession.openImplicit()); - - static { - - SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); - SYMBOL_LOOKUP = name -> loaderLookup.lookup(name).or(() -> LINKER.defaultLookup().lookup(name)); - } - - static T requireNonNull(T obj, String symbolName) { - if (obj == null) { - throw new UnsatisfiedLinkError("unresolved symbol: " + symbolName); - } - return obj; - } - - private final static SegmentAllocator THROWING_ALLOCATOR = (x, y) -> { throw new AssertionError("should not reach here"); }; - - static final MemorySegment lookupGlobalVariable(String name, MemoryLayout layout) { - return SYMBOL_LOOKUP.lookup(name).map(symbol -> MemorySegment.ofAddress(symbol.address(), layout.byteSize(), MemorySession.openShared())).orElse(null); - } - - static final MethodHandle downcallHandle(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> LINKER.downcallHandle(addr, fdesc)). - orElse(null); - } - - static final MethodHandle downcallHandle(FunctionDescriptor fdesc) { - return LINKER.downcallHandle(fdesc); - } - - static final MethodHandle downcallHandleVariadic(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> VarargsInvoker.make(addr, fdesc)). - orElse(null); - } - - static final MemorySegment upcallStub(Class fi, Z z, FunctionDescriptor fdesc, MemorySession session) { - try { - MethodHandle handle = MH_LOOKUP.findVirtual(fi, "apply", Linker.upcallType(fdesc)); - handle = handle.bindTo(z); - return LINKER.upcallStub(handle, fdesc, session); - } catch (Throwable ex) { - throw new AssertionError(ex); - } - } - - static MemorySegment asArray(MemoryAddress addr, MemoryLayout layout, int numElements, MemorySession session) { - return MemorySegment.ofAddress(addr, numElements * layout.byteSize(), session); - } - - // Internals only below this point - - private static class VarargsInvoker { - private static final MethodHandle INVOKE_MH; - private final MemorySegment symbol; - private final FunctionDescriptor function; - - private VarargsInvoker(MemorySegment symbol, FunctionDescriptor function) { - this.symbol = symbol; - this.function = function; - } - - static { - try { - INVOKE_MH = MethodHandles.lookup().findVirtual(VarargsInvoker.class, "invoke", MethodType.methodType(Object.class, SegmentAllocator.class, Object[].class)); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - static MethodHandle make(MemorySegment symbol, FunctionDescriptor function) { - VarargsInvoker invoker = new VarargsInvoker(symbol, function); - MethodHandle handle = INVOKE_MH.bindTo(invoker).asCollector(Object[].class, function.argumentLayouts().size() + 1); - MethodType mtype = MethodType.methodType(function.returnLayout().isPresent() ? carrier(function.returnLayout().get(), true) : void.class); - for (MemoryLayout layout : function.argumentLayouts()) { - mtype = mtype.appendParameterTypes(carrier(layout, false)); - } - mtype = mtype.appendParameterTypes(Object[].class); - if (mtype.returnType().equals(MemorySegment.class)) { - mtype = mtype.insertParameterTypes(0, SegmentAllocator.class); - } else { - handle = MethodHandles.insertArguments(handle, 0, THROWING_ALLOCATOR); - } - return handle.asType(mtype); - } - - static Class carrier(MemoryLayout layout, boolean ret) { - if (layout instanceof ValueLayout valueLayout) { - return (ret || valueLayout.carrier() != MemoryAddress.class) ? - valueLayout.carrier() : Addressable.class; - } else if (layout instanceof GroupLayout) { - return MemorySegment.class; - } else { - throw new AssertionError("Cannot get here!"); - } - } - - private Object invoke(SegmentAllocator allocator, Object[] args) throws Throwable { - // one trailing Object[] - int nNamedArgs = function.argumentLayouts().size(); - assert(args.length == nNamedArgs + 1); - // The last argument is the array of vararg collector - Object[] unnamedArgs = (Object[]) args[args.length - 1]; - - int argsCount = nNamedArgs + unnamedArgs.length; - Class[] argTypes = new Class[argsCount]; - MemoryLayout[] argLayouts = new MemoryLayout[nNamedArgs + unnamedArgs.length]; - - int pos = 0; - for (pos = 0; pos < nNamedArgs; pos++) { - argLayouts[pos] = function.argumentLayouts().get(pos); - } - - assert pos == nNamedArgs; - for (Object o: unnamedArgs) { - argLayouts[pos] = variadicLayout(normalize(o.getClass())); - pos++; - } - assert pos == argsCount; - - FunctionDescriptor f = (function.returnLayout().isEmpty()) ? - FunctionDescriptor.ofVoid(argLayouts) : - FunctionDescriptor.of(function.returnLayout().get(), argLayouts); - MethodHandle mh = LINKER.downcallHandle(symbol, f); - if (mh.type().returnType() == MemorySegment.class) { - mh = mh.bindTo(allocator); - } - // flatten argument list so that it can be passed to an asSpreader MH - Object[] allArgs = new Object[nNamedArgs + unnamedArgs.length]; - System.arraycopy(args, 0, allArgs, 0, nNamedArgs); - System.arraycopy(unnamedArgs, 0, allArgs, nNamedArgs, unnamedArgs.length); - - return mh.asSpreader(Object[].class, argsCount).invoke(allArgs); - } - - private static Class unboxIfNeeded(Class clazz) { - if (clazz == Boolean.class) { - return boolean.class; - } else if (clazz == Void.class) { - return void.class; - } else if (clazz == Byte.class) { - return byte.class; - } else if (clazz == Character.class) { - return char.class; - } else if (clazz == Short.class) { - return short.class; - } else if (clazz == Integer.class) { - return int.class; - } else if (clazz == Long.class) { - return long.class; - } else if (clazz == Float.class) { - return float.class; - } else if (clazz == Double.class) { - return double.class; - } else { - return clazz; - } - } - - private Class promote(Class c) { - if (c == byte.class || c == char.class || c == short.class || c == int.class) { - return long.class; - } else if (c == float.class) { - return double.class; - } else { - return c; - } - } - - private Class normalize(Class c) { - c = unboxIfNeeded(c); - if (c.isPrimitive()) { - return promote(c); - } - if (MemoryAddress.class.isAssignableFrom(c)) { - return MemoryAddress.class; - } - if (MemorySegment.class.isAssignableFrom(c)) { - return MemorySegment.class; - } - throw new IllegalArgumentException("Invalid type for ABI: " + c.getTypeName()); - } - - private MemoryLayout variadicLayout(Class c) { - if (c == long.class) { - return JAVA_LONG; - } else if (c == double.class) { - return JAVA_DOUBLE; - } else if (MemoryAddress.class.isAssignableFrom(c)) { - return ADDRESS; - } else { - throw new IllegalArgumentException("Unhandled variadic argument class: " + c); - } - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/mach/constants$0.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/mach/constants$0.java deleted file mode 100644 index 00707d13..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/mach/constants$0.java +++ /dev/null @@ -1,18 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.macos.gen.mach; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.invoke.MethodHandle; -class constants$0 { - - static final FunctionDescriptor mach_error_string$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_INT$LAYOUT - ); - static final MethodHandle mach_error_string$MH = RuntimeHelper.downcallHandle( - "mach_error_string", - constants$0.mach_error_string$FUNC - ); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/mach/mach$shared.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/mach/mach$shared.java new file mode 100644 index 00000000..afa05d01 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/mach/mach$shared.java @@ -0,0 +1,63 @@ +// Generated by jextract + +package net.codecrete.usb.macos.gen.mach; + +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import static java.lang.foreign.ValueLayout.*; +import static java.lang.foreign.MemoryLayout.PathElement.*; + +public class mach$shared { + + mach$shared() { + // Should not be called directly + } + + public static final ValueLayout.OfBoolean C_BOOL = (ValueLayout.OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool"); + public static final ValueLayout.OfByte C_CHAR =(ValueLayout.OfByte)Linker.nativeLinker().canonicalLayouts().get("char"); + public static final ValueLayout.OfShort C_SHORT = (ValueLayout.OfShort) Linker.nativeLinker().canonicalLayouts().get("short"); + public static final ValueLayout.OfInt C_INT = (ValueLayout.OfInt) Linker.nativeLinker().canonicalLayouts().get("int"); + public static final ValueLayout.OfLong C_LONG_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long long"); + public static final ValueLayout.OfFloat C_FLOAT = (ValueLayout.OfFloat) Linker.nativeLinker().canonicalLayouts().get("float"); + public static final ValueLayout.OfDouble C_DOUBLE = (ValueLayout.OfDouble) Linker.nativeLinker().canonicalLayouts().get("double"); + public static final AddressLayout C_POINTER = ((AddressLayout) Linker.nativeLinker().canonicalLayouts().get("void*")) + .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, C_CHAR)); + public static final ValueLayout.OfLong C_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long"); + + static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + + static void traceDowncall(String name, Object... args) { + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("%s(%s)\n", name, traceArgs); + } + + static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { + try { + return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } + + static MemoryLayout align(MemoryLayout layout, long align) { + return switch (layout) { + case PaddingLayout p -> p; + case ValueLayout v -> v.withByteAlignment(align); + case GroupLayout g -> { + MemoryLayout[] alignedMembers = g.memberLayouts().stream() + .map(m -> align(m, align)).toArray(MemoryLayout[]::new); + yield g instanceof StructLayout ? + MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); + } + case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); + }; + } +} + diff --git a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/mach/mach.java b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/mach/mach.java index 6b42d952..80f92c11 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/mach/mach.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/macos/gen/mach/mach.java @@ -2,32 +2,86 @@ package net.codecrete.usb.macos.gen.mach; -import java.lang.foreign.MemoryAddress; -import java.lang.invoke.MethodHandle; +import java.lang.invoke.*; +import java.lang.foreign.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; import static java.lang.foreign.ValueLayout.*; -public class mach { - - /* package-private */ mach() {} - public static OfByte C_CHAR = Constants$root.C_CHAR$LAYOUT; - public static OfShort C_SHORT = Constants$root.C_SHORT$LAYOUT; - public static OfInt C_INT = Constants$root.C_INT$LAYOUT; - public static OfLong C_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfLong C_LONG_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfFloat C_FLOAT = Constants$root.C_FLOAT$LAYOUT; - public static OfDouble C_DOUBLE = Constants$root.C_DOUBLE$LAYOUT; - public static OfAddress C_POINTER = Constants$root.C_POINTER$LAYOUT; - public static MethodHandle mach_error_string$MH() { - return RuntimeHelper.requireNonNull(constants$0.mach_error_string$MH,"mach_error_string"); +import static java.lang.foreign.MemoryLayout.PathElement.*; + +public class mach extends mach$shared { + + mach() { + // Should not be called directly + } + + static final Arena LIBRARY_ARENA = Arena.ofAuto(); + + static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup() + .or(Linker.nativeLinker().defaultLookup()); + + + private static class mach_error_string { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + mach.C_POINTER, + mach.C_INT + ); + + public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("mach_error_string"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } - public static MemoryAddress mach_error_string ( int error_value) { - var mh$ = mach_error_string$MH(); + + /** + * Function descriptor for: + * {@snippet lang=c : + * char *mach_error_string(mach_error_t error_value) + * } + */ + public static FunctionDescriptor mach_error_string$descriptor() { + return mach_error_string.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * char *mach_error_string(mach_error_t error_value) + * } + */ + public static MethodHandle mach_error_string$handle() { + return mach_error_string.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * char *mach_error_string(mach_error_t error_value) + * } + */ + public static MemorySegment mach_error_string$address() { + return mach_error_string.ADDR; + } + + /** + * {@snippet lang=c : + * char *mach_error_string(mach_error_t error_value) + * } + */ + public static MemorySegment mach_error_string(int error_value) { + var mh$ = mach_error_string.HANDLE; try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(error_value); + if (TRACE_DOWNCALLS) { + traceDowncall("mach_error_string", error_value); + } + return (MemorySegment)mh$.invokeExact(error_value); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } } - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/ConfigurationDescriptor.java b/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/ConfigurationDescriptor.java new file mode 100644 index 00000000..5f651820 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/ConfigurationDescriptor.java @@ -0,0 +1,90 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.usbstandard; + +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemorySegment; + +import static java.lang.foreign.MemoryLayout.structLayout; +import static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static java.lang.foreign.ValueLayout.JAVA_SHORT_UNALIGNED; + +/** + * USB configuration descriptor + */ +@SuppressWarnings({"java:S115", "java:S125"}) +public class ConfigurationDescriptor { + + private final MemorySegment descriptor; + + public ConfigurationDescriptor(MemorySegment descriptor) { + this.descriptor = descriptor; + } + + public int descriptorType() { + return 0xff & descriptor.get(JAVA_BYTE, bDescriptorType$OFFSET); + } + + public int totalLength() { + return 0xffff & descriptor.get(JAVA_SHORT_UNALIGNED, wTotalLength$OFFSET); + } + + public int numInterfaces() { + return 0xff & descriptor.get(JAVA_BYTE, bNumInterfaces$OFFSET); + } + + public int configurationValue() { + return 0xff & descriptor.get(JAVA_BYTE, bConfigurationValue$OFFSET); + } + + public int iConfiguration() { + return 0xff & descriptor.get(JAVA_BYTE, iConfiguration$OFFSET); + } + + public int attributes() { + return 0xff & descriptor.get(JAVA_BYTE, bmAttributes$OFFSET); + } + + public int maxPower() { + return 0xff & descriptor.get(JAVA_BYTE, bMaxPower$OFFSET); + } + + + // struct USBConfigurationDescriptor { + // uint8_t bLength; + // uint8_t bDescriptorType; + // uint16_t wTotalLength; + // uint8_t bNumInterfaces; + // uint8_t bConfigurationValue; + // uint8_t iConfiguration; + // uint8_t bmAttributes; + // uint8_t bMaxPower; + // } __attribute__((packed)); + public static final GroupLayout LAYOUT = structLayout( + JAVA_BYTE.withName("bLength"), + JAVA_BYTE.withName("bDescriptorType"), + JAVA_SHORT_UNALIGNED.withName("wTotalLength"), + JAVA_BYTE.withName("bNumInterfaces"), + JAVA_BYTE.withName("bConfigurationValue"), + JAVA_BYTE.withName("iConfiguration"), + JAVA_BYTE.withName("bmAttributes"), + JAVA_BYTE.withName("bMaxPower") + ); + + private static final long bDescriptorType$OFFSET = 1; + private static final long wTotalLength$OFFSET = 2; + private static final long bNumInterfaces$OFFSET = 4; + private static final long bConfigurationValue$OFFSET = 5; + private static final long iConfiguration$OFFSET = 6; + private static final long bmAttributes$OFFSET = 7; + private static final long bMaxPower$OFFSET = 8; + + static { + assert LAYOUT.byteSize() == 9; + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/Constants.java b/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/Constants.java new file mode 100644 index 00000000..8868ea18 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/Constants.java @@ -0,0 +1,40 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.usbstandard; + +/** + * Memory layout of USB descriptors. + */ +public class Constants { + + private Constants() { + } + + public static final byte DEVICE_DESCRIPTOR_TYPE = 0x01; + public static final byte CONFIGURATION_DESCRIPTOR_TYPE = 0x02; + public static final byte STRING_DESCRIPTOR_TYPE = 0x03; + public static final byte INTERFACE_DESCRIPTOR_TYPE = 0x04; + public static final byte ENDPOINT_DESCRIPTOR_TYPE = 0x05; + public static final byte DEVICE_QUALIFIER_DESCRIPTOR_TYPE = 0x06; + public static final byte OTHER_SPEED_CONFIGURATION_DESCRIPTOR_TYPE = 0x07; + public static final byte INTERFACE_POWER_DESCRIPTOR_TYPE = 0x08; + public static final byte OTG_DESCRIPTOR_TYPE = 0x09; + public static final byte DEBUG_DESCRIPTOR_TYPE = 0x0a; + public static final byte INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE = 0x0b; + public static final byte BOS_DESCRIPTOR_TYPE = 0x0f; + public static final byte DEVICE_CAPABILITY_DESCRIPTOR_TYPE = 0x10; + public static final byte HID_DESCRIPTOR_TYPE = 0x21; + public static final byte CS_INTERFACE_DESCRIPTOR_TYPE = 0x24; + public static final byte CS_ENDPOINT_DESCRIPTOR_TYPE = 0x25; + public static final byte USB_20_HUB_DESCRIPTOR_TYPE = 0x29; + public static final byte USB_30_HUB_DESCRIPTOR_TYPE = 0x2a; + public static final byte SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR_TYPE = 0x30; + public static final byte SUPERSPEEDPLUS_ISOCH_ENDPOINT_COMPANION_DESCRIPTOR_TYPE = 0x31; + + public static final short DEFAULT_LANGUAGE = 0x0409; +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/DeviceDescriptor.java b/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/DeviceDescriptor.java new file mode 100644 index 00000000..d122711d --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/DeviceDescriptor.java @@ -0,0 +1,117 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.usbstandard; + +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemorySegment; + +import static java.lang.foreign.MemoryLayout.structLayout; +import static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static java.lang.foreign.ValueLayout.JAVA_SHORT_UNALIGNED; + +/** + * USB device descriptor + */ +@SuppressWarnings({"java:S115", "java:S125"}) +public class DeviceDescriptor { + + private final MemorySegment descriptor; + + public DeviceDescriptor(MemorySegment descriptor) { + this.descriptor = descriptor; + } + + public int usbVersion() { + return 0xffff & descriptor.get(JAVA_SHORT_UNALIGNED, bcdUSB$OFFSET); + } + + public int deviceClass() { + return 0xff & descriptor.get(JAVA_BYTE, bDeviceClass$OFFSET); + } + + public int deviceSubClass() { + return 0xff & descriptor.get(JAVA_BYTE, bDeviceSubClass$OFFSET); + } + + public int deviceProtocol() { + return 0xff & descriptor.get(JAVA_BYTE, bDeviceProtocol$OFFSET); + } + + public int vendorID() { + return 0xffff & descriptor.get(JAVA_SHORT_UNALIGNED, idVendor$OFFSET); + } + + public int productID() { + return 0xffff & descriptor.get(JAVA_SHORT_UNALIGNED, idProduct$OFFSET); + } + + public int deviceVersion() { + return 0xffff & descriptor.get(JAVA_SHORT_UNALIGNED, bcdDevice$OFFSET); + } + + public int iManufacturer() { + return 0xffff & descriptor.get(JAVA_SHORT_UNALIGNED, iManufacturer$OFFSET); + } + + public int iProduct() { + return 0xffff & descriptor.get(JAVA_SHORT_UNALIGNED, iProduct$OFFSET); + } + + public int iSerialNumber() { + return 0xffff & descriptor.get(JAVA_SHORT_UNALIGNED, iSerialNumber$OFFSET); + } + + // struct USBDeviceDescriptor { + // uint8_t bLength; + // uint8_t bDescriptorType; + // uint16_t bcdUSB; + // uint8_t bDeviceClass; + // uint8_t bDeviceSubClass; + // uint8_t bDeviceProtocol; + // uint8_t bMaxPacketSize0; + // uint16_t idVendor; + // uint16_t idProduct; + // uint16_t bcdDevice; + // uint8_t iManufacturer; + // uint8_t iProduct; + // uint8_t iSerialNumber; + // uint8_t bNumConfigurations; + // } __attribute__((packed)); + public static final GroupLayout LAYOUT = structLayout( + JAVA_BYTE.withName("bLength"), + JAVA_BYTE.withName("bDescriptorType"), + JAVA_SHORT_UNALIGNED.withName("bcdUSB"), + JAVA_BYTE.withName("bDeviceClass"), + JAVA_BYTE.withName("bDeviceSubClass"), + JAVA_BYTE.withName("bDeviceProtocol"), + JAVA_BYTE.withName("bMaxPacketSize0"), + JAVA_SHORT_UNALIGNED.withName("idVendor"), + JAVA_SHORT_UNALIGNED.withName("idProduct"), + JAVA_SHORT_UNALIGNED.withName("bcdDevice"), + JAVA_BYTE.withName("iManufacturer"), + JAVA_BYTE.withName("iProduct"), + JAVA_BYTE.withName("iSerialNumber"), + JAVA_BYTE.withName("bNumConfigurations") + ); + + private static final long bcdUSB$OFFSET = 2; + private static final long bDeviceClass$OFFSET = 4; + private static final long bDeviceSubClass$OFFSET = 5; + private static final long bDeviceProtocol$OFFSET = 6; + private static final long idVendor$OFFSET = 8; + private static final long idProduct$OFFSET = 10; + private static final long bcdDevice$OFFSET = 12; + private static final long iManufacturer$OFFSET = 14; + private static final long iProduct$OFFSET = 15; + private static final long iSerialNumber$OFFSET = 16; + + + static { + assert LAYOUT.byteSize() == 18; + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/EndpointDescriptor.java b/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/EndpointDescriptor.java new file mode 100644 index 00000000..c45c3179 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/EndpointDescriptor.java @@ -0,0 +1,74 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.usbstandard; + +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemorySegment; + +import static java.lang.foreign.MemoryLayout.structLayout; +import static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static java.lang.foreign.ValueLayout.JAVA_SHORT_UNALIGNED; + +/** + * USB endpoint descriptor + */ +@SuppressWarnings({"java:S115", "java:S125"}) +public class EndpointDescriptor { + + private final MemorySegment descriptor; + + public EndpointDescriptor(MemorySegment descriptor) { + this.descriptor = descriptor; + } + + public EndpointDescriptor(MemorySegment segment, long offset) { + this(segment.asSlice(offset, LAYOUT.byteSize())); + } + + public int endpointAddress() { + return 0xff & descriptor.get(JAVA_BYTE, bEndpointAddress$OFFSET); + } + + public int attributes() { + return 0xff & descriptor.get(JAVA_BYTE, bmAttributes$OFFSET); + } + + public int maxPacketSize() { + return 0xffff & descriptor.get(JAVA_SHORT_UNALIGNED, wMaxPacketSize$OFFSET); + } + + public int interval() { + return 0xff & descriptor.get(JAVA_BYTE, bInterval$OFFSET); + } + + // struct USBEndpointDescriptor { + // uint8_t bLength; + // uint8_t bDescriptorType; + // uint8_t bEndpointAddress; + // uint8_t bmAttributes; + // uint16_t wMaxPacketSize; + // uint8_t bInterval; + // } __attribute__((packed)); + public static final GroupLayout LAYOUT = structLayout( + JAVA_BYTE.withName("bLength"), + JAVA_BYTE.withName("bDescriptorType"), + JAVA_BYTE.withName("bEndpointAddress"), + JAVA_BYTE.withName("bmAttributes"), + JAVA_SHORT_UNALIGNED.withName("wMaxPacketSize"), + JAVA_BYTE.withName("bInterval") + ); + + private static final long bEndpointAddress$OFFSET = 2; + private static final long bmAttributes$OFFSET = 3; + private static final long wMaxPacketSize$OFFSET = 4; + private static final long bInterval$OFFSET = 6; + + static { + assert LAYOUT.byteSize() == 7; + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/InterfaceAssociationDescriptor.java b/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/InterfaceAssociationDescriptor.java new file mode 100644 index 00000000..b2b14350 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/InterfaceAssociationDescriptor.java @@ -0,0 +1,87 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.usbstandard; + +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemorySegment; + +import static java.lang.foreign.MemoryLayout.structLayout; +import static java.lang.foreign.ValueLayout.JAVA_BYTE; + +/** + * USB interface association descriptor (IAD) + */ +@SuppressWarnings({"java:S115", "java:S125"}) +public class InterfaceAssociationDescriptor { + + private final MemorySegment descriptor; + + public InterfaceAssociationDescriptor(MemorySegment descriptor) { + this.descriptor = descriptor; + } + + public InterfaceAssociationDescriptor(MemorySegment segment, long offset) { + this(segment.asSlice(offset, LAYOUT.byteSize())); + } + + public int firstInterface() { + return 0xff & descriptor.get(JAVA_BYTE, bFirstInterface$OFFSET); + } + + public int interfaceCount() { + return 0xff & descriptor.get(JAVA_BYTE, bInterfaceCount$OFFSET); + } + + public int functionClass() { + return 0xff & descriptor.get(JAVA_BYTE, bFunctionClass$OFFSET); + } + + public int functionSubClass() { + return 0xff & descriptor.get(JAVA_BYTE, bFunctionSubClass$OFFSET); + } + + public int functionProtocol() { + return 0xff & descriptor.get(JAVA_BYTE, bFunctionProtocol$OFFSET); + } + + public int function() { + return 0xff & descriptor.get(JAVA_BYTE, iFunction$OFFSET); + } + + // struct USBInterfaceAssociationDescriptor { + // uint8_t bLength, + // uint8_t bDescriptorType, + // uint8_t bFirstInterface, + // uint8_t bInterfaceCount, + // uint8_t bFunctionClass, + // uint8_t bFunctionSubClass, + // uint8_t bFunctionProtocol, + // uint8_t iFunction + // } __attribute__((packed)); + public static final GroupLayout LAYOUT = structLayout( + JAVA_BYTE.withName("bLength"), + JAVA_BYTE.withName("bDescriptorType"), + JAVA_BYTE.withName("bFirstInterface"), + JAVA_BYTE.withName("bInterfaceCount"), + JAVA_BYTE.withName("bFunctionClass"), + JAVA_BYTE.withName("bFunctionSubClass"), + JAVA_BYTE.withName("bFunctionProtocol"), + JAVA_BYTE.withName("iFunction") + ); + + private static final long bFirstInterface$OFFSET = 2; + private static final long bInterfaceCount$OFFSET = 3; + private static final long bFunctionClass$OFFSET = 4; + private static final long bFunctionSubClass$OFFSET = 5; + private static final long bFunctionProtocol$OFFSET = 6; + private static final long iFunction$OFFSET = 7; + + static { + assert LAYOUT.byteSize() == 8; + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/InterfaceDescriptor.java b/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/InterfaceDescriptor.java new file mode 100644 index 00000000..27351670 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/InterfaceDescriptor.java @@ -0,0 +1,95 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.usbstandard; + +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemorySegment; + +import static java.lang.foreign.MemoryLayout.structLayout; +import static java.lang.foreign.ValueLayout.JAVA_BYTE; + +/** + * USB interface descriptor + */ +@SuppressWarnings({"java:S115", "java:S125"}) +public class InterfaceDescriptor { + + private final MemorySegment descriptor; + + public InterfaceDescriptor(MemorySegment descriptor) { + this.descriptor = descriptor; + } + + public InterfaceDescriptor(MemorySegment segment, long offset) { + this(segment.asSlice(offset, LAYOUT.byteSize())); + } + + public int interfaceNumber() { + return 0xff & descriptor.get(JAVA_BYTE, bInterfaceNumber$OFFSET); + } + + public int alternateSetting() { + return 0xff & descriptor.get(JAVA_BYTE, bAlternateSetting$OFFSET); + } + + public int numEndpoints() { + return 0xff & descriptor.get(JAVA_BYTE, bNumEndpoints$OFFSET); + } + + public int interfaceClass() { + return 0xff & descriptor.get(JAVA_BYTE, bInterfaceClass$OFFSET); + } + + public int interfaceSubClass() { + return 0xff & descriptor.get(JAVA_BYTE, bInterfaceSubClass$OFFSET); + } + + public int interfaceProtocol() { + return 0xff & descriptor.get(JAVA_BYTE, bInterfaceProtocol$OFFSET); + } + + public int iInterface() { + return 0xff & descriptor.get(JAVA_BYTE, iInterface$OFFSET); + } + + // struct USBInterfaceDescriptor { + // uint8_t bLength; + // uint8_t bDescriptorType; + // uint8_t bInterfaceNumber; + // uint8_t bAlternateSetting; + // uint8_t bNumEndpoints; + // uint8_t bInterfaceClass; + // uint8_t bInterfaceSubClass; + // uint8_t bInterfaceProtocol; + // uint8_t iInterface; + // } __attribute__((packed)); + public static final GroupLayout LAYOUT = structLayout( + JAVA_BYTE.withName("bLength"), + JAVA_BYTE.withName("bDescriptorType"), + JAVA_BYTE.withName("bInterfaceNumber"), + JAVA_BYTE.withName("bAlternateSetting"), + JAVA_BYTE.withName("bNumEndpoints"), + JAVA_BYTE.withName("bInterfaceClass"), + JAVA_BYTE.withName("bInterfaceSubClass"), + JAVA_BYTE.withName("bInterfaceProtocol"), + JAVA_BYTE.withName("iInterface") + ); + + private static final long bInterfaceNumber$OFFSET = 2; + private static final long bAlternateSetting$OFFSET = 3; + private static final long bNumEndpoints$OFFSET = 4; + private static final long bInterfaceClass$OFFSET = 5; + private static final long bInterfaceSubClass$OFFSET = 6; + private static final long bInterfaceProtocol$OFFSET = 7; + private static final long iInterface$OFFSET = 8; + + + static { + assert LAYOUT.byteSize() == 9; + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/SetupPacket.java b/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/SetupPacket.java new file mode 100644 index 00000000..e907d42f --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/SetupPacket.java @@ -0,0 +1,110 @@ +package net.codecrete.usb.usbstandard; + +import java.lang.foreign.Arena; +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemorySegment; + +import static java.lang.foreign.MemoryLayout.structLayout; +import static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static java.lang.foreign.ValueLayout.JAVA_SHORT; + +/** + * USB setup packet. + */ +@SuppressWarnings({"java:S115", "java:S125"}) +public class SetupPacket { + + private final MemorySegment descriptor; + + /** + * Creates a setup packet accessing the specified memory segment. + * + * @param descriptor memory segment + */ + public SetupPacket(MemorySegment descriptor) { + this.descriptor = descriptor; + } + + /** + * Creates a setup packet by allocating a native memory segment. + * + * @param arena arena + */ + public SetupPacket(Arena arena) { + this.descriptor = arena.allocate(LAYOUT); + } + + /** + * Gets the memory segment of this setup packet. + * + * @return memory segment + */ + public MemorySegment segment() { + return descriptor; + } + + public int requestType() { + return 0xff & descriptor.get(JAVA_BYTE, bmRequestType$OFFSET); + } + + public void setRequestType(int requestType) { + descriptor.set(JAVA_BYTE, bmRequestType$OFFSET, (byte) requestType); + } + + public int request() { + return 0xff & descriptor.get(JAVA_BYTE, bRequest$OFFSET); + } + + public void setRequest(int request) { + descriptor.set(JAVA_BYTE, bRequest$OFFSET, (byte) request); + } + + public int value() { + return 0xffff & descriptor.get(JAVA_SHORT, wValue$OFFSET); + } + + public void setValue(int value) { + descriptor.set(JAVA_SHORT, wValue$OFFSET, (short) value); + } + + public int index() { + return 0xffff & descriptor.get(JAVA_SHORT, wIndex$OFFSET); + } + + public void setIndex(int index) { + descriptor.set(JAVA_SHORT, wIndex$OFFSET, (short) index); + } + + public int length() { + return 0xffff & descriptor.get(JAVA_SHORT, wLength$OFFSET); + } + + public void setLength(int length) { + descriptor.set(JAVA_SHORT, wLength$OFFSET, (short) length); + } + + // struct USBSetupPacket { + // uint8_t bmRequestType; + // uint8_t bRequest; + // uint8_t wValue; + // uint16_t wIndex; + // uint16_t wLength; + // } __attribute__((packed)); + public static final GroupLayout LAYOUT = structLayout( + JAVA_BYTE.withName("bmRequestType"), + JAVA_BYTE.withName("bRequest"), + JAVA_SHORT.withName("wValue"), + JAVA_SHORT.withName("wIndex"), + JAVA_SHORT.withName("wLength") + ); + + private static final long bmRequestType$OFFSET = 0; + private static final long bRequest$OFFSET = 1; + private static final long wValue$OFFSET = 2; + private static final long wIndex$OFFSET = 4; + private static final long wLength$OFFSET = 6; + + static { + assert LAYOUT.byteSize() == 8; + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/StringDescriptor.java b/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/StringDescriptor.java new file mode 100644 index 00000000..a324e531 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/usbstandard/StringDescriptor.java @@ -0,0 +1,88 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.usbstandard; + +import net.codecrete.usb.UsbException; + +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.nio.charset.StandardCharsets; + +import static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static java.lang.foreign.ValueLayout.JAVA_SHORT; + +/** + * USB string descriptor + */ +@SuppressWarnings({"java:S115", "java:S125"}) +public class StringDescriptor { + + private final MemorySegment descriptor; + + public StringDescriptor(MemorySegment descriptor) { + this.descriptor = descriptor; + } + + /** + * Indicates if this string descriptor is valid. + *

+ * Invalid string descriptors might be missing the header, + * have a descriptor type that is not a string descriptor, + * indicate an incorrect length or have incomplete UTF-16 code units. + *

+ * @return if this descriptor is valid + */ + public boolean isValid() { + return descriptor.byteSize() >= 2 + && descriptor.get(JAVA_BYTE, bDescriptorType$OFFSET) == 3 + && length() == descriptor.byteSize() + && (descriptor.byteSize() & 1) == 0; + } + + public int length() { + return 0xff & descriptor.get(JAVA_BYTE, bLength$OFFSET); + } + + /** + * Returns the string of this string descriptor. + *

+ * Invalid UTF-16 code units are replaced with the Unicode replacement character. + * Trailing 0s (UTF-16 code unit with value 0) are truncated. + *

+ * @throws UsbException if the string descriptor is invalid + * @return the string value + */ + public String string() { + if (!isValid()) + throw new UsbException("String descriptor is invalid"); + var len = (int) (length() - 2L); + var bytes = descriptor.asSlice(string$OFFSET, len).toArray(JAVA_BYTE); + + // truncate trailing 0s + while (len > 0 && bytes[len - 2] == 0 && bytes[len - 1] == 0) + len--; + + return new String(bytes, 0, len, StandardCharsets.UTF_16LE); + } + + // struct USBStringDescriptor { + // uint8_t bLength; + // uint8_t bDescriptorType; + // uint16_t string[1]; + // } __attribute__((packed)); + public static final GroupLayout LAYOUT = MemoryLayout.structLayout( + JAVA_BYTE.withName("bLength"), + JAVA_BYTE.withName("bDescriptorType"), + JAVA_SHORT.withName("string") + ); + + private static final long bLength$OFFSET = 0; + private static final long bDescriptorType$OFFSET = 1; + private static final long string$OFFSET = 2; +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/CompositeFunction.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/CompositeFunction.java deleted file mode 100644 index acf7326c..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/CompositeFunction.java +++ /dev/null @@ -1,81 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// - -package net.codecrete.usb.windows; - -import java.lang.foreign.MemoryAddress; - -/** - * Describes the function of an interface of a composite USB device. - *

- * A composite USB device can have multiple functions, e.g. a mass - * storage function and a virtual serial port function. Each function - * will appear as a separate device in Window. - *

- *

- * A function consists of one or more interfaces. Functions with - * multiple interfaces must have consecutive interface numbers. The - * interfaces after the first one are called associated interfaces. - *

- *

- * On Windows, each function has a separate device path. Each device - * path must be opened and each interface must be opened. - * Furthermore, the first and the associated interfaces are - * treated differently. - *

- */ -public class CompositeFunction { - // TODO: implement associated interfaces - - // TODO: implement devices without interfaces - - private final int firstInterfaceNumber_; - private final int numInterfaces_; - private final String devicePath_; - private MemoryAddress deviceHandle_; - private MemoryAddress firstInterfaceHandle_; - - /** - * Creates a new instance with a single interface. - * - * @param interfaceNumber the interface number - * @param devicePath the device path - */ - public CompositeFunction(int interfaceNumber, String devicePath) { - firstInterfaceNumber_ = interfaceNumber; - numInterfaces_ = 1; - devicePath_ = devicePath; - } - - public int firstInterfaceNumber() { - return firstInterfaceNumber_; - } - - public int numInterfaces() { - return numInterfaces_; - } - - public String devicePath() { - return devicePath_; - } - - public MemoryAddress deviceHandle() { - return deviceHandle_; - } - - public void setDeviceHandle(MemoryAddress deviceHandle) { - deviceHandle_ = deviceHandle; - } - - public MemoryAddress firstInterfaceHandle() { - return firstInterfaceHandle_; - } - - public void setFirstInterfaceHandle(MemoryAddress firstInterfaceHandle) { - firstInterfaceHandle_ = firstInterfaceHandle; - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/CustomApis.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/CustomApis.java new file mode 100644 index 00000000..34ecb727 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/windows/CustomApis.java @@ -0,0 +1,58 @@ +package net.codecrete.usb.windows; + +import net.codecrete.usb.usbstandard.SetupPacket; + +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SymbolLookup; +import java.lang.invoke.MethodHandle; + +import static java.lang.foreign.ValueLayout.ADDRESS; +import static java.lang.foreign.ValueLayout.JAVA_INT; + +@SuppressWarnings({"java:S100", "java:S101", "java:S112", "java:S117"}) +public class CustomApis { + private CustomApis() { + } + + static { + System.loadLibrary("KERNEL32"); + System.loadLibrary("WINUSB"); + } + + private static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup(); + private static final Linker LINKER = Linker.nativeLinker(); + private static final Linker.Option LAST_ERROR_STATE = Linker.Option.captureCallState("GetLastError"); + + // Custom implementation of WinUsb_ControlTransfer as FFM cannot deal with the + // WINUSB_SETUP_PACKET being passed by value as it uses unaligned fields. + // SetupPacket does not use unaligned fields. + private static class WinUsb_ControlTransfer$IMPL { + private static final FunctionDescriptor DESC = FunctionDescriptor.of(JAVA_INT, ADDRESS, SetupPacket.LAYOUT, ADDRESS, JAVA_INT, ADDRESS, ADDRESS); + private static final MethodHandle HANDLE = LINKER.downcallHandle(SYMBOL_LOOKUP.findOrThrow("WinUsb_ControlTransfer"), DESC, LAST_ERROR_STATE); + } + + public static int WinUsb_ControlTransfer(MemorySegment lastErrorState, MemorySegment InterfaceHandle, MemorySegment SetupPacket, MemorySegment Buffer, int BufferLength, MemorySegment LengthTransferred, MemorySegment Overlapped) { + try { + return (int) WinUsb_ControlTransfer$IMPL.HANDLE.invokeExact(lastErrorState, InterfaceHandle, SetupPacket, Buffer, BufferLength, LengthTransferred, Overlapped); + } catch (Throwable ex) { + throw new RuntimeException(ex); + } + } + + // CloseHandle implementation without error state + private static class CloseHandle$IMPL { + private static final FunctionDescriptor DESC = FunctionDescriptor.of(JAVA_INT, ADDRESS); + private static final MethodHandle HANDLE = LINKER.downcallHandle(SYMBOL_LOOKUP.findOrThrow("CloseHandle"), DESC); + } + + public static int CloseHandle(MemorySegment hObject) { + try { + return (int) CloseHandle$IMPL.HANDLE.invokeExact(hObject); + } catch (Throwable ex) { + throw new RuntimeException(ex); + } + } + +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/DeviceInfoSet.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/DeviceInfoSet.java new file mode 100644 index 00000000..e9d3bfb8 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/windows/DeviceInfoSet.java @@ -0,0 +1,395 @@ +package net.codecrete.usb.windows; + +import net.codecrete.usb.common.ScopeCleanup; +import system.Guid; +import windows.win32.devices.deviceanddriverinstallation.SP_DEVICE_INTERFACE_DATA; +import windows.win32.devices.deviceanddriverinstallation.SP_DEVICE_INTERFACE_DETAIL_DATA_W; +import windows.win32.devices.deviceanddriverinstallation.SP_DEVINFO_DATA; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.util.List; + +import static java.lang.foreign.MemorySegment.NULL; +import static java.lang.foreign.ValueLayout.JAVA_CHAR; +import static java.lang.foreign.ValueLayout.JAVA_INT; +import static java.nio.charset.StandardCharsets.UTF_16LE; +import static net.codecrete.usb.windows.Win.allocateErrorState; +import static net.codecrete.usb.windows.WindowsUsbException.throwException; +import static net.codecrete.usb.windows.WindowsUsbException.throwLastError; +import static windows.win32.devices.deviceanddriverinstallation.Apis.SetupDiDeleteDeviceInterfaceData; +import static windows.win32.devices.deviceanddriverinstallation.Apis.SetupDiDestroyDeviceInfoList; +import static windows.win32.devices.deviceanddriverinstallation.Apis.SetupDiEnumDeviceInfo; +import static windows.win32.devices.deviceanddriverinstallation.Apis.SetupDiEnumDeviceInterfaces; +import static windows.win32.devices.deviceanddriverinstallation.Apis.SetupDiGetClassDevsW; +import static windows.win32.devices.deviceanddriverinstallation.Apis.SetupDiCreateDeviceInfoList; +import static windows.win32.devices.deviceanddriverinstallation.Apis.SetupDiGetDeviceInterfaceDetailW; +import static windows.win32.devices.deviceanddriverinstallation.Apis.SetupDiGetDevicePropertyW; +import static windows.win32.devices.deviceanddriverinstallation.Apis.SetupDiOpenDevRegKey; +import static windows.win32.devices.deviceanddriverinstallation.Apis.SetupDiOpenDeviceInfoW; +import static windows.win32.devices.deviceanddriverinstallation.Apis.SetupDiOpenDeviceInterfaceW; +import static windows.win32.devices.deviceanddriverinstallation.Constants.DIREG_DEV; +import static windows.win32.devices.deviceanddriverinstallation.SETUP_DI_GET_CLASS_DEVS_FLAGS.DIGCF_DEVICEINTERFACE; +import static windows.win32.devices.deviceanddriverinstallation.SETUP_DI_GET_CLASS_DEVS_FLAGS.DIGCF_PRESENT; +import static windows.win32.devices.deviceanddriverinstallation.SETUP_DI_PROPERTY_CHANGE_SCOPE.DICS_FLAG_GLOBAL; +import static windows.win32.devices.properties.Constants.DEVPKEY_Device_Service; +import static windows.win32.devices.properties.DEVPROPTYPE.DEVPROP_TYPEMOD_LIST; +import static windows.win32.devices.properties.DEVPROPTYPE.DEVPROP_TYPE_STRING; +import static windows.win32.devices.properties.DEVPROPTYPE.DEVPROP_TYPE_UINT32; +import static windows.win32.foundation.WIN32_ERROR.ERROR_FILE_NOT_FOUND; +import static windows.win32.foundation.WIN32_ERROR.ERROR_INSUFFICIENT_BUFFER; +import static windows.win32.foundation.WIN32_ERROR.ERROR_MORE_DATA; +import static windows.win32.foundation.WIN32_ERROR.ERROR_NOT_FOUND; +import static windows.win32.foundation.WIN32_ERROR.ERROR_NO_MORE_ITEMS; +import static windows.win32.system.com.Apis.CLSIDFromString; +import static windows.win32.system.registry.Apis.RegCloseKey; +import static windows.win32.system.registry.Apis.RegQueryValueExW; +import static windows.win32.system.registry.REG_SAM_FLAGS.KEY_READ; + +/** + * Device information set (of Windows Setup API). + * + *

+ * An instance of this class represents a device information set ({@code HDEVINFO}) + * and a current element within the set. + *

+ */ +public class DeviceInfoSet implements AutoCloseable { + + @FunctionalInterface + interface InfoSetCreator { + long create(Arena arena, MemorySegment errorState); + } + + private final Arena arena; + private final MemorySegment errorState; + private final long devInfoSet; + private final MemorySegment devInfoData; + private MemorySegment devIntfData; + private int iterationIndex = -1; + + /** + * Creates a new device info set containing the present devices of the specified device class and + * optionally device instance ID. + * + *

+ * After creation, there is no current element. {@link #next()} should be called to iterate the first + * and all subsequent elements. + *

+ * + * @param interfaceGuid device interface class GUID + * @param instanceId device instance ID + * @return device info set + */ + static DeviceInfoSet ofPresentDevices(MemorySegment interfaceGuid, String instanceId) { + return new DeviceInfoSet((arena, errorState) -> { + var instanceIdSegment = instanceId != null ? arena.allocateFrom(instanceId, UTF_16LE) : NULL; + return SetupDiGetClassDevsW(errorState, interfaceGuid, instanceIdSegment, NULL, + DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + }); + } + + /** + * Creates a new device info set containing a single device with the specified instance ID. + * + *

+ * The device becomes the current element. The set cannot be iterated. + *

+ * + * @param instanceId instance ID + */ + static DeviceInfoSet ofInstance(String instanceId) { + var devInfoSet = ofEmpty(); + try { + devInfoSet.addInstanceId(instanceId); + } catch (Exception t) { + devInfoSet.close(); + throw t; + } + return devInfoSet; + } + + /** + * Creates a new device info set containing a single device with the specified path. + * + *

+ * The device becomes the current element. The set cannot be iterated. + *

+ * + * @param devicePath device path + */ + static DeviceInfoSet ofPath(String devicePath) { + var devInfoSet = ofEmpty(); + try { + devInfoSet.addDevicePath(devicePath); + } catch (Exception t) { + devInfoSet.close(); + throw t; + } + return devInfoSet; + } + + /** + * Creates a new empty device info set. + * + * @return device info set + */ + private static DeviceInfoSet ofEmpty() { + return new DeviceInfoSet((_, errorState) -> SetupDiCreateDeviceInfoList(errorState, NULL, NULL)); + } + + private DeviceInfoSet(InfoSetCreator creator) { + arena = Arena.ofConfined(); + try { + errorState = allocateErrorState(arena); + + devInfoSet = creator.create(arena, errorState); + if (Win.isInvalidHandle(devInfoSet)) + throwLastError(errorState, "internal error (creating device info set)"); + + // allocate SP_DEVINFO_DATA (will receive device details) + devInfoData = SP_DEVINFO_DATA.allocate(arena); + + } catch (Exception e) { + arena.close(); + throw e; + } + } + + @Override + public void close() { + if (devIntfData != null) + SetupDiDeleteDeviceInterfaceData(errorState, devInfoSet, devIntfData); + SetupDiDestroyDeviceInfoList(errorState, devInfoSet); + arena.close(); + } + + private void addInstanceId(String instanceId) { + var instanceIdSegment = arena.allocateFrom(instanceId, UTF_16LE); + if (SetupDiOpenDeviceInfoW(errorState, devInfoSet, instanceIdSegment, NULL, 0, devInfoData) == 0) + throwLastError(errorState, "internal error (SetupDiOpenDeviceInfoW)"); + } + + private void addDevicePath(String devicePath) { + if (devIntfData != null) + throw new AssertionError("calling addDevice() multiple times is not implemented"); + + // load device information into dev info set + var intfData = SP_DEVICE_INTERFACE_DATA.allocate(arena); + var devicePathSegment = arena.allocateFrom(devicePath, UTF_16LE); + if (SetupDiOpenDeviceInterfaceW(errorState, devInfoSet, devicePathSegment, 0, intfData) == 0) + throwLastError(errorState, "internal error (SetupDiOpenDeviceInterfaceW)"); + + devIntfData = intfData; // for later cleanup + + if (SetupDiGetDeviceInterfaceDetailW(errorState, devInfoSet, intfData, NULL, 0, NULL, devInfoData) == 0) { + var err = Win.getLastError(errorState); + if (err != ERROR_INSUFFICIENT_BUFFER) + throwException(err, "internal error (SetupDiGetDeviceInterfaceDetailW)"); + } + } + + /** + * Iterates to the next element in this set. + * + * @return {@code true} if there is a current element, {@code false} if the iteration moved beyond the last element + */ + boolean next() { + iterationIndex += 1; + if (SetupDiEnumDeviceInfo(errorState, devInfoSet, iterationIndex, devInfoData) == 0) { + var err = Win.getLastError(errorState); + if (err == ERROR_NO_MORE_ITEMS) + return false; + throwLastError(errorState, "internal error (SetupDiEnumDeviceInfo)"); + } + + return true; + } + + /** + * Checks if the current element is a composite USB device + * + * @return {@code true} if it is a composite device + */ + boolean isCompositeDevice() { + var deviceService = getStringProperty(DEVPKEY_Device_Service()); + + // usbccgp is the USB Generic Parent Driver used for composite devices + return "usbccgp".equalsIgnoreCase(deviceService); + } + + /** + * Gets the device path for the device with the given instance ID. + *

+ * The device path is looked up by checking the GUIDs associated with the current element. + *

+ * + * @param instanceId device instance ID + * @return the device path, {@code null} if not found + */ + String getDevicePathByGUID(String instanceId) { + var guids = findDeviceInterfaceGUIDs(arena); + + for (var guid : guids) { + // check for class GUID + var guidSegment = arena.allocateFrom(guid, UTF_16LE); + var clsid = Guid.allocate(arena); + if (CLSIDFromString(guidSegment, clsid) != 0) + continue; + + try { + return getDevicePath(instanceId, clsid); + } catch (Exception _) { + // ignore and try next one + } + } + + return null; + } + + /** + * Gets a list of {@code DeviceInterfaceGUIDs} from the current element's device configuration information + * in the registry. + * + * @param arena arena for allocating memory + * @return list of GUIDs + */ + private List findDeviceInterfaceGUIDs(Arena arena) { + + try (var cleanup = new ScopeCleanup()) { + // open device registry key + var regKey = SetupDiOpenDevRegKey(errorState, devInfoSet, devInfoData, DICS_FLAG_GLOBAL, 0, + DIREG_DEV, KEY_READ); + if (Win.isInvalidHandle(regKey)) + throwLastError(errorState, "internal error (SetupDiOpenDevRegKey)"); + cleanup.add(() -> RegCloseKey(regKey)); + + // read registry value (without buffer, to query length) + var keyNameSegment = arena.allocateFrom("DeviceInterfaceGUIDs", UTF_16LE); + var valueTypeHolder = arena.allocate(JAVA_INT); + var valueSizeHolder = arena.allocate(JAVA_INT); + var res = RegQueryValueExW(regKey, keyNameSegment, NULL, valueTypeHolder, NULL, valueSizeHolder); + if (res == ERROR_FILE_NOT_FOUND) + return List.of(); // no device interface GUIDs + if (res != 0 && res != ERROR_MORE_DATA) + throwException(res, "internal error (RegQueryValueExW)"); + + // read registry value (with buffer) + var valueSize = valueSizeHolder.get(JAVA_INT, 0); + var value = arena.allocate(valueSize); + res = RegQueryValueExW(regKey, keyNameSegment, NULL, valueTypeHolder, value, valueSizeHolder); + if (res != 0) + throwException(res, "internal error (RegQueryValueExW)"); + + return Win.createStringListFromSegment(value); + } + } + + /** + * Gets the integer device property of the current element. + * + * @param propertyKey property key (of type {@code DEVPKEY}) + * @return property value + */ + @SuppressWarnings("SameParameterValue") + int getIntProperty(MemorySegment propertyKey) { + var propertyTypeHolder = arena.allocate(JAVA_INT); + var propertyValueHolder = arena.allocate(JAVA_INT); + if (SetupDiGetDevicePropertyW(errorState, devInfoSet, devInfoData, propertyKey, propertyTypeHolder, + propertyValueHolder, (int) propertyValueHolder.byteSize(), NULL, 0) == 0) + throwLastError(errorState, "internal error (SetupDiGetDevicePropertyW - A)"); + + if (propertyTypeHolder.get(JAVA_INT, 0) != DEVPROP_TYPE_UINT32) + throwException("internal error (expected property type UINT32)"); + + return propertyValueHolder.get(JAVA_INT, 0); + } + + /** + * Gets the string device property of the current element. + * + * @param propertyKey property key (of type {@code DEVPKEY}) + * @return property value + */ + String getStringProperty(MemorySegment propertyKey) { + var propertyValue = getVariableLengthProperty(propertyKey, DEVPROP_TYPE_STRING, arena); + if (propertyValue == null) + return null; + return propertyValue.getString(0, UTF_16LE); + } + + /** + * Gets the string list device property of the current element. + * + * @param propertyKey property key (of type {@code DEVPKEY}) + * @return property value + */ + @SuppressWarnings("java:S1168") + List getStringListProperty(MemorySegment propertyKey) { + var propertyValue = getVariableLengthProperty(propertyKey, + DEVPROP_TYPE_STRING | DEVPROP_TYPEMOD_LIST, arena); + if (propertyValue == null) + return null; + + return Win.createStringListFromSegment(propertyValue); + } + + private MemorySegment getVariableLengthProperty(MemorySegment propertyKey, int propertyType, Arena arena) { + + // query length (thus no buffer) + var propertyTypeHolder = arena.allocate(JAVA_INT); + var requiredSizeHolder = arena.allocate(JAVA_INT); + if (SetupDiGetDevicePropertyW(errorState, devInfoSet, devInfoData, propertyKey, propertyTypeHolder, NULL, 0, + requiredSizeHolder, 0) == 0) { + var err = Win.getLastError(errorState); + if (err == ERROR_NOT_FOUND) + return null; + if (err != ERROR_INSUFFICIENT_BUFFER) + throwException(err, "internal error (SetupDiGetDevicePropertyW - B)"); + } + + if (propertyTypeHolder.get(JAVA_INT, 0) != propertyType) + throwException("internal error (unexpected property type)"); + + var stringLen = (requiredSizeHolder.get(JAVA_INT, 0) + 1) / 2; + + // allocate buffer + var propertyValueHolder = arena.allocate(JAVA_CHAR, stringLen); + + // get property value + if (SetupDiGetDevicePropertyW(errorState, devInfoSet, devInfoData, propertyKey, propertyTypeHolder, + propertyValueHolder, (int) propertyValueHolder.byteSize(), NULL, 0) == 0) + throwLastError(errorState, "internal error (SetupDiGetDevicePropertyW - C)"); + + return propertyValueHolder; + } + + /** + * Gets the device path for the device with the given device instance ID and device interface class. + * + * @param instanceId device instance ID + * @param interfaceGuid device interface class GUID + * @return the device path + */ + static String getDevicePath(String instanceId, MemorySegment interfaceGuid) { + try (var deviceInfoSet = DeviceInfoSet.ofPresentDevices(interfaceGuid, instanceId)) { + return deviceInfoSet.getDevicePathForGuid(interfaceGuid); + } + } + + private String getDevicePathForGuid(MemorySegment interfaceGuid) { + // retrieve first element of enumeration + devIntfData = SP_DEVICE_INTERFACE_DATA.allocate(arena); + if (SetupDiEnumDeviceInterfaces(errorState, devInfoSet, NULL, interfaceGuid, 0, devIntfData) == 0) + throwLastError(errorState, "internal error (SetupDiEnumDeviceInterfaces)"); + + // get device path + var intfDetailData = SP_DEVICE_INTERFACE_DETAIL_DATA_W.allocate(arena, 260); + if (SetupDiGetDeviceInterfaceDetailW(errorState, devInfoSet, devIntfData, intfDetailData, + (int) intfDetailData.byteSize(), NULL, NULL) == 0) + throwLastError(errorState, "Internal error (SetupDiGetDeviceInterfaceDetailW)"); + + var devicePath = SP_DEVICE_INTERFACE_DETAIL_DATA_W.DevicePath(intfDetailData); + return devicePath.getString(0, UTF_16LE); + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/DeviceProperty.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/DeviceProperty.java deleted file mode 100644 index a57aa7b3..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/DeviceProperty.java +++ /dev/null @@ -1,190 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// - -package net.codecrete.usb.windows; - -import net.codecrete.usb.USBException; -import net.codecrete.usb.windows.gen.advapi32.Advapi32; -import net.codecrete.usb.windows.gen.kernel32.GUID; -import net.codecrete.usb.windows.gen.kernel32.Kernel32; -import net.codecrete.usb.windows.gen.setupapi.SP_DEVICE_INTERFACE_DATA; -import net.codecrete.usb.windows.gen.setupapi.SP_DEVICE_INTERFACE_DETAIL_DATA_W; -import net.codecrete.usb.windows.gen.setupapi.SetupAPI; - -import java.lang.foreign.Addressable; -import java.lang.foreign.MemoryAddress; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; -import java.util.List; - -import static java.lang.foreign.MemoryAddress.NULL; -import static java.lang.foreign.ValueLayout.JAVA_CHAR; -import static java.lang.foreign.ValueLayout.JAVA_INT; - -/** - * Device property GUIDs and functions - */ -public class DeviceProperty { - - public static final MemorySegment DEVPKEY_Device_Address = createDEVPROPKEY(0xa45c254e, (short) 0xdf1c, - (short) 0x4efd, (byte) 0x80, (byte) 0x20, (byte) 0x67, (byte) 0xd1, (byte) 0x46, (byte) 0xa8, (byte) 0x50 - , (byte) 0xe0, 30); - - public static final MemorySegment DEVPKEY_Device_InstanceId = createDEVPROPKEY(0x78c34fc8, (short) 0x104a, - (short) 0x4aca, (byte) 0x9e, (byte) 0xa4, (byte) 0x52, (byte) 0x4d, (byte) 0x52, (byte) 0x99, (byte) 0x6e - , (byte) 0x57, 256); - - public static final MemorySegment DEVPKEY_Device_Parent = createDEVPROPKEY(0x4340a6c5, (short) 0x93fa, - (short) 0x4706, (byte) 0x97, (byte) 0x2c, (byte) 0x7b, (byte) 0x64, (byte) 0x80, (byte) 0x08, (byte) 0xa5 - , (byte) 0xa7, 8); - - public static final MemorySegment DEVPKEY_Device_Service = createDEVPROPKEY(0xa45c254e, (short) 0xdf1c, - (short) 0x4efd, (byte) 0x80, (byte) 0x20, (byte) 0x67, (byte) 0xd1, (byte) 0x46, (byte) 0xa8, (byte) 0x50, - (byte) 0xe0, 6); - - public static final MemorySegment DEVPKEY_Device_Children = createDEVPROPKEY(0x4340a6c5, (short) 0x93fa, - (short) 0x4706, (byte) 0x97, (byte) 0x2c, (byte) 0x7b, (byte) 0x64, (byte) 0x80, (byte) 0x08, (byte) 0xa5, - (byte) 0xa7, 9); - - public static final MemorySegment DEVPKEY_Device_HardwareIds = createDEVPROPKEY(0xa45c254e, (short) 0xdf1c, - (short) 0x4efd, (byte) 0x80, (byte) 0x20, (byte) 0x67, (byte) 0xd1, (byte) 0x46, (byte) 0xa8, (byte) 0x50, - (byte) 0xe0, 3); - - public static int getDeviceIntProperty(Addressable devInfo, Addressable devInfoData, Addressable propertyKey) { - try (var session = MemorySession.openConfined()) { - var propertyTypeHolder = session.allocate(JAVA_INT); - var propertyValueHolder = session.allocate(JAVA_INT); - if (SetupAPI.SetupDiGetDevicePropertyW(devInfo, devInfoData, propertyKey, propertyTypeHolder, - propertyValueHolder, (int) propertyValueHolder.byteSize(), NULL, 0) == 0) - throw new WindowsUSBException("Internal error (SetupDiGetDevicePropertyW)", Kernel32.GetLastError()); - - if (propertyTypeHolder.get(JAVA_INT, 0) != SetupAPI.DEVPROP_TYPE_UINT32()) - throw new USBException("Internal error (expected property type UINT32)"); - - return propertyValueHolder.get(JAVA_INT, 0); - } - } - - public static String getDeviceStringProperty(Addressable devInfo, Addressable devInfoData, - Addressable propertyKey) { - try (var session = MemorySession.openConfined()) { - var propertyValue = getProperty(devInfo, devInfoData, propertyKey, - SetupAPI.DEVPROP_TYPE_STRING(), session); - return Win.createStringFromSegment(propertyValue); - } - } - - public static List getDeviceStringListProperty(Addressable devInfo, Addressable devInfoData, - Addressable propertyKey) { - try (var session = MemorySession.openConfined()) { - var propertyValue = getProperty(devInfo, devInfoData, propertyKey, - SetupAPI.DEVPROP_TYPE_STRING() | SetupAPI.DEVPROP_TYPEMOD_LIST(), session); - - return Win.createStringListFromSegment(propertyValue); - } - } - - private static MemorySegment getProperty(Addressable devInfo, Addressable devInfoData, Addressable propertyKey, - int propertyType, MemorySession session) { - var propertyTypeHolder = session.allocate(JAVA_INT); - var requiredSizeHolder = session.allocate(JAVA_INT); - if (SetupAPI.SetupDiGetDevicePropertyW(devInfo, devInfoData, propertyKey, propertyTypeHolder, NULL, 0, - requiredSizeHolder, 0) == 0) { - // TODO: Reactivate when proper GetLastError() handling is available - // int err = Kernel32.GetLastError(); - // if (err != Kernel32.ERROR_INSUFFICIENT_BUFFER()) - // throw new WindowsUSBException("Internal error (SetupDiGetDevicePropertyW)", Kernel32 - // .GetLastError()); - } - - if (propertyTypeHolder.get(JAVA_INT, 0) != propertyType) - throw new USBException("Internal error (unexpected property type)"); - - int stringLen = requiredSizeHolder.get(JAVA_INT, 0) / 2 - 1; - - var propertyValueHolder = session.allocateArray(JAVA_CHAR, stringLen + 1); - if (SetupAPI.SetupDiGetDevicePropertyW(devInfo, devInfoData, propertyKey, propertyTypeHolder, - propertyValueHolder, (int) propertyValueHolder.byteSize(), NULL, 0) == 0) - throw new WindowsUSBException("Internal error (SetupDiGetDevicePropertyW)", Kernel32.GetLastError()); - - return propertyValueHolder; - } - - public static List findDeviceInterfaceGUIDs(MemoryAddress devInfoSetHandle, MemorySegment devInfo, MemorySession session) { - - // open device registry key - var regKey = SetupAPI.SetupDiOpenDevRegKey(devInfoSetHandle, devInfo, SetupAPI.DICS_FLAG_GLOBAL(), - 0, SetupAPI.DIREG_DEV(), Advapi32.KEY_READ()); - if (Win.IsInvalidHandle(regKey)) - throw new WindowsUSBException("Cannot open device registry key", Kernel32.GetLastError()); - session.addCloseAction(() -> Advapi32.RegCloseKey(regKey)); - - // read registry value (without buffer, to query length) - var keyNameSegment = Win.createSegmentFromString("DeviceInterfaceGUIDs", session); - var valueTypeHolder = session.allocate(JAVA_INT); - var valueSizeHolder = session.allocate(JAVA_INT); - var res = Advapi32.RegQueryValueExW(regKey, keyNameSegment, NULL, valueTypeHolder, NULL, valueSizeHolder); - if (res == Kernel32.ERROR_FILE_NOT_FOUND()) - return List.of(); // no device interface GUIDs - if (res != 0 && res != Kernel32.ERROR_MORE_DATA()) - throw new WindowsUSBException("Internal error (RegQueryValueExW)", res); - - // read registry value (with buffer) - var valueSize = valueSizeHolder.get(JAVA_INT, 0); - var value = session.allocate(valueSize); - res = Advapi32.RegQueryValueExW(regKey, keyNameSegment, NULL, valueTypeHolder, value, valueSizeHolder); - if (res != 0) - throw new WindowsUSBException("Internal error (RegQueryValueExW)", res); - - return Win.createStringListFromSegment(value); - } - - public static String getDevicePath(String instanceID, Addressable interfaceGuid) { - try (var session = MemorySession.openConfined()) { - // get device info set for instance - var instanceIDSegment = Win.createSegmentFromString(instanceID, session); - final var devInfoSetHandle = SetupAPI.SetupDiGetClassDevsW(interfaceGuid, instanceIDSegment, NULL, - SetupAPI.DIGCF_PRESENT() | SetupAPI.DIGCF_DEVICEINTERFACE()); - if (Win.IsInvalidHandle(devInfoSetHandle)) - throw new USBException("internal error (SetupDiGetClassDevsW)"); - - // ensure the result is destroyed when the scope is left - session.addCloseAction(() -> SetupAPI.SetupDiDestroyDeviceInfoList(devInfoSetHandle)); - - // retrieve first element of enumeration - var devIntfData = session.allocate(SP_DEVICE_INTERFACE_DATA.$LAYOUT()); - SP_DEVICE_INTERFACE_DATA.cbSize$set(devIntfData, (int) devIntfData.byteSize()); - if (SetupAPI.SetupDiEnumDeviceInterfaces(devInfoSetHandle, NULL, interfaceGuid, 0, devIntfData) == 0) - throw new USBException("internal error (SetupDiEnumDeviceInterfaces)"); - - // get device path - // (SP_DEVICE_INTERFACE_DETAIL_DATA_W is of variable length and requires a bigger allocation so - // the device path fits) - final int devicePathOffset = 4; - var intfDetailData = session.allocate(4 + 260 * 2); - SP_DEVICE_INTERFACE_DETAIL_DATA_W.cbSize$set(intfDetailData, - (int) SP_DEVICE_INTERFACE_DETAIL_DATA_W.sizeof()); - int intfDetailDataSize = (int) intfDetailData.byteSize(); - if (SetupAPI.SetupDiGetDeviceInterfaceDetailW(devInfoSetHandle, devIntfData, intfDetailData, - intfDetailDataSize, NULL, NULL) == 0) - throw new WindowsUSBException("Internal error (SetupDiGetDeviceInterfaceDetailW)", Kernel32.GetLastError()); - - return Win.createStringFromSegment(intfDetailData.asSlice(devicePathOffset)); - } - } - - public static MemorySegment createDEVPROPKEY(int data1, short data2, short data3, byte data4_0, byte data4_1, - byte data4_2, byte data4_3, byte data4_4, byte data4_5, byte data4_6 - , byte data4_7, int pid) { - var propKey = MemorySession.global().allocate(GUID.sizeof() + JAVA_INT.byteSize()); - Win.setGUID(propKey, data1, data2, data3, data4_0, data4_1, data4_2, data4_3, data4_4, data4_5, data4_6, - data4_7); - propKey.set(JAVA_INT, 16, pid); - return propKey; - } - -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/InterfaceHandle.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/InterfaceHandle.java new file mode 100644 index 00000000..c3d56a74 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/windows/InterfaceHandle.java @@ -0,0 +1,45 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.windows; + +import java.lang.foreign.MemorySegment; + +/** + * Handles for WinUSB devices and interfaces + */ +class InterfaceHandle { + InterfaceHandle(int interfaceNumber, int firstInterfaceNumber) { + this.interfaceNumber = interfaceNumber; + this.firstInterfaceNumber = firstInterfaceNumber; + } + + /** + * The number of this interface. + */ + final int interfaceNumber; + /** + * The number of the first interface in the same composite function. + */ + final int firstInterfaceNumber; + /** + * The file handle of the device. + *

+ * This is only used for the first interface in a composite function. + *

+ */ + MemorySegment deviceHandle; + /** + * The WinUSB handle of the interface. + */ + @SuppressWarnings("java:S1700") + MemorySegment winusbHandle; + /** + * Count indicating how many interface depend on the device being open. + */ + int deviceOpenCount; +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/USBHelper.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/USBHelper.java deleted file mode 100644 index c0c84fdc..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/USBHelper.java +++ /dev/null @@ -1,112 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// - -package net.codecrete.usb.windows; - -import net.codecrete.usb.common.USBDescriptors; -import net.codecrete.usb.common.USBStructs; - -import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemorySegment; -import java.lang.invoke.VarHandle; - -import static java.lang.foreign.MemoryLayout.PathElement.groupElement; -import static java.lang.foreign.MemoryLayout.structLayout; -import static java.lang.foreign.ValueLayout.*; - -/** - * USB constants and struct - *

- * Mainly used to work around an alignment problem with the generated - * USB_NODE_CONNECTION_INFORMATION_EX struct. - *

- */ -public class USBHelper { - - public static final byte USB_REQUEST_GET_DESCRIPTOR = 0x06; - - - // typedef struct _USB_NODE_CONNECTION_INFORMATION_EX { - // ULONG ConnectionIndex; /* INPUT */ - // /* usb device descriptor returned by this device - // during enumeration */ - // USB_DEVICE_DESCRIPTOR DeviceDescriptor;/* OUTPUT */ - // UCHAR CurrentConfigurationValue;/* OUTPUT */ - // /* values for the speed field are defined in USB200.h */ - // UCHAR Speed;/* OUTPUT */ - // BOOLEAN DeviceIsHub;/* OUTPUT */ - // USHORT DeviceAddress;/* OUTPUT */ - // ULONG NumberOfOpenPipes;/* OUTPUT */ - // USB_CONNECTION_STATUS ConnectionStatus;/* OUTPUT */ - // USB_PIPE_INFO PipeList[0];/* OUTPUT */ - //} USB_NODE_CONNECTION_INFORMATION_EX, *PUSB_NODE_CONNECTION_INFORMATION_EX; - public static final GroupLayout USB_NODE_CONNECTION_INFORMATION_EX$Struct = structLayout(JAVA_INT.withName( - "ConnectionIndex"), USBDescriptors.Device$Struct.withName("DeviceDescriptor"), JAVA_BYTE.withName( - "CurrentConfigurationValue"), JAVA_BYTE.withName("Speed"), JAVA_BYTE.withName("DeviceIsHub"), - JAVA_SHORT.withName("DeviceAddress"), JAVA_INT.withName("NumberOfOpenPipes"), JAVA_INT.withName( - "ConnectionStatus") - // USB_PIPE_INFO PipeList[0] - ); - public static final VarHandle USB_NODE_CONNECTION_INFORMATION_EX_ConnectionIndex = - USB_NODE_CONNECTION_INFORMATION_EX$Struct.varHandle(groupElement("ConnectionIndex")); - public static final long USB_NODE_CONNECTION_INFORMATION_EX_DeviceDescriptor$Offset = - USB_NODE_CONNECTION_INFORMATION_EX$Struct.byteOffset(groupElement("DeviceDescriptor")); - - public static MemorySegment USB_NODE_CONNECTION_INFORMATION_EX_DeviceDescriptor$slice(MemorySegment seg) { - return seg.asSlice(USB_NODE_CONNECTION_INFORMATION_EX_DeviceDescriptor$Offset, - USBDescriptors.Device$Struct.byteSize()); - } - - public static final VarHandle USB_NODE_CONNECTION_INFORMATION_EX_CurrentConfigurationValue = - USB_NODE_CONNECTION_INFORMATION_EX$Struct.varHandle(groupElement("CurrentConfigurationValue")); - - // typedef struct _USB_DESCRIPTOR_REQUEST { - // ULONG ConnectionIndex; - // struct { - // UCHAR bmRequest; - // UCHAR bRequest; - // USHORT wValue; - // USHORT wIndex; - // USHORT wLength; - // } SetupPacket; - // UCHAR Data[0]; - //} USB_DESCRIPTOR_REQUEST, *PUSB_DESCRIPTOR_REQUEST; - public static final GroupLayout USB_DESCRIPTOR_REQUEST$Struct = structLayout(JAVA_INT.withName("ConnectionIndex") - , USBStructs.SetupPacket$Struct.withName("SetupPacket")); - public static final VarHandle USB_DESCRIPTOR_REQUEST_ConnectionIndex = - USB_DESCRIPTOR_REQUEST$Struct.varHandle(groupElement("ConnectionIndex")); - public static final long USB_DESCRIPTOR_REQUEST_SetupPacket$Offset = - USB_DESCRIPTOR_REQUEST$Struct.byteOffset(groupElement("SetupPacket")); - public static final long USB_DESCRIPTOR_REQUEST_Data$Offset = USB_DESCRIPTOR_REQUEST$Struct.byteSize(); - - // typedef struct _USB_STRING_DESCRIPTOR { - // UCHAR bLength; - // UCHAR bDescriptorType; - // WCHAR bString[1]; - //} USB_STRING_DESCRIPTOR, *PUSB_STRING_DESCRIPTOR; - public static final GroupLayout USB_STRING_DESCRIPTOR$Struct = structLayout(JAVA_BYTE.withName("bLength"), - JAVA_BYTE.withName("bDescriptorType")); - public static final VarHandle USB_STRING_DESCRIPTOR_bLength = - USB_STRING_DESCRIPTOR$Struct.varHandle(groupElement("bLength")); - public static final VarHandle USB_STRING_DESCRIPTOR_bDescriptorType = - USB_STRING_DESCRIPTOR$Struct.varHandle(groupElement("bDescriptorType")); - public static final long USB_STRING_DESCRIPTOR_bString$Offset = USB_STRING_DESCRIPTOR$Struct.byteSize(); - - // A5DCBF10-6530-11D2-901F-00C04FB951ED - public static final MemorySegment GUID_DEVINTERFACE_USB_DEVICE = Win.CreateGUID(0xA5DCBF10, (short) 0x6530, - (short) 0x11D2, (byte) 0x90, (byte) 0x1F, (byte) 0x00, (byte) 0xC0, (byte) 0x4F, (byte) 0xB9, (byte) 0x51 - , (byte) 0xED); - - // f18a0e88-c30c-11d0-8815-00a0c906bed8 - public static final MemorySegment GUID_DEVINTERFACE_USB_HUB = Win.CreateGUID(0xf18a0e88, (short) 0xc30c, - (short) 0x11d0, (byte) 0x88, (byte) 0x15, (byte) 0x00, (byte) 0xa0, (byte) 0xc9, (byte) 0x06, (byte) 0xbe - , (byte) 0xd8); - - static { - assert USB_NODE_CONNECTION_INFORMATION_EX$Struct.byteSize() == 35; - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/Win.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/Win.java index ac2e51a0..6f5791bf 100644 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/Win.java +++ b/java-does-usb/src/main/java/net/codecrete/usb/windows/Win.java @@ -7,70 +7,72 @@ package net.codecrete.usb.windows; -import net.codecrete.usb.windows.gen.kernel32.GUID; -import net.codecrete.usb.windows.gen.stdlib.StdLib; - -import java.lang.foreign.MemoryAddress; +import java.lang.foreign.Arena; +import java.lang.foreign.Linker; +import java.lang.foreign.MemoryLayout.PathElement; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; -import java.lang.foreign.ValueLayout; +import java.lang.foreign.StructLayout; +import java.lang.invoke.VarHandle; import java.util.ArrayList; import java.util.List; -import static java.lang.foreign.ValueLayout.JAVA_BYTE; import static java.lang.foreign.ValueLayout.JAVA_CHAR; +import static java.nio.charset.StandardCharsets.UTF_16LE; /** * Windows helpers. */ public class Win { + private Win() { + } /** - * Checks if a Windows handle is invalid. + * Call state for capturing the {@code GetLastError()} value. + */ + public static final Linker.Option LAST_ERROR_STATE = Linker.Option.captureCallState("GetLastError"); + private static final StructLayout LAST_ERROR_STATE_LAYOUT = Linker.Option.captureStateLayout(); + + private static final VarHandle callState_GetLastError$VH = + LAST_ERROR_STATE_LAYOUT.varHandle(PathElement.groupElement("GetLastError")); + + static MemorySegment allocateErrorState(Arena arena) { + return arena.allocate(LAST_ERROR_STATE_LAYOUT); + } + + /** + * Returns the error code captured using the call state {@link #LAST_ERROR_STATE}. * - * @param handle Windows handle - * @return {@code true} if the handle is invalid, {@code false} otherwise + * @param callState the call state + * @return the error code */ - public static boolean IsInvalidHandle(MemoryAddress handle) { - return handle.toRawLongValue() == -1L; + public static int getLastError(MemorySegment callState) { + return (int) callState_GetLastError$VH.get(callState, 0); } /** - * Creates a memory segment as a copy of a Java string. - *

- * The memory segment contains a copy of the string (null-terminated, UTF-16/wide characters). - *

+ * Checks if a Windows handle is invalid. * - * @param str the string to copy - * @param session the memory session for the memory segment - * @return the resulting memory segment + * @param handle Windows handle + * @return {@code true} if the handle is invalid, {@code false} otherwise */ - public static MemorySegment createSegmentFromString(String str, MemorySession session) { - // allocate segment (including space for terminating null) - var segment = session.allocateArray(ValueLayout.JAVA_CHAR, str.length() + 1); - // copy characters - segment.copyFrom(MemorySegment.ofArray(str.toCharArray())); - return segment; + public static boolean isInvalidHandle(MemorySegment handle) { + return handle.address() == -1L; } /** - * Creates a copy of the string in the memory segment. - *

- * The string must be a null-terminated UTF-16 (wide character) string. - *

+ * Checks if a Windows handle is invalid. * - * @param segment the memory segment - * @return copied string + * @param handle Windows handle + * @return {@code true} if the handle is invalid, {@code false} otherwise */ - public static String createStringFromSegment(MemorySegment segment) { - long strLen = StdLib.wcslen(segment); - return new String(segment.asSlice(0, 2L * strLen).toArray(JAVA_CHAR)); + public static boolean isInvalidHandle(long handle) { + return handle == -1L; } /** * Creates a copy of the string list in the memory segment. *

- * The string list a a series of null-terminated UTF-16 (wide character) strings. + * The string list is a series of null-terminated UTF-16 (wide character) strings. * The list is terminated with yet another null character. *

* @@ -79,52 +81,12 @@ public static String createStringFromSegment(MemorySegment segment) { */ public static List createStringListFromSegment(MemorySegment segment) { var stringList = new ArrayList(); - int offset = 0; + var offset = 0; while (segment.get(JAVA_CHAR, offset) != '\0') { - var str = Win.createStringFromSegment(segment.asSlice(offset)); + var str = segment.getString(offset, UTF_16LE); offset += str.length() * 2 + 2; stringList.add(str); } return stringList; } - - /** - * Creates a GUID in native memory. - * - * @param data1 Group 1 (4 bytes). - * @param data2 Group 2 (2 bytes). - * @param data3 Group 3 (2 bytes). - * @param data4_0 Byte 0 of group 4 - * @param data4_1 Byte 1 of group 4 - * @param data4_2 Byte 2 of group 4 - * @param data4_3 Byte 3 of group 4 - * @param data4_4 Byte 4 of group 4 - * @param data4_5 Byte 5 of group 4 - * @param data4_6 Byte 6 of group 4 - * @param data4_7 Byte 7 of group 4 - * @return GUID as memory segment - */ - public static MemorySegment CreateGUID(int data1, short data2, short data3, byte data4_0, byte data4_1, - byte data4_2, byte data4_3, byte data4_4, byte data4_5, byte data4_6, - byte data4_7) { - var guid = MemorySegment.allocateNative(GUID.$LAYOUT(), MemorySession.global()); - setGUID(guid, data1, data2, data3, data4_0, data4_1, data4_2, data4_3, data4_4, data4_5, data4_6, data4_7); - return guid; - } - - public static void setGUID(MemorySegment guid, int data1, short data2, short data3, byte data4_0, byte data4_1, - byte data4_2, byte data4_3, byte data4_4, byte data4_5, byte data4_6, byte data4_7) { - GUID.Data1$set(guid, data1); - GUID.Data2$set(guid, data2); - GUID.Data3$set(guid, data3); - var data4 = GUID.Data4$slice(guid); - data4.set(JAVA_BYTE, 0, data4_0); - data4.set(JAVA_BYTE, 1, data4_1); - data4.set(JAVA_BYTE, 2, data4_2); - data4.set(JAVA_BYTE, 3, data4_3); - data4.set(JAVA_BYTE, 4, data4_4); - data4.set(JAVA_BYTE, 5, data4_5); - data4.set(JAVA_BYTE, 6, data4_6); - data4.set(JAVA_BYTE, 7, data4_7); - } } diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsAsyncTask.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsAsyncTask.java new file mode 100644 index 00000000..fa31dad2 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsAsyncTask.java @@ -0,0 +1,258 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.windows; + +import net.codecrete.usb.UsbException; +import windows.win32.system.io.OVERLAPPED; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static java.lang.System.Logger.Level.ERROR; +import static java.lang.foreign.MemorySegment.NULL; +import static java.lang.foreign.ValueLayout.ADDRESS; +import static java.lang.foreign.ValueLayout.JAVA_INT; +import static java.lang.foreign.ValueLayout.JAVA_LONG; +import static net.codecrete.usb.windows.Win.allocateErrorState; +import static net.codecrete.usb.windows.WindowsUsbException.throwLastError; +import static windows.win32.foundation.WIN32_ERROR.ERROR_OPERATION_ABORTED; +import static windows.win32.system.io.Apis.CreateIoCompletionPort; +import static windows.win32.system.io.Apis.GetQueuedCompletionStatus; +import static windows.win32.system.threading.Constants.INFINITE; + +/** + * Background task for handling asynchronous transfers. + *

+ * Each USB device must register its handle with this task. + *

+ *

+ * The task keeps track of the submitted transfers by indexing them + * by OVERLAPPED struct address. + *

+ *

+ * OVERLAPPED structs are allocated but never freed. To limit the memory usage, + * OVERLAPPED structs are reused. So the maximum number of outstanding transfers + * determines the number of allocated OVERLAPPED structs. + *

+ */ +@SuppressWarnings("java:S6548") +class WindowsAsyncTask { + + private static final System.Logger LOG = System.getLogger(WindowsAsyncTask.class.getName()); + + /** + * Singleton instance of background task. + */ + static final WindowsAsyncTask INSTANCE = new WindowsAsyncTask(); + + // Currently outstanding transfer requests, + // indexed by OVERLAPPED address. + private Map requestsByOverlapped; + // available OVERLAPPED data structures + private List availableOverlappedStructs; + // Arena used to allocate OVERLAPPED data structures + private Arena overlappedArena; + + /** + * Windows completion port for asynchronous/overlapped IO + */ + private MemorySegment asyncIoCompletionPort = NULL; + + /** + * Indicates that the background task has terminated due to an unrecoverable error. + */ + private boolean taskTerminated; + + /** + * Background task for handling asynchronous IO completions. + */ + @SuppressWarnings("java:S2189") + private void asyncCompletionTask() { + + try (var arena = Arena.ofConfined()) { + + var overlappedHolder = arena.allocate(ADDRESS); + var numBytesHolder = arena.allocate(JAVA_INT); + var completionKeyHolder = arena.allocate(JAVA_LONG); + var errorState = allocateErrorState(arena); + + while (true) { + try { + overlappedHolder.set(ADDRESS, 0, NULL); + completionKeyHolder.set(JAVA_LONG, 0, 0); + + var res = GetQueuedCompletionStatus(errorState, asyncIoCompletionPort, numBytesHolder, + completionKeyHolder, overlappedHolder, INFINITE); + var overlappedAddr = overlappedHolder.get(JAVA_LONG, 0); + + // A null OVERLAPPED means no completion packet was dequeued (nothing posts + // packets without an OVERLAPPED): the completion port itself has failed, + // and no further completions will ever be delivered. + if (overlappedAddr == 0) { + var success = res != 0; + throwLastError(errorState, "internal error (GetQueuedCompletionStatus, success: %s)", success); + } + + completeTransfer(overlappedAddr); + + } catch (Exception e) { + LOG.log(ERROR, "USB async IO thread failed and is terminating; " + + "all outstanding transfers will fail, and no further transfers are possible", e); + failAllPendingTransfers(); + return; + } + } + } + } + + /** + * Fails all outstanding transfers and marks this task as terminated. + *

+ * Called when the background task can no longer dispatch completions. Waiters blocked + * on the failed transfers wake up with an error result instead of hanging forever, + * and future submissions are rejected. + *

+ */ + private void failAllPendingTransfers() { + List pendingTransfers; + synchronized (this) { + taskTerminated = true; + pendingTransfers = new ArrayList<>(requestsByOverlapped.values()); + requestsByOverlapped.clear(); + availableOverlappedStructs.clear(); + for (var transfer : pendingTransfers) { + transfer.setResultCode(ERROR_OPERATION_ABORTED); + transfer.setResultSize(0); + transfer.setOverlapped(null); + } + } + + for (var transfer : pendingTransfers) { + try { + transfer.completion().completed(transfer); + } catch (Exception e) { + LOG.log(ERROR, "Unexpected exception while handling async IO completion", e); + } + } + } + + /** + * Add a Windows handle (of a USB device) to the completion port. + *

+ * The handle is removed by closing it. + *

+ * + * @param handle Windows handle + */ + synchronized void addDevice(MemorySegment handle) { + + try (var arena = Arena.ofConfined()) { + var errorState = allocateErrorState(arena); + + // Creates a new port if it doesn't exist; adds handle to existing port if it exists + var portHandle = CreateIoCompletionPort(errorState, handle, asyncIoCompletionPort, + handle.address(), 0); + if (portHandle == MemorySegment.NULL) + throwLastError(errorState, "internal error (CreateIoCompletionPort)"); + + if (asyncIoCompletionPort == MemorySegment.NULL) { + asyncIoCompletionPort = portHandle; + startAsyncIOTask(); + } + } + } + + private void startAsyncIOTask() { + availableOverlappedStructs = new ArrayList<>(); + overlappedArena = Arena.ofAuto(); + requestsByOverlapped = new HashMap<>(); + + // start background thread for handling IO completion + var thread = new Thread(this::asyncCompletionTask, "USB async IO"); + thread.setDaemon(true); + thread.start(); + } + + /** + * Prepare a transfer for submission by adding the OVERLAPPED struct. + * + * @param transfer transfer to prepare + */ + synchronized void prepareForSubmission(WindowsTransfer transfer) { + if (taskTerminated) + throw new UsbException("USB async IO background thread has terminated due to an unrecoverable error; " + + "USB transfers are no longer possible"); + + MemorySegment overlapped; + var size = availableOverlappedStructs.size(); + if (size == 0) { + overlapped = OVERLAPPED.allocate(overlappedArena); + } else { + overlapped = availableOverlappedStructs.remove(size - 1); + } + + transfer.setOverlapped(overlapped); + transfer.setResultSize(-1); + requestsByOverlapped.put(overlapped.address(), transfer); + } + + /** + * Undoes the registration performed by {@link #prepareForSubmission(WindowsTransfer)}. + *

+ * Must be called if the native submission of a prepared transfer fails synchronously. + * In that case, no completion packet will ever be posted for the transfer, so its map + * entry would leak and the OVERLAPPED struct would never return to the pool unless + * they are cleaned up here. + *

+ * + * @param transfer transfer whose submission failed + */ + synchronized void submissionFailed(WindowsTransfer transfer) { + requestsByOverlapped.remove(transfer.overlapped().address()); + availableOverlappedStructs.add(transfer.overlapped()); + transfer.setOverlapped(null); + } + + /** + * Completes the transfer by calling the completion handler. + * + * @param overlappedAddr address of OVERLAPPED struct + */ + private void completeTransfer(long overlappedAddr) { + WindowsTransfer transfer; + synchronized (this) { + transfer = requestsByOverlapped.remove(overlappedAddr); + if (transfer == null) + return; + + // the results must be read from the OVERLAPPED struct before it is + // returned to the pool and possibly reused by another submission + transfer.setResultCode((int) OVERLAPPED.Internal(transfer.overlapped())); + transfer.setResultSize((int) OVERLAPPED.InternalHigh(transfer.overlapped())); + + availableOverlappedStructs.add(transfer.overlapped()); + transfer.setOverlapped(null); + } + + // The completion handler must be called without holding the lock: handlers acquire + // other monitors (transfer, device), and threads submitting transfers acquire this + // task's lock while holding those monitors, so calling handlers under the lock can + // deadlock. + try { + transfer.completion().completed(transfer); + } catch (Exception e) { + // This method runs on the process-wide async IO thread. Any exception escaping + // would kill that thread and hang all async transfers for the entire library. + LOG.log(ERROR, "Unexpected exception while handling async IO completion", e); + } + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsEndpointInputStream.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsEndpointInputStream.java new file mode 100644 index 00000000..0c96fc94 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsEndpointInputStream.java @@ -0,0 +1,29 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.windows; + +import net.codecrete.usb.UsbDirection; +import net.codecrete.usb.common.EndpointInputStream; +import net.codecrete.usb.common.Transfer; + +public class WindowsEndpointInputStream extends EndpointInputStream { + + WindowsEndpointInputStream(WindowsUsbDevice device, int endpointNumber, int bufferSize) { + super(device, endpointNumber, bufferSize); + } + + @Override + protected void submitTransferIn(Transfer transfer) { + ((WindowsUsbDevice) device).submitTransferIn(endpointNumber, (WindowsTransfer) transfer); + } + + @Override + protected void configureEndpoint() { + ((WindowsUsbDevice) device).configureForAsyncIo(UsbDirection.IN, endpointNumber); + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsEndpointOutputStream.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsEndpointOutputStream.java new file mode 100644 index 00000000..8125dd5a --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsEndpointOutputStream.java @@ -0,0 +1,29 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.windows; + +import net.codecrete.usb.UsbDirection; +import net.codecrete.usb.common.EndpointOutputStream; +import net.codecrete.usb.common.Transfer; + +public class WindowsEndpointOutputStream extends EndpointOutputStream { + + WindowsEndpointOutputStream(WindowsUsbDevice device, int endpointNumber, int bufferSize) { + super(device, endpointNumber, bufferSize); + } + + @Override + protected void submitTransferOut(Transfer request) { + ((WindowsUsbDevice) device).submitTransferOut(endpointNumber, (WindowsTransfer) request); + } + + @Override + protected void configureEndpoint() { + ((WindowsUsbDevice) device).configureForAsyncIo(UsbDirection.OUT, endpointNumber); + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsTransfer.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsTransfer.java new file mode 100644 index 00000000..9b45f4bb --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsTransfer.java @@ -0,0 +1,24 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.windows; + +import net.codecrete.usb.common.Transfer; + +import java.lang.foreign.MemorySegment; + +class WindowsTransfer extends Transfer { + private MemorySegment overlapped; + + public MemorySegment overlapped() { + return overlapped; + } + + public void setOverlapped(MemorySegment overlapped) { + this.overlapped = overlapped; + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsUSBDevice.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsUSBDevice.java deleted file mode 100644 index f4fa3c7a..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsUSBDevice.java +++ /dev/null @@ -1,269 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// - -package net.codecrete.usb.windows; - -import net.codecrete.usb.*; -import net.codecrete.usb.common.DescriptorParser; -import net.codecrete.usb.common.USBDeviceImpl; -import net.codecrete.usb.common.USBStructs; -import net.codecrete.usb.windows.gen.kernel32.Kernel32; -import net.codecrete.usb.windows.gen.winusb.WinUSB; - -import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; -import java.util.List; - -import static java.lang.foreign.MemoryAddress.NULL; -import static java.lang.foreign.ValueLayout.*; - -/** - * Windows implementation for USB device. - */ -public class WindowsUSBDevice extends USBDeviceImpl { - - private final List functions; - private boolean isOpen_; - - WindowsUSBDevice(String devicePath, List functions, int vendorId, int productId, String manufacturer, String product, String serial, - MemorySegment configDesc) { - super(devicePath, vendorId, productId, manufacturer, product, serial); - this.functions = functions; - readDescription(configDesc); - } - - @Override - public boolean isOpen() { - return isOpen_; - } - - @Override - public void open() { - if (isOpen()) - throw new USBException("the device is already open"); - - isOpen_ = true; - } - - private void readDescription(MemorySegment configDesc) { - var configuration = DescriptorParser.parseConfigurationDescriptor(configDesc, vendorId(), productId()); - setInterfaces(configuration.interfaces); - } - - @Override - public void close() { - if (!isOpen()) - return; - - for (var intf : interfaces_) { - if (intf.isClaimed()) - releaseInterface(intf.number()); - } - - isOpen_ = false; - } - - private CompositeFunction findFunction(int interfaceNumber) { - return functions.stream() - .filter((func) -> func.firstInterfaceNumber() == interfaceNumber).findFirst().orElse(null); - } - - private CompositeFunction findAnyOpenFunction() { - return functions.stream() - .filter((func) -> func.firstInterfaceHandle() != null).findFirst().orElse(null); - } - - private CompositeFunction findControlTransferFunction(USBControlTransfer setup) { - - int interfaceNumber = -1; - int endpointNumber; - - if (setup.recipient() == USBRecipient.INTERFACE) { - - interfaceNumber = setup.index() & 0xff; - - } else if (setup.recipient() == USBRecipient.ENDPOINT) { - - endpointNumber = setup.index() & 0xff; - if (endpointNumber != 0) { - interfaceNumber = getInterfaceNumber(endpointNumber); - if (interfaceNumber == -1) - interfaceNumber = -2; - } - } - - CompositeFunction function = null; - if (interfaceNumber >= 0) { - function = findFunction(interfaceNumber); - } else if (interfaceNumber == -1) { - function = findAnyOpenFunction(); - } - - if (function == null || function.firstInterfaceHandle() == null) - throw new USBException("Interface not claimed for control transfer"); - - return function; - } - - public void claimInterface(int interfaceNumber) { - checkIsOpen(); - - var intf = getInterface(interfaceNumber); - if (intf == null) - throw new USBException(String.format("Invalid interface number: %d", interfaceNumber)); - if (intf.isClaimed()) - throw new USBException(String.format("Interface %d has already been claimed", interfaceNumber)); - var function = findFunction(interfaceNumber); - if (function == null) - throw new USBException(String.format("Interface number %d cannot be claimed (no DeviceInterfaceGUID?)", interfaceNumber)); - - try (var session = MemorySession.openConfined()) { - - // open Windows device - var pathSegment = Win.createSegmentFromString(function.devicePath(), session); - var deviceHandle = Kernel32.CreateFileW(pathSegment, Kernel32.GENERIC_WRITE() | Kernel32.GENERIC_READ(), - Kernel32.FILE_SHARE_WRITE() | Kernel32.FILE_SHARE_READ(), NULL, Kernel32.OPEN_EXISTING(), - Kernel32.FILE_ATTRIBUTE_NORMAL() | Kernel32.FILE_FLAG_OVERLAPPED(), NULL); - - if (Win.IsInvalidHandle(deviceHandle)) - throw new WindowsUSBException( - String.format("Cannot open USB device %s", function.devicePath()), Kernel32.GetLastError()); - - try { - // open interface - var interfaceHandleHolder = session.allocate(ADDRESS); - if (WinUSB.WinUsb_Initialize(deviceHandle, interfaceHandleHolder) == 0) - throw new WindowsUSBException("Cannot open WinUSB device", Kernel32.GetLastError()); - var interfaceHandle = interfaceHandleHolder.get(ADDRESS, 0); - - function.setDeviceHandle(deviceHandle); - function.setFirstInterfaceHandle(interfaceHandle); - - } catch (Throwable e) { - Kernel32.CloseHandle(deviceHandle); - throw e; - } - } - - setClaimed(interfaceNumber, true); - } - - public void releaseInterface(int interfaceNumber) { - checkIsOpen(); - - var intf = getInterface(interfaceNumber); - if (intf == null) - throw new USBException(String.format("Invalid interface number: %d", interfaceNumber)); - if (!intf.isClaimed()) - throw new USBException(String.format("Interface %d has not been claimed", interfaceNumber)); - - var function = findFunction(interfaceNumber); - assert function != null; - - if (function.deviceHandle() != null) { - WinUSB.WinUsb_Free(function.firstInterfaceHandle()); - function.setFirstInterfaceHandle(null); - Kernel32.CloseHandle(function.deviceHandle()); - function.setDeviceHandle(null); - } - - setClaimed(interfaceNumber, false); - } - - private MemorySegment createSetupPacket(MemorySession session, USBDirection direction, USBControlTransfer setup, - MemorySegment data) { - var setupPacket = session.allocate(USBStructs.SetupPacket$Struct); - var bmRequest = - (direction == USBDirection.IN ? 0x80 : 0) | (setup.requestType().ordinal() << 5) | setup.recipient().ordinal(); - USBStructs.SetupPacket_bmRequest.set(setupPacket, (byte) bmRequest); - USBStructs.SetupPacket_bRequest.set(setupPacket, setup.request()); - USBStructs.SetupPacket_wValue.set(setupPacket, setup.value()); - USBStructs.SetupPacket_wIndex.set(setupPacket, setup.index()); - USBStructs.SetupPacket_wLength.set(setupPacket, (short) (data != null ? data.byteSize() : 0)); - return setupPacket; - } - - @Override - public byte[] controlTransferIn(USBControlTransfer setup, int length) { - checkIsOpen(); - var function = findControlTransferFunction(setup); - - try (var session = MemorySession.openConfined()) { - var buffer = session.allocate(length); - var setupPacket = createSetupPacket(session, USBDirection.IN, setup, buffer); - var lengthHolder = session.allocate(JAVA_INT); - - if (WinUSB.WinUsb_ControlTransfer(function.firstInterfaceHandle(), setupPacket, buffer, (int) buffer.byteSize(), - lengthHolder, NULL) == 0) - throw new WindowsUSBException("Control transfer IN failed", Kernel32.GetLastError()); - - int rxLength = lengthHolder.get(JAVA_INT, 0); - return buffer.asSlice(0, rxLength).toArray(JAVA_BYTE); - } - } - - @Override - public void controlTransferOut(USBControlTransfer setup, byte[] data) { - checkIsOpen(); - var function = findControlTransferFunction(setup); - - try (var session = MemorySession.openConfined()) { - - // copy data to native memory - int dataLength = data != null ? data.length : 0; - MemorySegment buffer = session.allocate(dataLength); - if (dataLength != 0) - buffer.copyFrom(MemorySegment.ofArray(data)); - - // create setup packet - var setupPacket = createSetupPacket(session, USBDirection.OUT, setup, buffer); - var lengthHolder = session.allocate(JAVA_INT); - - if (WinUSB.WinUsb_ControlTransfer(function.firstInterfaceHandle(), setupPacket, buffer, (int) buffer.byteSize(), - lengthHolder, NULL) == 0) - throw new WindowsUSBException("Control transfer OUT failed", Kernel32.GetLastError()); - } - } - - @Override - public void transferOut(int endpointNumber, byte[] data) { - checkIsOpen(); - - var endpoint = getEndpoint(endpointNumber, USBDirection.OUT, USBTransferType.BULK, USBTransferType.INTERRUPT); - var function = findFunction(endpoint.interfaceNumber()); - assert function != null; - - try (var session = MemorySession.openConfined()) { - var buffer = session.allocate(data.length); - buffer.copyFrom(MemorySegment.ofArray(data)); - var lengthHolder = session.allocate(JAVA_INT); - - if (WinUSB.WinUsb_WritePipe(function.firstInterfaceHandle(), endpoint.endpointAddress(), buffer, (int) buffer.byteSize(), - lengthHolder, NULL) == 0) - throw new WindowsUSBException("Bulk/interrupt transfer OUT failed", Kernel32.GetLastError()); - } - } - - @Override - public byte[] transferIn(int endpointNumber, int maxLength) { - var endpoint = getEndpoint(endpointNumber, USBDirection.IN, USBTransferType.BULK, USBTransferType.INTERRUPT); - var function = findFunction(endpoint.interfaceNumber()); - assert function != null; - - try (var session = MemorySession.openConfined()) { - var buffer = session.allocate(maxLength); - var lengthHolder = session.allocate(JAVA_INT); - - if (WinUSB.WinUsb_ReadPipe(function.firstInterfaceHandle(), endpoint.endpointAddress(), buffer, (int) buffer.byteSize(), lengthHolder - , NULL) == 0) - throw new WindowsUSBException("Bulk/interrupt transfer IN failed", Kernel32.GetLastError()); - - int len = lengthHolder.get(JAVA_INT, 0); - return buffer.asSlice(0, len).toArray(JAVA_BYTE); - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsUSBDeviceRegistry.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsUSBDeviceRegistry.java deleted file mode 100644 index 702b83f6..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsUSBDeviceRegistry.java +++ /dev/null @@ -1,492 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// - -package net.codecrete.usb.windows; - -import net.codecrete.usb.USBDevice; -import net.codecrete.usb.USBException; -import net.codecrete.usb.common.*; -import net.codecrete.usb.windows.gen.kernel32.GUID; -import net.codecrete.usb.windows.gen.kernel32.Kernel32; -import net.codecrete.usb.windows.gen.ole32.Ole32; -import net.codecrete.usb.windows.gen.setupapi.SP_DEVICE_INTERFACE_DATA; -import net.codecrete.usb.windows.gen.setupapi.SP_DEVINFO_DATA; -import net.codecrete.usb.windows.gen.setupapi.SetupAPI; -import net.codecrete.usb.windows.gen.usbioctl.USBIoctl; -import net.codecrete.usb.windows.gen.user32.*; - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.regex.Pattern; - -import static java.lang.foreign.MemoryAddress.NULL; -import static java.lang.foreign.ValueLayout.*; - -/** - * Windows implementation of USB device registry. - *

- * To retrieve details of a USB device, this class accesses it indirectly - * via the parent. To address it the parent's handle (hub handle) and - * the device's port number is needed. - *

- */ -public class WindowsUSBDeviceRegistry extends USBDeviceRegistry { - - @Override - protected void monitorDevices() { - try (var session = MemorySession.openConfined()) { - - Addressable hwnd; - - try { - final var className = Win.createSegmentFromString("USB_MONITOR", session); - final var windowName = Win.createSegmentFromString("USB device monitor", session); - final var instance = Kernel32.GetModuleHandleW(NULL); - - // create upcall for handling window messages - var handleWindowMessageMH = MethodHandles.lookup().findVirtual(WindowsUSBDeviceRegistry.class, - "handleWindowMessage", MethodType.methodType(long.class, MemoryAddress.class, int.class, - long.class, long.class)).bindTo(this); - var handleWindowMessageStub = Linker.nativeLinker().upcallStub(handleWindowMessageMH, - FunctionDescriptor.of(JAVA_LONG, ADDRESS, JAVA_INT, JAVA_LONG, JAVA_LONG), session); - - // register window class - var wx = session.allocate(WNDCLASSEXW.$LAYOUT()); - WNDCLASSEXW.cbSize$set(wx, (int) wx.byteSize()); - WNDCLASSEXW.lpfnWndProc$set(wx, handleWindowMessageStub.address()); - WNDCLASSEXW.hInstance$set(wx, instance); - WNDCLASSEXW.lpszClassName$set(wx, className.address()); - User32.RegisterClassExW(wx); - - // create message-only window - hwnd = User32.CreateWindowExW(0, className, windowName, 0, 0, 0, 0, 0, User32.HWND_MESSAGE(), NULL, - instance, NULL); - if (hwnd == NULL) - throw new WindowsUSBException("internal error (CreateWindowExW)", Kernel32.GetLastError()); - - // configure notifications - var notificationFilter = session.allocate(DEV_BROADCAST_DEVICEINTERFACE_W.$LAYOUT()); - DEV_BROADCAST_DEVICEINTERFACE_W.dbcc_size$set(notificationFilter, (int) notificationFilter.byteSize()); - DEV_BROADCAST_DEVICEINTERFACE_W.dbcc_devicetype$set(notificationFilter, - User32.DBT_DEVTYP_DEVICEINTERFACE()); - DEV_BROADCAST_DEVICEINTERFACE_W.dbcc_classguid$slice(notificationFilter).copyFrom(USBHelper.GUID_DEVINTERFACE_USB_DEVICE); - - var notifyHandle = User32.RegisterDeviceNotificationW(hwnd, notificationFilter, - User32.DEVICE_NOTIFY_WINDOW_HANDLE()); - if (notifyHandle == NULL) - throw new WindowsUSBException("internal error (RegisterDeviceNotificationW)", Kernel32.GetLastError()); - - // initial device enumeration - enumeratePresentDevices(); - - } catch (Throwable e) { - enumerationFailed(e); - return; - } - - // process messages - var msg = session.allocate(MSG.$LAYOUT()); - //noinspection StatementWithEmptyBody - while (User32.GetMessageW(msg, hwnd, 0, 0) > 0) - ; // do nothing - } - } - - private void enumeratePresentDevices() { - - List deviceList = new ArrayList<>(); - try (var outerSession = MemorySession.openConfined()) { - - // get device information set of all USB devices present - final var devInfoSetHandle = SetupAPI.SetupDiGetClassDevsW(USBHelper.GUID_DEVINTERFACE_USB_DEVICE, NULL, - NULL, SetupAPI.DIGCF_PRESENT() | SetupAPI.DIGCF_DEVICEINTERFACE()); - if (Win.IsInvalidHandle(devInfoSetHandle)) - throw new USBException("internal error (SetupDiGetClassDevsW)"); - - // ensure the result is destroyed when the scope is left - outerSession.addCloseAction(() -> SetupAPI.SetupDiDestroyDeviceInfoList(devInfoSetHandle)); - - var devInfo = MemorySegment.allocateNative(SP_DEVINFO_DATA.$LAYOUT(), outerSession); - SP_DEVINFO_DATA.cbSize$set(devInfo, (int) SP_DEVINFO_DATA.$LAYOUT().byteSize()); - - // ensure all hubs are closed later - final var hubHandles = new HashMap(); - outerSession.addCloseAction(() -> hubHandles.forEach((path, handle) -> Kernel32.CloseHandle(handle))); - - // iterate all devices - for (int i = 0; true; i++) { - - if (SetupAPI.SetupDiEnumDeviceInfo(devInfoSetHandle, i, devInfo) == 0) { - int err = Kernel32.GetLastError(); - // TODO: Remove check for ERROR_SUCCESS if proper GetLastError() handling is available - if (err == Kernel32.ERROR_NO_MORE_ITEMS() || err == Kernel32.ERROR_SUCCESS()) - break; - throw new USBException("Internal error (SetupDiEnumDeviceInfo) "); - } - - var instanceID = DeviceProperty.getDeviceStringProperty(devInfoSetHandle, devInfo, - DeviceProperty.DEVPKEY_Device_InstanceId); - var devicePath = DeviceProperty.getDevicePath(instanceID, USBHelper.GUID_DEVINTERFACE_USB_DEVICE); - - try { - deviceList.add(createDeviceFromDeviceInfo(devInfoSetHandle, devInfo, devicePath, hubHandles)); - - } catch (Throwable e) { - System.err.printf("Info: [JavaDoesUSB] failed to retrieve information about device %s - ignoring " - + "device%n", devicePath); - e.printStackTrace(System.err); - } - } - - setInitialDeviceList(deviceList); - } - } - - /** - * Gets the functions of the children of a composite device - * - * @param childrenIds the children IDs - * @return a list of functions (interface number and device path) - */ - private List getCompositeFunctions(List childrenIds) { - - var functions = new ArrayList(); - - // iterate all children - for (var instanceId : childrenIds) { - try (var session = MemorySession.openConfined()) { - // create device info set - var devInfoSetHandle = SetupAPI.SetupDiCreateDeviceInfoList(NULL, NULL); - if (Win.IsInvalidHandle(devInfoSetHandle)) - throw new WindowsUSBException("Cannot create device info list", Kernel32.GetLastError()); - session.addCloseAction(() -> SetupAPI.SetupDiDestroyDeviceInfoList(devInfoSetHandle)); - - // get device info for child - var devInfo = session.allocate(SP_DEVINFO_DATA.$LAYOUT()); - SP_DEVINFO_DATA.cbSize$set(devInfo, (int) devInfo.byteSize()); - var instanceIdSegment = Win.createSegmentFromString(instanceId, session); - if (SetupAPI.SetupDiOpenDeviceInfoW(devInfoSetHandle, instanceIdSegment, NULL, 0, devInfo) == 0) - throw new WindowsUSBException("Internal error (SetupDiOpenDeviceInfoW)", Kernel32.GetLastError()); - - // get hardware IDs (to extract interface number) - var hardwareIds = DeviceProperty.getDeviceStringListProperty(devInfoSetHandle, devInfo, - DeviceProperty.DEVPKEY_Device_HardwareIds); - int interfaceNumber = extractInterfaceNumber(hardwareIds); - if (interfaceNumber == -1) - continue; - - var guids = DeviceProperty.findDeviceInterfaceGUIDs(devInfoSetHandle, devInfo, session); - - for (var guid : guids) { - // check for Class GUID - var guidSegment = Win.createSegmentFromString(guid, session); - var clsid = session.allocate(GUID.$LAYOUT()); - if (Ole32.CLSIDFromString(guidSegment, clsid) != 0) - continue; - - try { - var devicePath = DeviceProperty.getDevicePath(instanceId, clsid); - functions.add(new CompositeFunction(interfaceNumber, devicePath)); - break; - } catch (Exception e) { - // ignore and try next one - } - } - } - - } - - return functions; - } - - private USBDevice createDeviceFromDeviceInfo(MemoryAddress devInfoSetHandle, MemorySegment devInfo, - String devicePath, HashMap hubHandles) { - try (var session = MemorySession.openConfined()) { - - var usbPortNum = DeviceProperty.getDeviceIntProperty(devInfoSetHandle, devInfo, - DeviceProperty.DEVPKEY_Device_Address); - var parentInstanceID = DeviceProperty.getDeviceStringProperty(devInfoSetHandle, devInfo, - DeviceProperty.DEVPKEY_Device_Parent); - var hubPath = DeviceProperty.getDevicePath(parentInstanceID, USBHelper.GUID_DEVINTERFACE_USB_HUB); - - // open hub if not open yet - var hubHandle = hubHandles.get(hubPath); - if (hubHandle == null) { - var hubPathSeg = Win.createSegmentFromString(hubPath, session); - hubHandle = Kernel32.CreateFileW(hubPathSeg, Kernel32.GENERIC_WRITE(), Kernel32.FILE_SHARE_WRITE(), - NULL, Kernel32.OPEN_EXISTING(), 0, NULL); - if (Win.IsInvalidHandle(hubHandle)) - throw new USBException("Cannot open USB hub", Kernel32.GetLastError()); - hubHandles.put(hubPath, hubHandle); - } - - // check for composite device - var deviceService = DeviceProperty.getDeviceStringProperty(devInfoSetHandle, devInfo, - DeviceProperty.DEVPKEY_Device_Service); - - List functions; - if (isCompositeDevice(deviceService)) { - functions = getCompositeFunctions(DeviceProperty.getDeviceStringListProperty(devInfoSetHandle, devInfo, - DeviceProperty.DEVPKEY_Device_Children)); - } else { - functions = new ArrayList<>(); - functions.add(new CompositeFunction(0, devicePath)); - } - - return createDevice(devicePath, functions, hubHandle, usbPortNum); - } - } - - /** - * Retrieve device descriptor and create {@code USBDevice} instance - * - * @param functions composite functions - * @param hubHandle the hub handle (parent) - * @param usbPortNum the USB port number - * @return the {@code USBDevice} instance - */ - private USBDevice createDevice(String devicePath, List functions, MemoryAddress hubHandle, - int usbPortNum) { - - try (var session = MemorySession.openConfined()) { - - // get device descriptor - var connInfo = session.allocate(USBHelper.USB_NODE_CONNECTION_INFORMATION_EX$Struct); - USBHelper.USB_NODE_CONNECTION_INFORMATION_EX_ConnectionIndex.set(connInfo, usbPortNum); - var sizeHolder = session.allocate(JAVA_INT); - if (Kernel32.DeviceIoControl(hubHandle, USBIoctl.IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX(), connInfo - , (int) connInfo.byteSize(), connInfo, (int) connInfo.byteSize(), sizeHolder, NULL) == 0) - throw new WindowsUSBException("Internal error (cannot get device descriptor)", Kernel32.GetLastError()); - - var deviceDesc = USBHelper.USB_NODE_CONNECTION_INFORMATION_EX_DeviceDescriptor$slice(connInfo); - - // extract info from device descriptor - int manufacturerIndex = 255 & (byte) USBDescriptors.Device_iManufacturer.get(deviceDesc); - String manufacturer = getStringDescriptor(hubHandle, usbPortNum, manufacturerIndex); - int productIndex = 255 & (byte) USBDescriptors.Device_iProduct.get(deviceDesc); - String product = getStringDescriptor(hubHandle, usbPortNum, productIndex); - int serialNumberIndex = 255 & (byte) USBDescriptors.Device_iSerialNumber.get(deviceDesc); - String serialNumber = getStringDescriptor(hubHandle, usbPortNum, serialNumberIndex); - - int vendorId = 0xffff & (short) USBDescriptors.Device_idVendor.get(deviceDesc); - int productId = 0xffff & (short) USBDescriptors.Device_idProduct.get(deviceDesc); - - var configDesc = getDescriptor(hubHandle, usbPortNum, USBDescriptors.CONFIGURATION_DESCRIPTOR_TYPE, 0, - (short) 0, session); - - var device = new WindowsUSBDevice(devicePath, functions, vendorId, productId, manufacturer, product, - serialNumber, configDesc); - - int classCode = 255 & (byte) USBDescriptors.Device_bDeviceClass.get(deviceDesc); - int subclassCode = 255 & (byte) USBDescriptors.Device_bDeviceSubClass.get(deviceDesc); - int protocolCode = 255 & (byte) USBDescriptors.Device_bDeviceProtocol.get(deviceDesc); - device.setClassCodes(classCode, subclassCode, protocolCode); - - var usbVersion = (short) USBDescriptors.Device_bcdUSB.get(deviceDesc); - var deviceVersion = (short) USBDescriptors.Device_bcdDevice.get(deviceDesc); - device.setVersions(usbVersion, deviceVersion); - - return device; - } - } - - private MemorySegment getDescriptor(Addressable hubHandle, int usbPortNumber, int descriptorType, int index, - short languageID, MemorySession session) { - return getDescriptor(hubHandle, usbPortNumber, descriptorType, index, languageID, 0, session); - - } - - private MemorySegment getDescriptor(Addressable hubHandle, int usbPortNumber, int descriptorType, int index, - short languageID, int requestSize, MemorySession session) { - - int size = requestSize != 0 ? requestSize + (int) USBHelper.USB_DESCRIPTOR_REQUEST_Data$Offset : 256; - - // create descriptor requests - var descriptorRequest = session.allocate(size); - USBHelper.USB_DESCRIPTOR_REQUEST_ConnectionIndex.set(descriptorRequest, usbPortNumber); - var setupPacket = descriptorRequest.asSlice(USBHelper.USB_DESCRIPTOR_REQUEST_SetupPacket$Offset, - USBStructs.SetupPacket$Struct.byteSize()); - USBStructs.SetupPacket_bmRequest.set(setupPacket, (byte) 0x80); // device-to-host / type standard / recipient - // device - USBStructs.SetupPacket_bRequest.set(setupPacket, USBHelper.USB_REQUEST_GET_DESCRIPTOR); - USBStructs.SetupPacket_wValue.set(setupPacket, (short) ((descriptorType << 8) | index)); - USBStructs.SetupPacket_wIndex.set(setupPacket, languageID); - USBStructs.SetupPacket_wLength.set(setupPacket, (short) (size - USBHelper.USB_DESCRIPTOR_REQUEST_Data$Offset)); - - // execute request - var effectiveSizeHolder = session.allocate(JAVA_INT); - if (Kernel32.DeviceIoControl(hubHandle, USBIoctl.IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION(), - descriptorRequest, size, descriptorRequest, size, effectiveSizeHolder, NULL) == 0) - throw new WindowsUSBException(String.format("Cannot retrieve descriptor %d", index), Kernel32.GetLastError()); - - // determine size of descriptor - int expectedSize; - if (descriptorType != USBDescriptors.CONFIGURATION_DESCRIPTOR_TYPE) { - expectedSize = 255 & descriptorRequest.get(JAVA_BYTE, USBHelper.USB_DESCRIPTOR_REQUEST_Data$Offset); - } else { - var configDesc = descriptorRequest.asSlice(USBHelper.USB_DESCRIPTOR_REQUEST_Data$Offset, - USBDescriptors.Configuration.byteSize()); - expectedSize = (short) USBDescriptors.Configuration_wTotalLength.get(configDesc); - } - - // check against effective size - var effectiveSize = effectiveSizeHolder.get(JAVA_INT, 0) - USBHelper.USB_DESCRIPTOR_REQUEST_Data$Offset; - if (effectiveSize != expectedSize) { - if (requestSize != 0) - throw new USBException("Unexpected descriptor size"); - - // repeat with correct size - return getDescriptor(hubHandle, usbPortNumber, descriptorType, index, languageID, expectedSize, session); - } - - return descriptorRequest.asSlice(USBHelper.USB_DESCRIPTOR_REQUEST_Data$Offset, effectiveSize); - } - - private String getStringDescriptor(Addressable hubHandle, int usbPortNumber, int index) { - if (index == 0) - return null; - - try (var session = MemorySession.openConfined()) { - var stringDesc = getDescriptor(hubHandle, usbPortNumber, USBDescriptors.STRING_DESCRIPTOR_TYPE, index, - USBDescriptors.DEFAULT_LANGUAGE, session); - - int stringLen = 255 & (byte) USBHelper.USB_STRING_DESCRIPTOR_bLength.get(stringDesc); - var chars = - stringDesc.asSlice(USBHelper.USB_STRING_DESCRIPTOR_bString$Offset, stringLen - 2).toArray(JAVA_CHAR); - return new String(chars); - } - } - - private long handleWindowMessage(MemoryAddress hWnd, int uMsg, long wParam, long lParam) { - - // check for message related to connecting/disconnecting devices - if (uMsg == User32.WM_DEVICECHANGE() && (wParam == User32.DBT_DEVICEARRIVAL() || wParam == User32.DBT_DEVICEREMOVECOMPLETE())) { - try (var session = MemorySession.openConfined()) { - var data = MemorySegment.ofAddress(MemoryAddress.ofLong(lParam), - DEV_BROADCAST_DEVICEINTERFACE_W.sizeof(), session); - if (DEV_BROADCAST_HDR.dbch_devicetype$get(data) == User32.DBT_DEVTYP_DEVICEINTERFACE()) { - - // get device path - var nameSlice = - MemorySegment.ofAddress(DEV_BROADCAST_DEVICEINTERFACE_W.dbcc_name$slice(data).address(), - 500, session); - var devicePath = Win.createStringFromSegment(nameSlice); - if (wParam == User32.DBT_DEVICEARRIVAL()) - onDeviceConnected(devicePath); - else - onDeviceDisconnected(devicePath); - return 0; - } - } - } - - // default message handling - return User32.DefWindowProcW(hWnd, uMsg, wParam, lParam); - } - - private void onDeviceConnected(String devicePath) { - try (var session = MemorySession.openConfined()) { - - // get device information set of all USB devices present - final var devInfoSetHandle = SetupAPI.SetupDiGetClassDevsW(USBHelper.GUID_DEVINTERFACE_USB_DEVICE, NULL, - NULL, SetupAPI.DIGCF_PRESENT() | SetupAPI.DIGCF_DEVICEINTERFACE()); - if (Win.IsInvalidHandle(devInfoSetHandle)) - throw new WindowsUSBException("internal error (SetupDiGetClassDevsW)", Kernel32.GetLastError()); - - // ensure the result is destroyed when the scope is left - session.addCloseAction(() -> SetupAPI.SetupDiDestroyDeviceInfoList(devInfoSetHandle)); - - var devIntfData = session.allocate(SP_DEVICE_INTERFACE_DATA.$LAYOUT()); - SP_DEVICE_INTERFACE_DATA.cbSize$set(devIntfData, (int) devIntfData.byteSize()); - var devicePathSegment = Win.createSegmentFromString(devicePath, session); - if (SetupAPI.SetupDiOpenDeviceInterfaceW(devInfoSetHandle, devicePathSegment, 0, devIntfData) == 0) - throw new WindowsUSBException("internal error (SetupDiOpenDeviceInterfaceW)", Kernel32.GetLastError()); - - ForeignMemory.addCloseAction(devIntfData, (segment) -> SetupAPI.SetupDiDeleteDeviceInterfaceData(devInfoSetHandle, segment)); - - var devInfo = session.allocate(SP_DEVINFO_DATA.$LAYOUT()); - SP_DEVINFO_DATA.cbSize$set(devInfo, (int) devInfo.byteSize()); - if (SetupAPI.SetupDiGetDeviceInterfaceDetailW(devInfoSetHandle, devIntfData, NULL, 0, NULL, devInfo) == 0) { - int err = Kernel32.GetLastError(); - if (err != Kernel32.ERROR_INSUFFICIENT_BUFFER()) - throw new USBException("internal error (SetupDiGetDeviceInterfaceDetailW)", err); - } - - // ensure all hubs are closed later - final var hubHandles = new HashMap(); - session.addCloseAction(() -> hubHandles.forEach((path, handle) -> Kernel32.CloseHandle(handle))); - - try { - // create device instance - var device = createDeviceFromDeviceInfo(devInfoSetHandle, devInfo, devicePath, hubHandles); - - // add it to device list - addDevice(device); - - } catch (Throwable e) { - System.err.printf("Info: [JavaDoesUSB] failed to retrieve information about device %s - ignoring " + - "device%n", devicePath); - e.printStackTrace(System.err); - } - } - } - - private void onDeviceDisconnected(String devicePath) { - closeAndRemoveDevice(devicePath); - } - - /** - * Finds the index of the device in the list. - *

- * This override uses a case-insensitive string comparison as Windows uses different casing - * when initially enumerating devices and during later monitoring. - *

- * - * @param deviceList the device list - * @param deviceId the unique device ID - * @return index, or -1 if not found - */ - @Override - protected int findDeviceIndex(List deviceList, Object deviceId) { - var id = deviceId.toString(); - for (int i = 0; i < deviceList.size(); i++) { - var dev = (USBDeviceImpl) deviceList.get(i); - if (id.equalsIgnoreCase(dev.getUniqueId().toString())) - return i; - } - return -1; - } - - private static boolean isCompositeDevice(String deviceService) { - // usbccgp is the USB Generic Parent Driver used for composite devices - return "usbccgp".equalsIgnoreCase(deviceService); - } - - private static final Pattern MULTIPLE_INTERFACE_ID = Pattern.compile("USB\\\\VID_[0-9A-Fa-f]{4}&PID_[0-9A-Fa-f]{4}&MI_([0-9A-Fa-f]{2})"); - - private static int extractInterfaceNumber(List hardwareIds) { - // Also see https://docs.microsoft.com/en-us/windows-hardware/drivers/install/standard-usb-identifiers#multiple-interface-usb-devices - - for (var id : hardwareIds) { - var matcher = MULTIPLE_INTERFACE_ID.matcher(id); - if (matcher.find()) { - var intfHexNumber = matcher.group(1); - try { - return Integer.parseInt(intfHexNumber, 16); - } catch (NumberFormatException e) { - // ignore and try next one - } - } - } - - return -1; - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsUSBException.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsUSBException.java deleted file mode 100644 index c6463801..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsUSBException.java +++ /dev/null @@ -1,43 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// -package net.codecrete.usb.windows; - -import net.codecrete.usb.USBException; -import net.codecrete.usb.windows.gen.kernel32.Kernel32; - -import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; - -import static java.lang.foreign.MemoryAddress.NULL; -import static java.lang.foreign.ValueLayout.ADDRESS; - -public class WindowsUSBException extends USBException { - public WindowsUSBException(String message) { - super(message); - } - - public WindowsUSBException(String message, int errorCode) { - super(String.format("%s - %s", message, getErrorMessage(errorCode)), errorCode); - } - - public WindowsUSBException(String message, Throwable cause) { - super(message, cause); - } - - private static String getErrorMessage(int errorCode) { - try (var session = MemorySession.openConfined()) { - var messagePointerHolder = session.allocate(ADDRESS); - Kernel32.FormatMessageW(Kernel32.FORMAT_MESSAGE_ALLOCATE_BUFFER() - | Kernel32.FORMAT_MESSAGE_FROM_SYSTEM() | Kernel32.FORMAT_MESSAGE_IGNORE_INSERTS(), - NULL, errorCode, 0, messagePointerHolder, 0, NULL); - var messagePointer = messagePointerHolder.get(ADDRESS, 0); - String message = Win.createStringFromSegment(MemorySegment.ofAddress(messagePointer, 4000, session)); - Kernel32.LocalFree(messagePointer); - return message.trim(); - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsUsbDevice.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsUsbDevice.java new file mode 100644 index 00000000..b98839c2 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsUsbDevice.java @@ -0,0 +1,669 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.windows; + +import net.codecrete.usb.UsbControlTransfer; +import net.codecrete.usb.UsbDirection; +import net.codecrete.usb.UsbException; +import net.codecrete.usb.UsbRecipient; +import net.codecrete.usb.UsbTransferType; +import net.codecrete.usb.common.Transfer; +import net.codecrete.usb.common.UsbDeviceImpl; +import net.codecrete.usb.usbstandard.SetupPacket; +import org.jetbrains.annotations.NotNull; + +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import static java.lang.System.Logger.Level.DEBUG; +import static java.lang.System.Logger.Level.INFO; +import static java.lang.foreign.MemorySegment.NULL; +import static java.lang.foreign.ValueLayout.ADDRESS; +import static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static java.lang.foreign.ValueLayout.JAVA_INT; +import static java.nio.charset.StandardCharsets.UTF_16LE; +import static net.codecrete.usb.common.ForeignMemory.dereference; +import static net.codecrete.usb.windows.CustomApis.CloseHandle; +import static net.codecrete.usb.windows.CustomApis.WinUsb_ControlTransfer; +import static net.codecrete.usb.windows.Win.allocateErrorState; +import static net.codecrete.usb.windows.WindowsUsbException.throwException; +import static net.codecrete.usb.windows.WindowsUsbException.throwLastError; +import static windows.win32.devices.properties.Constants.DEVPKEY_Device_Children; +import static windows.win32.devices.properties.Constants.DEVPKEY_Device_HardwareIds; +import static windows.win32.devices.usb.Apis.WinUsb_AbortPipe; +import static windows.win32.devices.usb.Apis.WinUsb_Free; +import static windows.win32.devices.usb.Apis.WinUsb_GetAssociatedInterface; +import static windows.win32.devices.usb.Apis.WinUsb_Initialize; +import static windows.win32.devices.usb.Apis.WinUsb_ReadPipe; +import static windows.win32.devices.usb.Apis.WinUsb_ResetPipe; +import static windows.win32.devices.usb.Apis.WinUsb_SetCurrentAlternateSetting; +import static windows.win32.devices.usb.Apis.WinUsb_SetPipePolicy; +import static windows.win32.devices.usb.Apis.WinUsb_WritePipe; +import static windows.win32.devices.usb.WINUSB_PIPE_POLICY.PIPE_TRANSFER_TIMEOUT; +import static windows.win32.devices.usb.WINUSB_PIPE_POLICY.RAW_IO; +import static windows.win32.foundation.GENERIC_ACCESS_RIGHTS.GENERIC_READ; +import static windows.win32.foundation.GENERIC_ACCESS_RIGHTS.GENERIC_WRITE; +import static windows.win32.foundation.WIN32_ERROR.ERROR_INVALID_PARAMETER; +import static windows.win32.foundation.WIN32_ERROR.ERROR_IO_PENDING; +import static windows.win32.storage.filesystem.Apis.CreateFileW; +import static windows.win32.storage.filesystem.FILE_CREATION_DISPOSITION.OPEN_EXISTING; +import static windows.win32.storage.filesystem.FILE_FLAGS_AND_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL; +import static windows.win32.storage.filesystem.FILE_FLAGS_AND_ATTRIBUTES.FILE_FLAG_OVERLAPPED; +import static windows.win32.storage.filesystem.FILE_SHARE_MODE.FILE_SHARE_READ; +import static windows.win32.storage.filesystem.FILE_SHARE_MODE.FILE_SHARE_WRITE; + +/** + * Windows implementation for USB device. + */ +@SuppressWarnings("java:S2160") +public class WindowsUsbDevice extends UsbDeviceImpl { + + private static final System.Logger LOG = System.getLogger(WindowsUsbDevice.class.getName()); + + private final WindowsAsyncTask asyncTask; + /** + * Indicates if the device is a composite device + */ + private final boolean isComposite; + + private List interfaceHandles; + + // device paths by interface number (first interface of function) + private Map devicePaths; + + /** + * Indicates if {@link #open()} has been called. Since separate interfaces can have separate underlying + * Windows device, {@link #claimInterface(int)} instead of {@link #open()} will open the Windows device. + * (volatile: written under the device monitor, read unlocked via {@link #isOpened()}) + */ + private volatile boolean showAsOpen; + + WindowsUsbDevice(String devicePath, int vendorId, int productId, MemorySegment configDesc, boolean isComposite) { + super(devicePath, vendorId, productId); + asyncTask = WindowsAsyncTask.INSTANCE; + this.isComposite = isComposite; + if (isComposite) + devicePaths = new HashMap<>(); + readDescription(configDesc); + } + + private void readDescription(MemorySegment configDesc) { + var configuration = setConfigurationDescriptor(configDesc); + + // build list of interface handles + interfaceHandles = configuration.interfaces().stream() + .map(intf -> { + var interfaceNumber = intf.getNumber(); + var function = configuration.findFunction(interfaceNumber); + return new InterfaceHandle(interfaceNumber, function.firstInterfaceNumber()); + }). + toList(); + } + + @Override + public boolean isOpened() { + return showAsOpen; + } + + @Override + public synchronized void open() { + checkIsClosed("device is already open"); + showAsOpen = true; + } + + @Override + public synchronized void close() { + if (!isOpened()) + return; + + for (var intf : interfaceList) { + if (intf.isClaimed()) + releaseInterface(intf.getNumber()); + } + + showAsOpen = false; + } + + public void claimInterface(int interfaceNumber) { + // When a device is plugged in, a notification is sent. For composite devices, it is a notification + // that the composite device is ready. Each composite function will be registered separately and + // the related information will be available with a delay. So for composite functions, several + // retries might be needed until the device path is available. + var numRetries = 30; // 30 x 100ms + // Defer interruption: keep a local flag instead of re-asserting the interrupt + // (which would make the remaining backoff sleeps throw immediately and defeat + // the retry delay). Re-assert when leaving the method. + var wasInterrupted = false; + try { + while (true) { + if (claimInterfaceSynchronized(interfaceNumber)) + return; // success + + numRetries -= 1; + if (numRetries == 0) + throw new UsbException("claiming interface failed (function has no device path / interface GUID, might be missing WinUSB driver)"); + + // sleep and retry + try { + LOG.log(DEBUG, "Sleeping for 100ms..."); + //noinspection BusyWait + Thread.sleep(100); + } catch (InterruptedException _) { + wasInterrupted = true; + } + } + } finally { + if (wasInterrupted) + Thread.currentThread().interrupt(); + } + } + + @SuppressWarnings("java:S3776") + private synchronized boolean claimInterfaceSynchronized(int interfaceNumber) { + checkIsOpen(); + + getInterfaceWithCheck(interfaceNumber, false); + + var intfHandle = getInterfaceHandle(interfaceNumber); + var firstIntfHandle = intfHandle; + if (intfHandle.firstInterfaceNumber != interfaceNumber) + firstIntfHandle = getInterfaceHandle(intfHandle.firstInterfaceNumber); + + var deviceOpenedHere = false; + + try (var arena = Arena.ofConfined()) { + var errorState = allocateErrorState(arena); + + // both the device and the first interface must be opened for any interface belonging to the same function + if (firstIntfHandle.deviceHandle == null) { + var devicePath = getInterfaceDevicePath(firstIntfHandle.interfaceNumber); + if (devicePath == null) + return false; // retry later + + LOG.log(DEBUG, "opening device {0}", devicePath); + + // open Windows device if needed + var pathSegment = arena.allocateFrom(devicePath, UTF_16LE); + var deviceHandle = CreateFileW(errorState, pathSegment, GENERIC_WRITE | GENERIC_READ, + FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + + if (Win.isInvalidHandle(deviceHandle)) + throwLastError(errorState, "claiming interface failed (opening USB device %s failed)", devicePath); + + MemorySegment interfaceHandle = null; + try { + // open first interface + var interfaceHandleHolder = arena.allocate(ADDRESS); + if (WinUsb_Initialize(errorState, deviceHandle, interfaceHandleHolder) == 0) { + if (Win.getLastError(errorState) == ERROR_INVALID_PARAMETER) + throw new UsbException( + "claiming interface failed (required WinUSB driver is probably not installed for the device)", + ERROR_INVALID_PARAMETER + ); + throwLastError(errorState, "claiming interface failed"); + } + interfaceHandle = dereference(interfaceHandleHolder); + + asyncTask.addDevice(deviceHandle); + + // Assign handles only after all fallible operations have succeeded. + // Otherwise a later claim would see them and submit I/O on a closed handle. + firstIntfHandle.deviceHandle = deviceHandle; + firstIntfHandle.winusbHandle = interfaceHandle; + deviceOpenedHere = true; + + } catch (Exception e) { + if (interfaceHandle != null) + WinUsb_Free(interfaceHandle); + CloseHandle(deviceHandle); + throw e; + } + } + + if (intfHandle != firstIntfHandle) { + try { + // open associated interface + var interfaceHandleHolder = arena.allocate(ADDRESS); + if (WinUsb_GetAssociatedInterface(errorState, firstIntfHandle.winusbHandle, + (byte) (intfHandle.interfaceNumber - firstIntfHandle.interfaceNumber - 1), + interfaceHandleHolder) == 0) + throwLastError(errorState, "claiming (associated) interface failed"); + intfHandle.winusbHandle = dereference(interfaceHandleHolder); + + } catch (Exception e) { + if (deviceOpenedHere) { + // no interface has been claimed yet, so close() would never release the device + WinUsb_Free(firstIntfHandle.winusbHandle); + CloseHandle(firstIntfHandle.deviceHandle); + firstIntfHandle.winusbHandle = null; + firstIntfHandle.deviceHandle = null; + } + throw e; + } + } + } + + firstIntfHandle.deviceOpenCount += 1; + setClaimed(interfaceNumber, true); + return true; + } + + @Override + public synchronized void selectAlternateSetting(int interfaceNumber, int alternateNumber) { + checkIsOpen(); + + var intf = getInterfaceWithCheck(interfaceNumber, true); + var intfHandle = getInterfaceHandle(interfaceNumber); + + // check alternate setting + var altSetting = intf.getAlternate(alternateNumber); + + try (var arena = Arena.ofConfined()) { + var errorState = allocateErrorState(arena); + if (WinUsb_SetCurrentAlternateSetting(errorState, intfHandle.winusbHandle, (byte) alternateNumber) == 0) + throwLastError(errorState, "setting alternate interface failed"); + } + intf.setAlternate(altSetting); + } + + public synchronized void releaseInterface(int interfaceNumber) { + checkIsOpen(); + + getInterfaceWithCheck(interfaceNumber, true); + + var intfHandle = getInterfaceHandle(interfaceNumber); + var firstIntfHandle = intfHandle; + if (intfHandle.firstInterfaceNumber != interfaceNumber) + firstIntfHandle = getInterfaceHandle(intfHandle.firstInterfaceNumber); + + setClaimed(interfaceNumber, false); + + if (intfHandle != firstIntfHandle) { + // close associated interface + WinUsb_Free(intfHandle.winusbHandle); + intfHandle.winusbHandle = null; + } + + // close device if needed + firstIntfHandle.deviceOpenCount -= 1; + if (firstIntfHandle.deviceOpenCount == 0) { + WinUsb_Free(firstIntfHandle.winusbHandle); + firstIntfHandle.winusbHandle = null; + + LOG.log(DEBUG, "closing device {0}", getCachedInterfaceDevicePath(interfaceNumber)); + + CloseHandle(firstIntfHandle.deviceHandle); + firstIntfHandle.deviceHandle = null; + } + } + + @Override + public void controlTransferOut(@NotNull UsbControlTransfer setup, byte[] data) { + try (var arena = Arena.ofConfined()) { + + // copy data to native memory + var transfer = createSyncControlTransfer(); + var dataLength = data != null ? data.length : 0; + transfer.setDataSize(dataLength); + if (dataLength != 0) { + var buffer = arena.allocate(data.length); + buffer.copyFrom(MemorySegment.ofArray(data)); + transfer.setData(buffer); + } else { + transfer.setData(NULL); + } + + synchronized (transfer) { + submitControlTransfer(UsbDirection.OUT, setup, transfer); + waitForTransfer(transfer, 0, UsbDirection.OUT, 0); + } + } + } + + @Override + public byte @NotNull [] controlTransferIn(@NotNull UsbControlTransfer setup, int length) { + try (var arena = Arena.ofConfined()) { + var transfer = createSyncControlTransfer(); + transfer.setData(arena.allocate(length)); + transfer.setDataSize(length); + + synchronized (transfer) { + submitControlTransfer(UsbDirection.IN, setup, transfer); + waitForTransfer(transfer, 0, UsbDirection.IN, 0); + } + + return transfer.data().asSlice(0, transfer.resultSize()).toArray(JAVA_BYTE); + } + } + + @Override + public void transferOut(int endpointNumber, byte @NotNull [] data, int offset, int length, int timeout) { + // Auto arena: a transfer that times out may be abandoned (see UsbDeviceImpl.waitForTransfer), + // so the buffer must outlive a possible late completion instead of being freed deterministically. + var arena = Arena.ofAuto(); + var buffer = arena.allocate(data.length); + buffer.copyFrom(MemorySegment.ofArray(data).asSlice(offset, length)); + var transfer = createSyncTransfer(buffer); + + synchronized (transfer) { + submitTransferOut(endpointNumber, transfer); + waitForTransfer(transfer, timeout, UsbDirection.OUT, endpointNumber); + } + } + + @Override + public byte @NotNull [] transferIn(int endpointNumber, int timeout) { + var endpoint = getEndpoint(UsbDirection.IN, endpointNumber, UsbTransferType.BULK, UsbTransferType.INTERRUPT); + + // Auto arena: a transfer that times out may be abandoned (see UsbDeviceImpl.waitForTransfer), + // so the buffer must outlive a possible late completion instead of being freed deterministically. + var arena = Arena.ofAuto(); + var buffer = arena.allocate(endpoint.packetSize()); + var transfer = createSyncTransfer(buffer); + + synchronized (transfer) { + submitTransferIn(endpointNumber, transfer); + waitForTransfer(transfer, timeout, UsbDirection.IN, endpointNumber); + } + + return buffer.asSlice(0, transfer.resultSize()).toArray(JAVA_BYTE); + } + + private WindowsTransfer createSyncControlTransfer() { + var transfer = new WindowsTransfer(); + transfer.setCompletion(UsbDeviceImpl::onSyncTransferCompleted); + return transfer; + } + + private WindowsTransfer createSyncTransfer(MemorySegment data) { + var transfer = new WindowsTransfer(); + transfer.setData(data); + transfer.setDataSize((int) data.byteSize()); + transfer.setCompletion(UsbDeviceImpl::onSyncTransferCompleted); + return transfer; + } + + @Override + protected Transfer createTransfer() { + return new WindowsTransfer(); + } + + @Override + protected void throwOSException(int errorCode, String message, Object... args) { + throwException(errorCode, message, args); + } + + synchronized void submitControlTransfer(UsbDirection direction, UsbControlTransfer setup, WindowsTransfer transfer) { + checkIsOpen(); + var intfHandle = findControlTransferInterface(setup); + + try (var arena = Arena.ofConfined()) { + var setupPacket = new SetupPacket(arena); + var bmRequest = + (direction == UsbDirection.IN ? 0x80 : 0) | (setup.requestType().ordinal() << 5) | setup.recipient().ordinal(); + setupPacket.setRequestType(bmRequest); + setupPacket.setRequest(setup.request()); + setupPacket.setValue(setup.value()); + setupPacket.setIndex(setup.index()); + setupPacket.setLength(transfer.dataSize()); + + var errorState = allocateErrorState(arena); + asyncTask.prepareForSubmission(transfer); + + // submit transfer + if (WinUsb_ControlTransfer(errorState, intfHandle.winusbHandle, setupPacket.segment(), transfer.data(), + transfer.dataSize(), NULL, transfer.overlapped()) == 0) { + var err = Win.getLastError(errorState); + if (err != ERROR_IO_PENDING) { + asyncTask.submissionFailed(transfer); + throwException(err, "submitting control transfer failed"); + } + } + } + } + + synchronized void submitTransferOut(int endpointNumber, WindowsTransfer transfer) { + var endpoint = getEndpoint(UsbDirection.OUT, endpointNumber, UsbTransferType.BULK, UsbTransferType.INTERRUPT); + var intfHandle = getInterfaceHandle(endpoint.interfaceNumber()); + + try (var arena = Arena.ofConfined()) { + var errorState = allocateErrorState(arena); + asyncTask.prepareForSubmission(transfer); + + // submit transfer + if (WinUsb_WritePipe(errorState, intfHandle.winusbHandle, endpoint.endpointAddress(), transfer.data(), + transfer.dataSize(), NULL, transfer.overlapped()) == 0) { + var err = Win.getLastError(errorState); + if (err != ERROR_IO_PENDING) { + asyncTask.submissionFailed(transfer); + throwException(err, "submitting transfer OUT failed"); + } + } + } + } + + synchronized void submitTransferIn(int endpointNumber, WindowsTransfer transfer) { + var endpoint = getEndpoint(UsbDirection.IN, endpointNumber, UsbTransferType.BULK, UsbTransferType.INTERRUPT); + var intfHandle = getInterfaceHandle(endpoint.interfaceNumber()); + + try (var arena = Arena.ofConfined()) { + var errorState = allocateErrorState(arena); + asyncTask.prepareForSubmission(transfer); + + // submit transfer + if (WinUsb_ReadPipe(errorState, intfHandle.winusbHandle, endpoint.endpointAddress(), transfer.data(), + transfer.dataSize(), NULL, transfer.overlapped()) == 0) { + var err = Win.getLastError(errorState); + if (err != ERROR_IO_PENDING) { + asyncTask.submissionFailed(transfer); + throwException(err, "submitting transfer IN failed"); + } + } + } + } + + synchronized void configureForAsyncIo(UsbDirection direction, int endpointNumber) { + var endpoint = getEndpoint(direction, endpointNumber, UsbTransferType.BULK, UsbTransferType.INTERRUPT); + var intfHandle = getInterfaceHandle(endpoint.interfaceNumber()); + + try (var arena = Arena.ofConfined()) { + var errorState = allocateErrorState(arena); + + var timeoutHolder = arena.allocate(JAVA_INT); + if (WinUsb_SetPipePolicy(errorState, intfHandle.winusbHandle, endpoint.endpointAddress(), + PIPE_TRANSFER_TIMEOUT, (int) timeoutHolder.byteSize(), timeoutHolder) == 0) + throwLastError(errorState, "setting timeout failed"); + + var rawIoHolder = arena.allocate(JAVA_BYTE); + rawIoHolder.setAtIndex(JAVA_BYTE, 0, (byte) 1); + if (WinUsb_SetPipePolicy(errorState, intfHandle.winusbHandle, endpoint.endpointAddress(), RAW_IO, + (int) rawIoHolder.byteSize(), rawIoHolder) == 0) + throwLastError(errorState, "setting raw IO failed"); + } + } + + @Override + public synchronized void clearHalt(UsbDirection direction, int endpointNumber) { + var endpoint = getEndpoint(direction, endpointNumber, UsbTransferType.BULK, UsbTransferType.INTERRUPT); + var intfHandle = getInterfaceHandle(endpoint.interfaceNumber()); + + try (var arena = Arena.ofConfined()) { + var errorState = allocateErrorState(arena); + if (WinUsb_ResetPipe(errorState, intfHandle.winusbHandle, endpoint.endpointAddress()) == 0) + throwLastError(errorState, "clearing halt failed"); + } + } + + @Override + public synchronized void abortTransfers(UsbDirection direction, int endpointNumber) { + var endpoint = getEndpoint(direction, endpointNumber, UsbTransferType.BULK, UsbTransferType.INTERRUPT); + var intfHandle = getInterfaceHandle(endpoint.interfaceNumber()); + + try (var arena = Arena.ofConfined()) { + var errorState = allocateErrorState(arena); + if (WinUsb_AbortPipe(errorState, intfHandle.winusbHandle, endpoint.endpointAddress()) == 0) + throwLastError(errorState, "aborting transfers on endpoint failed"); + } + } + + @Override + public synchronized @NotNull InputStream openInputStream(int endpointNumber, int bufferSize) { + // check that endpoint number is valid + getEndpoint(UsbDirection.IN, endpointNumber, UsbTransferType.BULK, null); + + return new WindowsEndpointInputStream(this, endpointNumber, bufferSize); + } + + @Override + public synchronized @NotNull OutputStream openOutputStream(int endpointNumber, int bufferSize) { + // check that endpoint number is valid + getEndpoint(UsbDirection.OUT, endpointNumber, UsbTransferType.BULK, null); + + return new WindowsEndpointOutputStream(this, endpointNumber, bufferSize); + } + + private InterfaceHandle getInterfaceHandle(int interfaceNumber) { + for (var intfHandle : interfaceHandles) { + if (intfHandle.interfaceNumber == interfaceNumber) + return intfHandle; + } + + throwException("invalid interface number %s", interfaceNumber); + throw new AssertionError("not reached"); + } + + private InterfaceHandle findControlTransferInterface(UsbControlTransfer setup) { + + var interfaceNumber = -1; + int endpointNumber; + + if (setup.recipient() == UsbRecipient.INTERFACE) { + + interfaceNumber = setup.index() & 0xff; + + } else if (setup.recipient() == UsbRecipient.ENDPOINT) { + + endpointNumber = setup.index() & 0x7f; + var direction = (setup.index() & 0x80) != 0 ? UsbDirection.IN : UsbDirection.OUT; + if (endpointNumber != 0) { + interfaceNumber = getInterfaceNumber(direction, endpointNumber); + if (interfaceNumber == -1) + throwException("invalid endpoint number %d or interface not claimed", endpointNumber); + } + } + + if (interfaceNumber >= 0) { + var intfHandle = getInterfaceHandle(interfaceNumber); + if (intfHandle.winusbHandle == null) + throwException("interface number %d has not been claimed", interfaceNumber); + return intfHandle; + } + + // for control transfer to device, use any claimed interface + for (var intfHandle : interfaceHandles) { + if (intfHandle.winusbHandle != null) + return intfHandle; + } + + throwException("control transfer cannot be executed as no interface has been claimed"); + throw new AssertionError("not reached"); + } + + private String getInterfaceDevicePath(int interfaceNumber) { + var devicePath = getCachedInterfaceDevicePath(interfaceNumber); + if (devicePath != null) + return devicePath; + + var parentDevicePath = (String) getUniqueId(); + + try (var deviceInfoSet = DeviceInfoSet.ofPath(parentDevicePath)) { + var childrenInstanceIDs = deviceInfoSet.getStringListProperty(DEVPKEY_Device_Children()); + if (childrenInstanceIDs == null) { + LOG.log(DEBUG, "missing children instance IDs for device {0}", parentDevicePath); + return null; + + } else { + LOG.log(DEBUG, "children instance IDs: {0}", childrenInstanceIDs); + + for (var instanceId : childrenInstanceIDs) { + devicePath = getChildDevicePath(instanceId, interfaceNumber); + if (devicePath != null) + return devicePath; + } + } + } + + return null; // retry later + } + + private String getCachedInterfaceDevicePath(int interfaceNumber) { + if (!isComposite) + return (String) getUniqueId(); + return devicePaths.get(interfaceNumber); + } + + private String getChildDevicePath(String instanceId, int interfaceNumber) { + try (var deviceInfoSet = DeviceInfoSet.ofInstance(instanceId)) { + + // get hardware IDs (to extract interface number) + var hardwareIds = deviceInfoSet.getStringListProperty(DEVPKEY_Device_HardwareIds()); + if (hardwareIds == null) { + LOG.log(DEBUG, "child device {0} has no hardware IDs", instanceId); + return null; + } + + var extractedNumber = extractInterfaceNumber(hardwareIds); + if (extractedNumber == -1) { + LOG.log(DEBUG, "child device {0} has no interface number", instanceId); + return null; + } + + if (extractedNumber != interfaceNumber) + return null; + + var devicePath = deviceInfoSet.getDevicePathByGUID(instanceId); + if (devicePath == null) { + LOG.log(INFO, "Child device {0} has no device path / interface GUID", instanceId); + throw new UsbException("claiming interface failed (composite function has no device path / interface GUID; the required WinUSB driver is probably not installed)"); + } + + if (devicePaths == null) + devicePaths = new HashMap<>(); + devicePaths.put(interfaceNumber, devicePath); + return devicePath; + } + } + + private static final Pattern MULTIPLE_INTERFACE_ID = Pattern.compile( + "USB\\\\VID_[0-9A-Fa-f]{4}&PID_[0-9A-Fa-f]{4}&MI_([0-9A-Fa-f]{2})"); + + private static int extractInterfaceNumber(List hardwareIds) { + // Also see https://docs.microsoft.com/en-us/windows-hardware/drivers/install/standard-usb-identifiers#multiple-interface-usb-devices + + for (var id : hardwareIds) { + var matcher = MULTIPLE_INTERFACE_ID.matcher(id); + if (matcher.find()) { + var intfHexNumber = matcher.group(1); + try { + return Integer.parseInt(intfHexNumber, 16); + } catch (NumberFormatException _) { + // ignore and try next one + } + } + } + + return -1; + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsUsbDeviceRegistry.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsUsbDeviceRegistry.java new file mode 100644 index 00000000..d9f86393 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsUsbDeviceRegistry.java @@ -0,0 +1,396 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.windows; + +import net.codecrete.usb.UsbDevice; +import net.codecrete.usb.UsbException; +import net.codecrete.usb.common.ScopeCleanup; +import net.codecrete.usb.common.UsbDeviceImpl; +import net.codecrete.usb.common.UsbDeviceRegistry; +import net.codecrete.usb.usbstandard.ConfigurationDescriptor; +import net.codecrete.usb.usbstandard.DeviceDescriptor; +import net.codecrete.usb.usbstandard.SetupPacket; +import net.codecrete.usb.usbstandard.StringDescriptor; +import windows.win32.devices.usb.USB_DESCRIPTOR_REQUEST; +import windows.win32.devices.usb.USB_NODE_CONNECTION_INFORMATION_EX; +import windows.win32.ui.windowsandmessaging.DEV_BROADCAST_DEVICEINTERFACE_W; +import windows.win32.ui.windowsandmessaging.DEV_BROADCAST_HDR; +import windows.win32.ui.windowsandmessaging.MSG; +import windows.win32.ui.windowsandmessaging.WNDCLASSEXW; +import windows.win32.ui.windowsandmessaging.WNDPROC; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static java.lang.System.Logger.Level.INFO; +import static java.lang.foreign.MemorySegment.NULL; +import static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static java.lang.foreign.ValueLayout.JAVA_INT; +import static java.lang.foreign.ValueLayout.JAVA_SHORT; +import static java.lang.foreign.ValueLayout.PathElement; +import static java.nio.charset.StandardCharsets.UTF_16LE; +import static net.codecrete.usb.usbstandard.Constants.CONFIGURATION_DESCRIPTOR_TYPE; +import static net.codecrete.usb.usbstandard.Constants.DEFAULT_LANGUAGE; +import static net.codecrete.usb.usbstandard.Constants.STRING_DESCRIPTOR_TYPE; +import static net.codecrete.usb.windows.CustomApis.CloseHandle; +import static net.codecrete.usb.windows.Win.allocateErrorState; +import static net.codecrete.usb.windows.WindowsUsbException.throwException; +import static net.codecrete.usb.windows.WindowsUsbException.throwLastError; +import static windows.win32.devices.properties.Constants.DEVPKEY_Device_Address; +import static windows.win32.devices.properties.Constants.DEVPKEY_Device_InstanceId; +import static windows.win32.devices.properties.Constants.DEVPKEY_Device_Parent; +import static windows.win32.devices.usb.Constants.GUID_DEVINTERFACE_USB_DEVICE; +import static windows.win32.devices.usb.Constants.GUID_DEVINTERFACE_USB_HUB; +import static windows.win32.devices.usb.Constants.IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION; +import static windows.win32.devices.usb.Constants.IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX; +import static windows.win32.devices.usb.Constants.USB_REQUEST_GET_DESCRIPTOR; +import static windows.win32.foundation.GENERIC_ACCESS_RIGHTS.GENERIC_WRITE; +import static windows.win32.storage.filesystem.Apis.CreateFileW; +import static windows.win32.storage.filesystem.FILE_CREATION_DISPOSITION.OPEN_EXISTING; +import static windows.win32.storage.filesystem.FILE_SHARE_MODE.FILE_SHARE_WRITE; +import static windows.win32.system.io.Apis.DeviceIoControl; +import static windows.win32.system.libraryloader.Apis.GetModuleHandleW; +import static windows.win32.ui.windowsandmessaging.Apis.CreateWindowExW; +import static windows.win32.ui.windowsandmessaging.Apis.DefWindowProcW; +import static windows.win32.ui.windowsandmessaging.Apis.GetMessageW; +import static windows.win32.ui.windowsandmessaging.Apis.RegisterClassExW; +import static windows.win32.ui.windowsandmessaging.Apis.RegisterDeviceNotificationW; +import static windows.win32.ui.windowsandmessaging.Constants.DBT_DEVICEARRIVAL; +import static windows.win32.ui.windowsandmessaging.Constants.DBT_DEVICEREMOVECOMPLETE; +import static windows.win32.ui.windowsandmessaging.Constants.HWND_MESSAGE; +import static windows.win32.ui.windowsandmessaging.Constants.WM_DEVICECHANGE; +import static windows.win32.ui.windowsandmessaging.DEV_BROADCAST_HDR_DEVICE_TYPE.DBT_DEVTYP_DEVICEINTERFACE; +import static windows.win32.ui.windowsandmessaging.REGISTER_NOTIFICATION_FLAGS.DEVICE_NOTIFY_WINDOW_HANDLE; + +/** + * Windows implementation of USB device registry. + *

+ * To retrieve details of a USB device, this class accesses it indirectly + * via the parent. To address it the parent's handle (hub handle) and + * the device's port number is needed. + *

+ */ +public class WindowsUsbDeviceRegistry extends UsbDeviceRegistry { + + private static final System.Logger LOG = System.getLogger(WindowsUsbDeviceRegistry.class.getName()); + + private static final long REQUEST_DATA_OFFSET + = USB_DESCRIPTOR_REQUEST.layout().byteOffset(PathElement.groupElement("Data")); + + @Override + protected void monitorDevices() { + try (var arena = Arena.ofConfined()) { + + MemorySegment hwnd; + var errorState = allocateErrorState(arena); + + try { + final var className = arena.allocateFrom("USB_MONITOR", UTF_16LE); + final var windowName = arena.allocateFrom("USB device monitor", UTF_16LE); + final var instance = GetModuleHandleW(errorState, NULL); + + // register window class + var wx = WNDCLASSEXW.allocate(arena); + WNDCLASSEXW.lpfnWndProc(wx, WNDPROC.allocate(arena, this::handleWindowMessage)); + WNDCLASSEXW.hInstance(wx, instance); + WNDCLASSEXW.lpszClassName(wx, className); + var atom = RegisterClassExW(errorState, wx); + if (atom == 0) + throwLastError(errorState, "internal error (RegisterClassExW)"); + + // create message-only window + hwnd = CreateWindowExW(errorState, 0, className, windowName, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, + instance, NULL); + if (hwnd.address() == 0) + throwLastError(errorState, "internal error (CreateWindowExW)"); + + // configure notifications + var notificationFilter = DEV_BROADCAST_DEVICEINTERFACE_W.allocate(arena, 260); + DEV_BROADCAST_DEVICEINTERFACE_W.dbcc_size(notificationFilter, (int) notificationFilter.byteSize()); + DEV_BROADCAST_DEVICEINTERFACE_W.dbcc_devicetype(notificationFilter, DBT_DEVTYP_DEVICEINTERFACE); + DEV_BROADCAST_DEVICEINTERFACE_W.dbcc_classguid(notificationFilter).copyFrom(GUID_DEVINTERFACE_USB_DEVICE()); + + var notifyHandle = RegisterDeviceNotificationW(errorState, hwnd, notificationFilter, + DEVICE_NOTIFY_WINDOW_HANDLE); + if (notifyHandle.address() == 0) + throwLastError(errorState, "internal error (RegisterDeviceNotificationW)"); + + // initial device enumeration + enumeratePresentDevices(); + + } catch (Exception e) { + enumerationFailed(e); + return; + } + + // process messages + var msg = MSG.allocate(arena); + int err; + //noinspection StatementWithEmptyBody + while ((err = GetMessageW(errorState, msg, hwnd, 0, 0)) > 0) + ; // do nothing + + if (err == -1) + throwLastError(errorState, "internal error (GetMessageW)"); + } + } + + @SuppressWarnings("java:S106") + private void enumeratePresentDevices() { + + List deviceList = new ArrayList<>(); + try (var cleanup = new ScopeCleanup(); + var deviceInfoSet = DeviceInfoSet.ofPresentDevices(GUID_DEVINTERFACE_USB_DEVICE(), null)) { + + // ensure all hubs are closed later + final var hubHandles = new HashMap(); + cleanup.add(() -> hubHandles.forEach((_, handle) -> CloseHandle(handle))); + + // iterate all devices + while (deviceInfoSet.next()) { + + var instanceId = deviceInfoSet.getStringProperty(DEVPKEY_Device_InstanceId()); + var devicePath = DeviceInfoSet.getDevicePath(instanceId, GUID_DEVINTERFACE_USB_DEVICE()); + + try { + deviceList.add(createDeviceFromDeviceInfo(deviceInfoSet, devicePath, hubHandles)); + + } catch (Exception e) { + LOG.log(INFO, String.format("failed to retrieve information about device %s - ignoring device", devicePath), e); + } + } + + setInitialDeviceList(deviceList); + } + } + + private UsbDevice createDeviceFromDeviceInfo(DeviceInfoSet deviceInfoSet, String devicePath, + Map hubHandles) { + try (var arena = Arena.ofConfined()) { + + var usbPortNum = deviceInfoSet.getIntProperty(DEVPKEY_Device_Address()); + var parentInstanceId = deviceInfoSet.getStringProperty(DEVPKEY_Device_Parent()); + var hubPath = DeviceInfoSet.getDevicePath(parentInstanceId, GUID_DEVINTERFACE_USB_HUB()); + + // open hub if not open yet + var hubHandle = hubHandles.get(hubPath); + if (hubHandle == null) { + var hubPathSeg = arena.allocateFrom(hubPath, UTF_16LE); + var errorState = allocateErrorState(arena); + hubHandle = CreateFileW(errorState, hubPathSeg, GENERIC_WRITE, FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + if (Win.isInvalidHandle(hubHandle)) + throwLastError(errorState, "internal error (opening hub device)"); + hubHandles.put(hubPath, hubHandle); + } + + return createDevice(devicePath, deviceInfoSet.isCompositeDevice(), hubHandle, usbPortNum); + } + } + + /** + * Retrieve device descriptor and create {@code UsbDevice} instance + * + * @param devicePath the device path + * @param hubHandle the hub handle (parent) + * @param usbPortNum the USB port number + * @return the {@code UsbDevice} instance + */ + private UsbDevice createDevice(String devicePath, boolean isComposite, MemorySegment hubHandle, int usbPortNum) { + + try (var arena = Arena.ofConfined()) { + + // get device descriptor + var connInfo = USB_NODE_CONNECTION_INFORMATION_EX.allocate(arena, 0); + USB_NODE_CONNECTION_INFORMATION_EX.ConnectionIndex(connInfo, usbPortNum); + var sizeHolder = arena.allocate(JAVA_INT); + var errorState = allocateErrorState(arena); + if (DeviceIoControl(errorState, hubHandle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, + connInfo, (int) connInfo.byteSize(), connInfo, (int) connInfo.byteSize(), sizeHolder, NULL) == 0) + throwLastError(errorState, "internal error (getting device descriptor failed)"); + + var descriptorSegment = USB_NODE_CONNECTION_INFORMATION_EX.DeviceDescriptor(connInfo); + var deviceDescriptor = new DeviceDescriptor(descriptorSegment); + + var vendorId = deviceDescriptor.vendorID(); + var productId = deviceDescriptor.productID(); + + var configDesc = getDescriptor(hubHandle, usbPortNum, CONFIGURATION_DESCRIPTOR_TYPE, 0, (short) 0, arena); + + // create new device + var device = new WindowsUsbDevice(devicePath, vendorId, productId, configDesc, isComposite); + device.setFromDeviceDescriptor(descriptorSegment); + + var languages = getLanguages(hubHandle, usbPortNum, arena); + device.setProductString(descriptorSegment, index -> getStringDescriptor(hubHandle, usbPortNum, index, languages)); + return device; + } + } + + private MemorySegment getDescriptor(MemorySegment hubHandle, int usbPortNumber, int descriptorType, int index, + short languageID, Arena arena) { + return getDescriptor(hubHandle, usbPortNumber, descriptorType, index, languageID, 0, arena); + + } + + private MemorySegment getDescriptor(MemorySegment hubHandle, int usbPortNumber, int descriptorType, int index, + short languageID, int requestSize, Arena arena) { + var size = requestSize != 0 ? requestSize + (int) REQUEST_DATA_OFFSET : 256; + + // create descriptor requests + var descriptorRequest = arena.allocate(size); + USB_DESCRIPTOR_REQUEST.ConnectionIndex(descriptorRequest, usbPortNumber); + var setupPacket = new SetupPacket(descriptorRequest.asSlice( + USB_DESCRIPTOR_REQUEST.SetupPacket_bmRequest$offset(), SetupPacket.LAYOUT.byteSize())); + setupPacket.setRequestType(0x80); // device-to-host / type standard / recipient device + setupPacket.setRequest(USB_REQUEST_GET_DESCRIPTOR); + setupPacket.setValue((descriptorType << 8) | index); + setupPacket.setIndex(languageID); + setupPacket.setLength(size - (int) REQUEST_DATA_OFFSET); + + // execute request + var effectiveSizeHolder = arena.allocate(JAVA_INT); + var errorState = allocateErrorState(arena); + if (DeviceIoControl(errorState, hubHandle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, + descriptorRequest, size, descriptorRequest, size, effectiveSizeHolder, NULL) == 0) + throwLastError(errorState, "internal error (retrieving descriptor %d failed)", index); + + // determine size of descriptor + int expectedSize; + if (descriptorType != CONFIGURATION_DESCRIPTOR_TYPE) { + expectedSize = 255 & descriptorRequest.get(JAVA_BYTE, REQUEST_DATA_OFFSET); + } else { + var configDesc = + new ConfigurationDescriptor(descriptorRequest.asSlice(REQUEST_DATA_OFFSET, ConfigurationDescriptor.LAYOUT.byteSize())); + expectedSize = configDesc.totalLength(); + } + + // check against effective size + var effectiveSize = effectiveSizeHolder.get(JAVA_INT, 0) - REQUEST_DATA_OFFSET; + if (effectiveSize != expectedSize) { + if (requestSize != 0) + throwException("internal error (unexpected descriptor size)"); + + // repeat with correct size + return getDescriptor(hubHandle, usbPortNumber, descriptorType, index, languageID, expectedSize, arena); + } + + return descriptorRequest.asSlice(REQUEST_DATA_OFFSET, effectiveSize); + } + + @SuppressWarnings("java:S106") + private String getStringDescriptor(MemorySegment hubHandle, int usbPortNumber, int index, short[] languages) { + if (index == 0) + return null; + + try (var arena = Arena.ofConfined()) { + for (var language : languages) { + try { + var stringDesc = new StringDescriptor(getDescriptor(hubHandle, usbPortNumber, STRING_DESCRIPTOR_TYPE, + index, language, arena)); + return stringDesc.string(); + + } catch (UsbException _) { + // ignore and try next language + } + } + } + + // Even though this function is only called for string descriptors referenced in the + // configuration descriptor, some device might not provide them; so ignore it. + return null; + } + + private short[] getLanguages(MemorySegment hubHandle, int usbPortNumber, Arena arena) { + try { + var languages = getDescriptor(hubHandle, usbPortNumber, STRING_DESCRIPTOR_TYPE, 0, (short) 0, arena); + var n = (languages.byteSize() - 2) / 2; + if (n == 0) + return new short[]{DEFAULT_LANGUAGE}; + return languages.asSlice(2, n * 2).toArray(JAVA_SHORT); + } catch (UsbException _) { + return new short[]{DEFAULT_LANGUAGE}; + } + } + + @SuppressWarnings("java:S1144") + private long handleWindowMessage(MemorySegment hWnd, int uMsg, long wParam, long lParam) { + + // check for message related to connecting/disconnecting devices + if (uMsg == WM_DEVICECHANGE && (wParam == DBT_DEVICEARRIVAL || wParam == DBT_DEVICEREMOVECOMPLETE)) { + var data = MemorySegment.ofAddress(lParam).reinterpret(DEV_BROADCAST_DEVICEINTERFACE_W.sizeof()); + if (DEV_BROADCAST_HDR.dbch_devicetype(data) == DBT_DEVTYP_DEVICEINTERFACE) { + + // get device path + var nameSlice = + MemorySegment.ofAddress(DEV_BROADCAST_DEVICEINTERFACE_W.dbcc_name(data).address()).reinterpret(500); + var devicePath = nameSlice.getString(0, UTF_16LE); + if (wParam == DBT_DEVICEARRIVAL) + onDeviceConnected(devicePath); + else + onDeviceDisconnected(devicePath); + return 0; + } + } + + // default message handling + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } + + @SuppressWarnings("java:S106") + private void onDeviceConnected(String devicePath) { + try (var cleanup = new ScopeCleanup(); + var deviceInfoSet = DeviceInfoSet.ofPath(devicePath)) { + + // ensure all hubs are closed later + final var hubHandles = new HashMap(); + cleanup.add(() -> hubHandles.forEach((_, handle) -> CloseHandle(handle))); + + try { + // create device instance + var device = createDeviceFromDeviceInfo(deviceInfoSet, devicePath, hubHandles); + + // add it to device list + addDevice(device); + + } catch (Exception e) { + LOG.log(INFO, String.format("failed to retrieve information about device %s - ignoring device", devicePath), e); + } + } + } + + private void onDeviceDisconnected(String devicePath) { + closeAndRemoveDevice(devicePath); + } + + /** + * Finds the index of the device in the list. + *

+ * This override uses a case-insensitive string comparison as Windows uses different casing + * when initially enumerating devices and during later monitoring. + *

+ * + * @param deviceList the device list + * @param deviceId the unique device ID + * @return index, or -1 if not found + */ + @Override + protected int findDeviceIndex(List deviceList, Object deviceId) { + var id = deviceId.toString(); + for (var i = 0; i < deviceList.size(); i++) { + var dev = (UsbDeviceImpl) deviceList.get(i); + if (id.equalsIgnoreCase(dev.getUniqueId().toString())) + return i; + } + return -1; + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsUsbException.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsUsbException.java new file mode 100644 index 00000000..94f70bf5 --- /dev/null +++ b/java-does-usb/src/main/java/net/codecrete/usb/windows/WindowsUsbException.java @@ -0,0 +1,132 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +package net.codecrete.usb.windows; + +import net.codecrete.usb.UsbException; +import net.codecrete.usb.UsbStallException; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; + +import static java.lang.foreign.MemorySegment.NULL; +import static java.lang.foreign.ValueLayout.ADDRESS; +import static java.nio.charset.StandardCharsets.UTF_16LE; +import static net.codecrete.usb.common.ForeignMemory.dereference; +import static windows.win32.foundation.Apis.LocalFree; +import static windows.win32.foundation.Constants.STATUS_UNSUCCESSFUL; +import static windows.win32.foundation.WIN32_ERROR.ERROR_GEN_FAILURE; +import static windows.win32.system.diagnostics.debug.Apis.FormatMessageW; +import static windows.win32.system.diagnostics.debug.FORMAT_MESSAGE_OPTIONS.FORMAT_MESSAGE_ALLOCATE_BUFFER; +import static windows.win32.system.diagnostics.debug.FORMAT_MESSAGE_OPTIONS.FORMAT_MESSAGE_FROM_HMODULE; +import static windows.win32.system.diagnostics.debug.FORMAT_MESSAGE_OPTIONS.FORMAT_MESSAGE_FROM_SYSTEM; +import static windows.win32.system.diagnostics.debug.FORMAT_MESSAGE_OPTIONS.FORMAT_MESSAGE_IGNORE_INSERTS; +import static windows.win32.system.libraryloader.Apis.GetModuleHandleW; + +/** + * Exception thrown if a Windows specific error occurs. + */ +public class WindowsUsbException extends UsbException { + + /** + * Creates a new instance. + *

+ * The message for the Windows error code is looked up and appended to the message. + *

+ * + * @param message exception message + * @param errorCode Windows error code (usually returned from {@code GetLastError()}) + */ + public WindowsUsbException(String message, int errorCode) { + super(String.format("%s: %s", message, getErrorMessage(errorCode)), errorCode); + } + + /** + * Throws an exception for the specified Windows error code. + *

+ * The message for the Windows error code is looked up and appended to the message. + *

+ * + * @param errorCode Windows error code (usually returned from {@code GetLastError()}) + * @param message exception message format ({@link String#format(String, Object...)} style) + * @param args arguments for exception message + */ + static void throwException(int errorCode, String message, Object... args) { + var formattedMessage = String.format(message, args); + if (errorCode == ERROR_GEN_FAILURE || errorCode == STATUS_UNSUCCESSFUL) { + throw new UsbStallException(formattedMessage); + } else { + throw new WindowsUsbException(formattedMessage, errorCode); + } + } + + /** + * Throws a USB exception. + * + * @param message exception message format ({@link String#format(String, Object...)} style) + * @param args arguments for exception message + */ + static void throwException(String message, Object... args) { + throw new UsbException(String.format(message, args)); + } + + /** + * Throws an exception for the last error. + *

+ * The last Windows error code is taken from the call capture state + * {@link Win#LAST_ERROR_STATE} provided as the first parameter. + *

+ * + * @param errorState call capture state containing last error code + * @param message exception message format ({@link String#format(String, Object...)} style) + * @param args arguments for exception message + */ + static void throwLastError(MemorySegment errorState, String message, Object... args) { + throwException(Win.getLastError(errorState), message, args); + } + + private static MemorySegment ntModule; // NOSONAR + + private static MemorySegment getNtModule() { + if (ntModule == null) { + try (var arena = Arena.ofConfined()) { + var errorState = Win.allocateErrorState(arena); + var moduleName = arena.allocateFrom("NTDLL.DLL", UTF_16LE); + ntModule = GetModuleHandleW(errorState, moduleName); + } + } + + return ntModule; + } + + static String getErrorMessage(int errorCode) { + try (var arena = Arena.ofConfined()) { + var errorState = Win.allocateErrorState(arena); + var messagePointerHolder = arena.allocate(ADDRESS); + + // First try: Win32 error code + var res = FormatMessageW( + errorState, FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorCode, 0, messagePointerHolder, 0, NULL); + + // Second try: NTSTATUS error code + if (res == 0) { + res = FormatMessageW( + errorState, FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, + getNtModule(), errorCode, 0, messagePointerHolder, 0, NULL); + } + + // Fallback + if (res == 0) + return "unspecified error"; + + var messagePointer = dereference(messagePointerHolder).reinterpret(128 * 1024); // NOSONAR + var message = messagePointer.getString(0, UTF_16LE); + LocalFree(errorState, messagePointer); + return message.trim(); + } + } +} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/advapi32/Advapi32.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/advapi32/Advapi32.java deleted file mode 100644 index d65c860a..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/advapi32/Advapi32.java +++ /dev/null @@ -1,50 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.advapi32; - -import java.lang.foreign.Addressable; -import java.lang.invoke.MethodHandle; - -import static java.lang.foreign.ValueLayout.*; -public class Advapi32 { - - /* package-private */ Advapi32() {} - public static OfByte C_CHAR = Constants$root.C_CHAR$LAYOUT; - public static OfShort C_SHORT = Constants$root.C_SHORT$LAYOUT; - public static OfInt C_INT = Constants$root.C_LONG$LAYOUT; - public static OfInt C_LONG = Constants$root.C_LONG$LAYOUT; - public static OfLong C_LONG_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfFloat C_FLOAT = Constants$root.C_FLOAT$LAYOUT; - public static OfDouble C_DOUBLE = Constants$root.C_DOUBLE$LAYOUT; - public static OfAddress C_POINTER = Constants$root.C_POINTER$LAYOUT; - public static MethodHandle RegCloseKey$MH() { - return RuntimeHelper.requireNonNull(constants$0.RegCloseKey$MH,"RegCloseKey"); - } - public static int RegCloseKey ( Addressable hKey) { - var mh$ = RegCloseKey$MH(); - try { - return (int)mh$.invokeExact(hKey); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle RegQueryValueExW$MH() { - return RuntimeHelper.requireNonNull(constants$0.RegQueryValueExW$MH,"RegQueryValueExW"); - } - public static int RegQueryValueExW ( Addressable hKey, Addressable lpValueName, Addressable lpReserved, Addressable lpType, Addressable lpData, Addressable lpcbData) { - var mh$ = RegQueryValueExW$MH(); - try { - return (int)mh$.invokeExact(hKey, lpValueName, lpReserved, lpType, lpData, lpcbData); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static int KEY_READ() { - return (int)131097L; - } - public static int REG_MULTI_SZ() { - return (int)7L; - } -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/advapi32/Constants$root.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/advapi32/Constants$root.java deleted file mode 100644 index e1a30f5a..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/advapi32/Constants$root.java +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.advapi32; - -import static java.lang.foreign.ValueLayout.*; -public class Constants$root { - - static final OfBoolean C_BOOL$LAYOUT = JAVA_BOOLEAN; - static final OfByte C_CHAR$LAYOUT = JAVA_BYTE; - static final OfShort C_SHORT$LAYOUT = JAVA_SHORT.withBitAlignment(16); - static final OfInt C_INT$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfInt C_LONG$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfLong C_LONG_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfFloat C_FLOAT$LAYOUT = JAVA_FLOAT.withBitAlignment(32); - static final OfDouble C_DOUBLE$LAYOUT = JAVA_DOUBLE.withBitAlignment(64); - static final OfAddress C_POINTER$LAYOUT = ADDRESS.withBitAlignment(64); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/advapi32/RuntimeHelper.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/advapi32/RuntimeHelper.java deleted file mode 100644 index 707bc5cb..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/advapi32/RuntimeHelper.java +++ /dev/null @@ -1,216 +0,0 @@ -package net.codecrete.usb.windows.gen.advapi32; -// Generated by jextract - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import static java.lang.foreign.ValueLayout.*; - -final class RuntimeHelper { - - private RuntimeHelper() {} - private final static Linker LINKER = Linker.nativeLinker(); - private final static ClassLoader LOADER = RuntimeHelper.class.getClassLoader(); - private final static MethodHandles.Lookup MH_LOOKUP = MethodHandles.lookup(); - private final static SymbolLookup SYMBOL_LOOKUP; - - final static SegmentAllocator CONSTANT_ALLOCATOR = - (size, align) -> MemorySegment.allocateNative(size, align, MemorySession.openImplicit()); - - static { - System.loadLibrary("Advapi32"); - SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); - SYMBOL_LOOKUP = name -> loaderLookup.lookup(name).or(() -> LINKER.defaultLookup().lookup(name)); - } - - static T requireNonNull(T obj, String symbolName) { - if (obj == null) { - throw new UnsatisfiedLinkError("unresolved symbol: " + symbolName); - } - return obj; - } - - private final static SegmentAllocator THROWING_ALLOCATOR = (x, y) -> { throw new AssertionError("should not reach here"); }; - - static final MemorySegment lookupGlobalVariable(String name, MemoryLayout layout) { - return SYMBOL_LOOKUP.lookup(name).map(symbol -> MemorySegment.ofAddress(symbol.address(), layout.byteSize(), MemorySession.openShared())).orElse(null); - } - - static final MethodHandle downcallHandle(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> LINKER.downcallHandle(addr, fdesc)). - orElse(null); - } - - static final MethodHandle downcallHandle(FunctionDescriptor fdesc) { - return LINKER.downcallHandle(fdesc); - } - - static final MethodHandle downcallHandleVariadic(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> VarargsInvoker.make(addr, fdesc)). - orElse(null); - } - - static final MemorySegment upcallStub(Class fi, Z z, FunctionDescriptor fdesc, MemorySession session) { - try { - MethodHandle handle = MH_LOOKUP.findVirtual(fi, "apply", Linker.upcallType(fdesc)); - handle = handle.bindTo(z); - return LINKER.upcallStub(handle, fdesc, session); - } catch (Throwable ex) { - throw new AssertionError(ex); - } - } - - static MemorySegment asArray(MemoryAddress addr, MemoryLayout layout, int numElements, MemorySession session) { - return MemorySegment.ofAddress(addr, numElements * layout.byteSize(), session); - } - - // Internals only below this point - - private static class VarargsInvoker { - private static final MethodHandle INVOKE_MH; - private final MemorySegment symbol; - private final FunctionDescriptor function; - - private VarargsInvoker(MemorySegment symbol, FunctionDescriptor function) { - this.symbol = symbol; - this.function = function; - } - - static { - try { - INVOKE_MH = MethodHandles.lookup().findVirtual(VarargsInvoker.class, "invoke", MethodType.methodType(Object.class, SegmentAllocator.class, Object[].class)); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - static MethodHandle make(MemorySegment symbol, FunctionDescriptor function) { - VarargsInvoker invoker = new VarargsInvoker(symbol, function); - MethodHandle handle = INVOKE_MH.bindTo(invoker).asCollector(Object[].class, function.argumentLayouts().size() + 1); - MethodType mtype = MethodType.methodType(function.returnLayout().isPresent() ? carrier(function.returnLayout().get(), true) : void.class); - for (MemoryLayout layout : function.argumentLayouts()) { - mtype = mtype.appendParameterTypes(carrier(layout, false)); - } - mtype = mtype.appendParameterTypes(Object[].class); - if (mtype.returnType().equals(MemorySegment.class)) { - mtype = mtype.insertParameterTypes(0, SegmentAllocator.class); - } else { - handle = MethodHandles.insertArguments(handle, 0, THROWING_ALLOCATOR); - } - return handle.asType(mtype); - } - - static Class carrier(MemoryLayout layout, boolean ret) { - if (layout instanceof ValueLayout valueLayout) { - return (ret || valueLayout.carrier() != MemoryAddress.class) ? - valueLayout.carrier() : Addressable.class; - } else if (layout instanceof GroupLayout) { - return MemorySegment.class; - } else { - throw new AssertionError("Cannot get here!"); - } - } - - private Object invoke(SegmentAllocator allocator, Object[] args) throws Throwable { - // one trailing Object[] - int nNamedArgs = function.argumentLayouts().size(); - assert(args.length == nNamedArgs + 1); - // The last argument is the array of vararg collector - Object[] unnamedArgs = (Object[]) args[args.length - 1]; - - int argsCount = nNamedArgs + unnamedArgs.length; - Class[] argTypes = new Class[argsCount]; - MemoryLayout[] argLayouts = new MemoryLayout[nNamedArgs + unnamedArgs.length]; - - int pos = 0; - for (pos = 0; pos < nNamedArgs; pos++) { - argLayouts[pos] = function.argumentLayouts().get(pos); - } - - assert pos == nNamedArgs; - for (Object o: unnamedArgs) { - argLayouts[pos] = variadicLayout(normalize(o.getClass())); - pos++; - } - assert pos == argsCount; - - FunctionDescriptor f = (function.returnLayout().isEmpty()) ? - FunctionDescriptor.ofVoid(argLayouts) : - FunctionDescriptor.of(function.returnLayout().get(), argLayouts); - MethodHandle mh = LINKER.downcallHandle(symbol, f); - if (mh.type().returnType() == MemorySegment.class) { - mh = mh.bindTo(allocator); - } - // flatten argument list so that it can be passed to an asSpreader MH - Object[] allArgs = new Object[nNamedArgs + unnamedArgs.length]; - System.arraycopy(args, 0, allArgs, 0, nNamedArgs); - System.arraycopy(unnamedArgs, 0, allArgs, nNamedArgs, unnamedArgs.length); - - return mh.asSpreader(Object[].class, argsCount).invoke(allArgs); - } - - private static Class unboxIfNeeded(Class clazz) { - if (clazz == Boolean.class) { - return boolean.class; - } else if (clazz == Void.class) { - return void.class; - } else if (clazz == Byte.class) { - return byte.class; - } else if (clazz == Character.class) { - return char.class; - } else if (clazz == Short.class) { - return short.class; - } else if (clazz == Integer.class) { - return int.class; - } else if (clazz == Long.class) { - return long.class; - } else if (clazz == Float.class) { - return float.class; - } else if (clazz == Double.class) { - return double.class; - } else { - return clazz; - } - } - - private Class promote(Class c) { - if (c == byte.class || c == char.class || c == short.class || c == int.class) { - return long.class; - } else if (c == float.class) { - return double.class; - } else { - return c; - } - } - - private Class normalize(Class c) { - c = unboxIfNeeded(c); - if (c.isPrimitive()) { - return promote(c); - } - if (MemoryAddress.class.isAssignableFrom(c)) { - return MemoryAddress.class; - } - if (MemorySegment.class.isAssignableFrom(c)) { - return MemorySegment.class; - } - throw new IllegalArgumentException("Invalid type for ABI: " + c.getTypeName()); - } - - private MemoryLayout variadicLayout(Class c) { - if (c == long.class) { - return JAVA_LONG; - } else if (c == double.class) { - return JAVA_DOUBLE; - } else if (MemoryAddress.class.isAssignableFrom(c)) { - return ADDRESS; - } else { - throw new IllegalArgumentException("Unhandled variadic argument class: " + c); - } - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/advapi32/constants$0.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/advapi32/constants$0.java deleted file mode 100644 index d7b38ce0..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/advapi32/constants$0.java +++ /dev/null @@ -1,30 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.advapi32; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.invoke.MethodHandle; -class constants$0 { - - static final FunctionDescriptor RegCloseKey$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle RegCloseKey$MH = RuntimeHelper.downcallHandle( - "RegCloseKey", - constants$0.RegCloseKey$FUNC - ); - static final FunctionDescriptor RegQueryValueExW$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle RegQueryValueExW$MH = RuntimeHelper.downcallHandle( - "RegQueryValueExW", - constants$0.RegQueryValueExW$FUNC - ); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/kernel32/Constants$root.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/kernel32/Constants$root.java deleted file mode 100644 index bfc827c7..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/kernel32/Constants$root.java +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.kernel32; - -import static java.lang.foreign.ValueLayout.*; -public class Constants$root { - - static final OfBoolean C_BOOL$LAYOUT = JAVA_BOOLEAN; - static final OfByte C_CHAR$LAYOUT = JAVA_BYTE; - static final OfShort C_SHORT$LAYOUT = JAVA_SHORT.withBitAlignment(16); - static final OfInt C_INT$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfInt C_LONG$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfLong C_LONG_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfFloat C_FLOAT$LAYOUT = JAVA_FLOAT.withBitAlignment(32); - static final OfDouble C_DOUBLE$LAYOUT = JAVA_DOUBLE.withBitAlignment(64); - static final OfAddress C_POINTER$LAYOUT = ADDRESS.withBitAlignment(64); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/kernel32/GUID.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/kernel32/GUID.java deleted file mode 100644 index ea550f1d..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/kernel32/GUID.java +++ /dev/null @@ -1,9 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.kernel32; - -public class GUID extends _GUID { - -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/kernel32/Kernel32.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/kernel32/Kernel32.java deleted file mode 100644 index f4de7118..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/kernel32/Kernel32.java +++ /dev/null @@ -1,145 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.kernel32; - -import java.lang.foreign.Addressable; -import java.lang.foreign.MemoryAddress; -import java.lang.invoke.MethodHandle; - -import static java.lang.foreign.ValueLayout.*; -public class Kernel32 { - - /* package-private */ Kernel32() {} - public static OfByte C_CHAR = Constants$root.C_CHAR$LAYOUT; - public static OfShort C_SHORT = Constants$root.C_SHORT$LAYOUT; - public static OfInt C_INT = Constants$root.C_LONG$LAYOUT; - public static OfInt C_LONG = Constants$root.C_LONG$LAYOUT; - public static OfLong C_LONG_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfFloat C_FLOAT = Constants$root.C_FLOAT$LAYOUT; - public static OfDouble C_DOUBLE = Constants$root.C_DOUBLE$LAYOUT; - public static OfAddress C_POINTER = Constants$root.C_POINTER$LAYOUT; - public static int FILE_SHARE_READ() { - return (int)1L; - } - public static int FILE_SHARE_WRITE() { - return (int)2L; - } - public static int FILE_ATTRIBUTE_NORMAL() { - return (int)128L; - } - public static int OPEN_EXISTING() { - return (int)3L; - } - public static int FILE_FLAG_OVERLAPPED() { - return (int)1073741824L; - } - public static int FORMAT_MESSAGE_ALLOCATE_BUFFER() { - return (int)256L; - } - public static int FORMAT_MESSAGE_IGNORE_INSERTS() { - return (int)512L; - } - public static int FORMAT_MESSAGE_FROM_SYSTEM() { - return (int)4096L; - } - public static MethodHandle CreateFileW$MH() { - return RuntimeHelper.requireNonNull(constants$0.CreateFileW$MH,"CreateFileW"); - } - public static MemoryAddress CreateFileW ( Addressable lpFileName, int dwDesiredAccess, int dwShareMode, Addressable lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, Addressable hTemplateFile) { - var mh$ = CreateFileW$MH(); - try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle CloseHandle$MH() { - return RuntimeHelper.requireNonNull(constants$0.CloseHandle$MH,"CloseHandle"); - } - public static int CloseHandle ( Addressable hObject) { - var mh$ = CloseHandle$MH(); - try { - return (int)mh$.invokeExact(hObject); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle GetLastError$MH() { - return RuntimeHelper.requireNonNull(constants$0.GetLastError$MH,"GetLastError"); - } - public static int GetLastError () { - var mh$ = GetLastError$MH(); - try { - return (int)mh$.invokeExact(); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle DeviceIoControl$MH() { - return RuntimeHelper.requireNonNull(constants$0.DeviceIoControl$MH,"DeviceIoControl"); - } - public static int DeviceIoControl ( Addressable hDevice, int dwIoControlCode, Addressable lpInBuffer, int nInBufferSize, Addressable lpOutBuffer, int nOutBufferSize, Addressable lpBytesReturned, Addressable lpOverlapped) { - var mh$ = DeviceIoControl$MH(); - try { - return (int)mh$.invokeExact(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, lpBytesReturned, lpOverlapped); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle GetModuleHandleW$MH() { - return RuntimeHelper.requireNonNull(constants$0.GetModuleHandleW$MH,"GetModuleHandleW"); - } - public static MemoryAddress GetModuleHandleW ( Addressable lpModuleName) { - var mh$ = GetModuleHandleW$MH(); - try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(lpModuleName); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle LocalFree$MH() { - return RuntimeHelper.requireNonNull(constants$0.LocalFree$MH,"LocalFree"); - } - public static MemoryAddress LocalFree ( Addressable hMem) { - var mh$ = LocalFree$MH(); - try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(hMem); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle FormatMessageW$MH() { - return RuntimeHelper.requireNonNull(constants$1.FormatMessageW$MH,"FormatMessageW"); - } - public static int FormatMessageW ( int dwFlags, Addressable lpSource, int dwMessageId, int dwLanguageId, Addressable lpBuffer, int nSize, Addressable Arguments) { - var mh$ = FormatMessageW$MH(); - try { - return (int)mh$.invokeExact(dwFlags, lpSource, dwMessageId, dwLanguageId, lpBuffer, nSize, Arguments); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static int GENERIC_READ() { - return (int)2147483648L; - } - public static int GENERIC_WRITE() { - return (int)1073741824L; - } - public static int ERROR_SUCCESS() { - return (int)0L; - } - public static int ERROR_FILE_NOT_FOUND() { - return (int)2L; - } - public static int ERROR_INSUFFICIENT_BUFFER() { - return (int)122L; - } - public static int ERROR_MORE_DATA() { - return (int)234L; - } - public static int ERROR_NO_MORE_ITEMS() { - return (int)259L; - } -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/kernel32/RuntimeHelper.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/kernel32/RuntimeHelper.java deleted file mode 100644 index 2ed328fb..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/kernel32/RuntimeHelper.java +++ /dev/null @@ -1,216 +0,0 @@ -package net.codecrete.usb.windows.gen.kernel32; -// Generated by jextract - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import static java.lang.foreign.ValueLayout.*; - -final class RuntimeHelper { - - private RuntimeHelper() {} - private final static Linker LINKER = Linker.nativeLinker(); - private final static ClassLoader LOADER = RuntimeHelper.class.getClassLoader(); - private final static MethodHandles.Lookup MH_LOOKUP = MethodHandles.lookup(); - private final static SymbolLookup SYMBOL_LOOKUP; - - final static SegmentAllocator CONSTANT_ALLOCATOR = - (size, align) -> MemorySegment.allocateNative(size, align, MemorySession.openImplicit()); - - static { - System.loadLibrary("Kernel32"); - SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); - SYMBOL_LOOKUP = name -> loaderLookup.lookup(name).or(() -> LINKER.defaultLookup().lookup(name)); - } - - static T requireNonNull(T obj, String symbolName) { - if (obj == null) { - throw new UnsatisfiedLinkError("unresolved symbol: " + symbolName); - } - return obj; - } - - private final static SegmentAllocator THROWING_ALLOCATOR = (x, y) -> { throw new AssertionError("should not reach here"); }; - - static final MemorySegment lookupGlobalVariable(String name, MemoryLayout layout) { - return SYMBOL_LOOKUP.lookup(name).map(symbol -> MemorySegment.ofAddress(symbol.address(), layout.byteSize(), MemorySession.openShared())).orElse(null); - } - - static final MethodHandle downcallHandle(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> LINKER.downcallHandle(addr, fdesc)). - orElse(null); - } - - static final MethodHandle downcallHandle(FunctionDescriptor fdesc) { - return LINKER.downcallHandle(fdesc); - } - - static final MethodHandle downcallHandleVariadic(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> VarargsInvoker.make(addr, fdesc)). - orElse(null); - } - - static final MemorySegment upcallStub(Class fi, Z z, FunctionDescriptor fdesc, MemorySession session) { - try { - MethodHandle handle = MH_LOOKUP.findVirtual(fi, "apply", Linker.upcallType(fdesc)); - handle = handle.bindTo(z); - return LINKER.upcallStub(handle, fdesc, session); - } catch (Throwable ex) { - throw new AssertionError(ex); - } - } - - static MemorySegment asArray(MemoryAddress addr, MemoryLayout layout, int numElements, MemorySession session) { - return MemorySegment.ofAddress(addr, numElements * layout.byteSize(), session); - } - - // Internals only below this point - - private static class VarargsInvoker { - private static final MethodHandle INVOKE_MH; - private final MemorySegment symbol; - private final FunctionDescriptor function; - - private VarargsInvoker(MemorySegment symbol, FunctionDescriptor function) { - this.symbol = symbol; - this.function = function; - } - - static { - try { - INVOKE_MH = MethodHandles.lookup().findVirtual(VarargsInvoker.class, "invoke", MethodType.methodType(Object.class, SegmentAllocator.class, Object[].class)); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - static MethodHandle make(MemorySegment symbol, FunctionDescriptor function) { - VarargsInvoker invoker = new VarargsInvoker(symbol, function); - MethodHandle handle = INVOKE_MH.bindTo(invoker).asCollector(Object[].class, function.argumentLayouts().size() + 1); - MethodType mtype = MethodType.methodType(function.returnLayout().isPresent() ? carrier(function.returnLayout().get(), true) : void.class); - for (MemoryLayout layout : function.argumentLayouts()) { - mtype = mtype.appendParameterTypes(carrier(layout, false)); - } - mtype = mtype.appendParameterTypes(Object[].class); - if (mtype.returnType().equals(MemorySegment.class)) { - mtype = mtype.insertParameterTypes(0, SegmentAllocator.class); - } else { - handle = MethodHandles.insertArguments(handle, 0, THROWING_ALLOCATOR); - } - return handle.asType(mtype); - } - - static Class carrier(MemoryLayout layout, boolean ret) { - if (layout instanceof ValueLayout valueLayout) { - return (ret || valueLayout.carrier() != MemoryAddress.class) ? - valueLayout.carrier() : Addressable.class; - } else if (layout instanceof GroupLayout) { - return MemorySegment.class; - } else { - throw new AssertionError("Cannot get here!"); - } - } - - private Object invoke(SegmentAllocator allocator, Object[] args) throws Throwable { - // one trailing Object[] - int nNamedArgs = function.argumentLayouts().size(); - assert(args.length == nNamedArgs + 1); - // The last argument is the array of vararg collector - Object[] unnamedArgs = (Object[]) args[args.length - 1]; - - int argsCount = nNamedArgs + unnamedArgs.length; - Class[] argTypes = new Class[argsCount]; - MemoryLayout[] argLayouts = new MemoryLayout[nNamedArgs + unnamedArgs.length]; - - int pos = 0; - for (pos = 0; pos < nNamedArgs; pos++) { - argLayouts[pos] = function.argumentLayouts().get(pos); - } - - assert pos == nNamedArgs; - for (Object o: unnamedArgs) { - argLayouts[pos] = variadicLayout(normalize(o.getClass())); - pos++; - } - assert pos == argsCount; - - FunctionDescriptor f = (function.returnLayout().isEmpty()) ? - FunctionDescriptor.ofVoid(argLayouts) : - FunctionDescriptor.of(function.returnLayout().get(), argLayouts); - MethodHandle mh = LINKER.downcallHandle(symbol, f); - if (mh.type().returnType() == MemorySegment.class) { - mh = mh.bindTo(allocator); - } - // flatten argument list so that it can be passed to an asSpreader MH - Object[] allArgs = new Object[nNamedArgs + unnamedArgs.length]; - System.arraycopy(args, 0, allArgs, 0, nNamedArgs); - System.arraycopy(unnamedArgs, 0, allArgs, nNamedArgs, unnamedArgs.length); - - return mh.asSpreader(Object[].class, argsCount).invoke(allArgs); - } - - private static Class unboxIfNeeded(Class clazz) { - if (clazz == Boolean.class) { - return boolean.class; - } else if (clazz == Void.class) { - return void.class; - } else if (clazz == Byte.class) { - return byte.class; - } else if (clazz == Character.class) { - return char.class; - } else if (clazz == Short.class) { - return short.class; - } else if (clazz == Integer.class) { - return int.class; - } else if (clazz == Long.class) { - return long.class; - } else if (clazz == Float.class) { - return float.class; - } else if (clazz == Double.class) { - return double.class; - } else { - return clazz; - } - } - - private Class promote(Class c) { - if (c == byte.class || c == char.class || c == short.class || c == int.class) { - return long.class; - } else if (c == float.class) { - return double.class; - } else { - return c; - } - } - - private Class normalize(Class c) { - c = unboxIfNeeded(c); - if (c.isPrimitive()) { - return promote(c); - } - if (MemoryAddress.class.isAssignableFrom(c)) { - return MemoryAddress.class; - } - if (MemorySegment.class.isAssignableFrom(c)) { - return MemorySegment.class; - } - throw new IllegalArgumentException("Invalid type for ABI: " + c.getTypeName()); - } - - private MemoryLayout variadicLayout(Class c) { - if (c == long.class) { - return JAVA_LONG; - } else if (c == double.class) { - return JAVA_DOUBLE; - } else if (MemoryAddress.class.isAssignableFrom(c)) { - return ADDRESS; - } else { - throw new IllegalArgumentException("Unhandled variadic argument class: " + c); - } - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/kernel32/_GUID.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/kernel32/_GUID.java deleted file mode 100644 index 98826c26..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/kernel32/_GUID.java +++ /dev/null @@ -1,77 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.kernel32; - -import java.lang.foreign.*; -import java.lang.invoke.VarHandle; -public class _GUID { - - static final GroupLayout $struct$LAYOUT = MemoryLayout.structLayout( - Constants$root.C_LONG$LAYOUT.withName("Data1"), - Constants$root.C_SHORT$LAYOUT.withName("Data2"), - Constants$root.C_SHORT$LAYOUT.withName("Data3"), - MemoryLayout.sequenceLayout(8, Constants$root.C_CHAR$LAYOUT).withName("Data4") - ).withName("_GUID"); - public static MemoryLayout $LAYOUT() { - return _GUID.$struct$LAYOUT; - } - static final VarHandle Data1$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("Data1")); - public static VarHandle Data1$VH() { - return _GUID.Data1$VH; - } - public static int Data1$get(MemorySegment seg) { - return (int)_GUID.Data1$VH.get(seg); - } - public static void Data1$set( MemorySegment seg, int x) { - _GUID.Data1$VH.set(seg, x); - } - public static int Data1$get(MemorySegment seg, long index) { - return (int)_GUID.Data1$VH.get(seg.asSlice(index*sizeof())); - } - public static void Data1$set(MemorySegment seg, long index, int x) { - _GUID.Data1$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle Data2$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("Data2")); - public static VarHandle Data2$VH() { - return _GUID.Data2$VH; - } - public static short Data2$get(MemorySegment seg) { - return (short)_GUID.Data2$VH.get(seg); - } - public static void Data2$set( MemorySegment seg, short x) { - _GUID.Data2$VH.set(seg, x); - } - public static short Data2$get(MemorySegment seg, long index) { - return (short)_GUID.Data2$VH.get(seg.asSlice(index*sizeof())); - } - public static void Data2$set(MemorySegment seg, long index, short x) { - _GUID.Data2$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle Data3$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("Data3")); - public static VarHandle Data3$VH() { - return _GUID.Data3$VH; - } - public static short Data3$get(MemorySegment seg) { - return (short)_GUID.Data3$VH.get(seg); - } - public static void Data3$set( MemorySegment seg, short x) { - _GUID.Data3$VH.set(seg, x); - } - public static short Data3$get(MemorySegment seg, long index) { - return (short)_GUID.Data3$VH.get(seg.asSlice(index*sizeof())); - } - public static void Data3$set(MemorySegment seg, long index, short x) { - _GUID.Data3$VH.set(seg.asSlice(index*sizeof()), x); - } - public static MemorySegment Data4$slice(MemorySegment seg) { - return seg.asSlice(8, 8); - } - public static long sizeof() { return $LAYOUT().byteSize(); } - public static MemorySegment allocate(SegmentAllocator allocator) { return allocator.allocate($LAYOUT()); } - public static MemorySegment allocateArray(int len, SegmentAllocator allocator) { - return allocator.allocate(MemoryLayout.sequenceLayout(len, $LAYOUT())); - } - public static MemorySegment ofAddress(MemoryAddress addr, MemorySession session) { return RuntimeHelper.asArray(addr, $LAYOUT(), 1, session); } -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/kernel32/constants$0.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/kernel32/constants$0.java deleted file mode 100644 index 60564f7a..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/kernel32/constants$0.java +++ /dev/null @@ -1,64 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.kernel32; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.invoke.MethodHandle; -class constants$0 { - - static final FunctionDescriptor CreateFileW$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle CreateFileW$MH = RuntimeHelper.downcallHandle( - "CreateFileW", - constants$0.CreateFileW$FUNC - ); - static final FunctionDescriptor CloseHandle$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle CloseHandle$MH = RuntimeHelper.downcallHandle( - "CloseHandle", - constants$0.CloseHandle$FUNC - ); - static final FunctionDescriptor GetLastError$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT); - static final MethodHandle GetLastError$MH = RuntimeHelper.downcallHandle( - "GetLastError", - constants$0.GetLastError$FUNC - ); - static final FunctionDescriptor DeviceIoControl$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle DeviceIoControl$MH = RuntimeHelper.downcallHandle( - "DeviceIoControl", - constants$0.DeviceIoControl$FUNC - ); - static final FunctionDescriptor GetModuleHandleW$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle GetModuleHandleW$MH = RuntimeHelper.downcallHandle( - "GetModuleHandleW", - constants$0.GetModuleHandleW$FUNC - ); - static final FunctionDescriptor LocalFree$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle LocalFree$MH = RuntimeHelper.downcallHandle( - "LocalFree", - constants$0.LocalFree$FUNC - ); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/kernel32/constants$1.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/kernel32/constants$1.java deleted file mode 100644 index 1c4e52c7..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/kernel32/constants$1.java +++ /dev/null @@ -1,24 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.kernel32; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.invoke.MethodHandle; -class constants$1 { - - static final FunctionDescriptor FormatMessageW$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle FormatMessageW$MH = RuntimeHelper.downcallHandle( - "FormatMessageW", - constants$1.FormatMessageW$FUNC - ); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/ole32/Constants$root.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/ole32/Constants$root.java deleted file mode 100644 index b87f060d..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/ole32/Constants$root.java +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.ole32; - -import static java.lang.foreign.ValueLayout.*; -public class Constants$root { - - static final OfBoolean C_BOOL$LAYOUT = JAVA_BOOLEAN; - static final OfByte C_CHAR$LAYOUT = JAVA_BYTE; - static final OfShort C_SHORT$LAYOUT = JAVA_SHORT.withBitAlignment(16); - static final OfInt C_INT$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfInt C_LONG$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfLong C_LONG_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfFloat C_FLOAT$LAYOUT = JAVA_FLOAT.withBitAlignment(32); - static final OfDouble C_DOUBLE$LAYOUT = JAVA_DOUBLE.withBitAlignment(64); - static final OfAddress C_POINTER$LAYOUT = ADDRESS.withBitAlignment(64); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/ole32/Ole32.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/ole32/Ole32.java deleted file mode 100644 index f52d7fbe..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/ole32/Ole32.java +++ /dev/null @@ -1,33 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.ole32; - -import java.lang.foreign.Addressable; -import java.lang.invoke.MethodHandle; - -import static java.lang.foreign.ValueLayout.*; -public class Ole32 { - - /* package-private */ Ole32() {} - public static OfByte C_CHAR = Constants$root.C_CHAR$LAYOUT; - public static OfShort C_SHORT = Constants$root.C_SHORT$LAYOUT; - public static OfInt C_INT = Constants$root.C_LONG$LAYOUT; - public static OfInt C_LONG = Constants$root.C_LONG$LAYOUT; - public static OfLong C_LONG_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfFloat C_FLOAT = Constants$root.C_FLOAT$LAYOUT; - public static OfDouble C_DOUBLE = Constants$root.C_DOUBLE$LAYOUT; - public static OfAddress C_POINTER = Constants$root.C_POINTER$LAYOUT; - public static MethodHandle CLSIDFromString$MH() { - return RuntimeHelper.requireNonNull(constants$0.CLSIDFromString$MH,"CLSIDFromString"); - } - public static int CLSIDFromString ( Addressable lpsz, Addressable pclsid) { - var mh$ = CLSIDFromString$MH(); - try { - return (int)mh$.invokeExact(lpsz, pclsid); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/ole32/RuntimeHelper.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/ole32/RuntimeHelper.java deleted file mode 100644 index cd067be7..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/ole32/RuntimeHelper.java +++ /dev/null @@ -1,216 +0,0 @@ -package net.codecrete.usb.windows.gen.ole32; -// Generated by jextract - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import static java.lang.foreign.ValueLayout.*; - -final class RuntimeHelper { - - private RuntimeHelper() {} - private final static Linker LINKER = Linker.nativeLinker(); - private final static ClassLoader LOADER = RuntimeHelper.class.getClassLoader(); - private final static MethodHandles.Lookup MH_LOOKUP = MethodHandles.lookup(); - private final static SymbolLookup SYMBOL_LOOKUP; - - final static SegmentAllocator CONSTANT_ALLOCATOR = - (size, align) -> MemorySegment.allocateNative(size, align, MemorySession.openImplicit()); - - static { - System.loadLibrary("Ole32"); - SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); - SYMBOL_LOOKUP = name -> loaderLookup.lookup(name).or(() -> LINKER.defaultLookup().lookup(name)); - } - - static T requireNonNull(T obj, String symbolName) { - if (obj == null) { - throw new UnsatisfiedLinkError("unresolved symbol: " + symbolName); - } - return obj; - } - - private final static SegmentAllocator THROWING_ALLOCATOR = (x, y) -> { throw new AssertionError("should not reach here"); }; - - static final MemorySegment lookupGlobalVariable(String name, MemoryLayout layout) { - return SYMBOL_LOOKUP.lookup(name).map(symbol -> MemorySegment.ofAddress(symbol.address(), layout.byteSize(), MemorySession.openShared())).orElse(null); - } - - static final MethodHandle downcallHandle(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> LINKER.downcallHandle(addr, fdesc)). - orElse(null); - } - - static final MethodHandle downcallHandle(FunctionDescriptor fdesc) { - return LINKER.downcallHandle(fdesc); - } - - static final MethodHandle downcallHandleVariadic(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> VarargsInvoker.make(addr, fdesc)). - orElse(null); - } - - static final MemorySegment upcallStub(Class fi, Z z, FunctionDescriptor fdesc, MemorySession session) { - try { - MethodHandle handle = MH_LOOKUP.findVirtual(fi, "apply", Linker.upcallType(fdesc)); - handle = handle.bindTo(z); - return LINKER.upcallStub(handle, fdesc, session); - } catch (Throwable ex) { - throw new AssertionError(ex); - } - } - - static MemorySegment asArray(MemoryAddress addr, MemoryLayout layout, int numElements, MemorySession session) { - return MemorySegment.ofAddress(addr, numElements * layout.byteSize(), session); - } - - // Internals only below this point - - private static class VarargsInvoker { - private static final MethodHandle INVOKE_MH; - private final MemorySegment symbol; - private final FunctionDescriptor function; - - private VarargsInvoker(MemorySegment symbol, FunctionDescriptor function) { - this.symbol = symbol; - this.function = function; - } - - static { - try { - INVOKE_MH = MethodHandles.lookup().findVirtual(VarargsInvoker.class, "invoke", MethodType.methodType(Object.class, SegmentAllocator.class, Object[].class)); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - static MethodHandle make(MemorySegment symbol, FunctionDescriptor function) { - VarargsInvoker invoker = new VarargsInvoker(symbol, function); - MethodHandle handle = INVOKE_MH.bindTo(invoker).asCollector(Object[].class, function.argumentLayouts().size() + 1); - MethodType mtype = MethodType.methodType(function.returnLayout().isPresent() ? carrier(function.returnLayout().get(), true) : void.class); - for (MemoryLayout layout : function.argumentLayouts()) { - mtype = mtype.appendParameterTypes(carrier(layout, false)); - } - mtype = mtype.appendParameterTypes(Object[].class); - if (mtype.returnType().equals(MemorySegment.class)) { - mtype = mtype.insertParameterTypes(0, SegmentAllocator.class); - } else { - handle = MethodHandles.insertArguments(handle, 0, THROWING_ALLOCATOR); - } - return handle.asType(mtype); - } - - static Class carrier(MemoryLayout layout, boolean ret) { - if (layout instanceof ValueLayout valueLayout) { - return (ret || valueLayout.carrier() != MemoryAddress.class) ? - valueLayout.carrier() : Addressable.class; - } else if (layout instanceof GroupLayout) { - return MemorySegment.class; - } else { - throw new AssertionError("Cannot get here!"); - } - } - - private Object invoke(SegmentAllocator allocator, Object[] args) throws Throwable { - // one trailing Object[] - int nNamedArgs = function.argumentLayouts().size(); - assert(args.length == nNamedArgs + 1); - // The last argument is the array of vararg collector - Object[] unnamedArgs = (Object[]) args[args.length - 1]; - - int argsCount = nNamedArgs + unnamedArgs.length; - Class[] argTypes = new Class[argsCount]; - MemoryLayout[] argLayouts = new MemoryLayout[nNamedArgs + unnamedArgs.length]; - - int pos = 0; - for (pos = 0; pos < nNamedArgs; pos++) { - argLayouts[pos] = function.argumentLayouts().get(pos); - } - - assert pos == nNamedArgs; - for (Object o: unnamedArgs) { - argLayouts[pos] = variadicLayout(normalize(o.getClass())); - pos++; - } - assert pos == argsCount; - - FunctionDescriptor f = (function.returnLayout().isEmpty()) ? - FunctionDescriptor.ofVoid(argLayouts) : - FunctionDescriptor.of(function.returnLayout().get(), argLayouts); - MethodHandle mh = LINKER.downcallHandle(symbol, f); - if (mh.type().returnType() == MemorySegment.class) { - mh = mh.bindTo(allocator); - } - // flatten argument list so that it can be passed to an asSpreader MH - Object[] allArgs = new Object[nNamedArgs + unnamedArgs.length]; - System.arraycopy(args, 0, allArgs, 0, nNamedArgs); - System.arraycopy(unnamedArgs, 0, allArgs, nNamedArgs, unnamedArgs.length); - - return mh.asSpreader(Object[].class, argsCount).invoke(allArgs); - } - - private static Class unboxIfNeeded(Class clazz) { - if (clazz == Boolean.class) { - return boolean.class; - } else if (clazz == Void.class) { - return void.class; - } else if (clazz == Byte.class) { - return byte.class; - } else if (clazz == Character.class) { - return char.class; - } else if (clazz == Short.class) { - return short.class; - } else if (clazz == Integer.class) { - return int.class; - } else if (clazz == Long.class) { - return long.class; - } else if (clazz == Float.class) { - return float.class; - } else if (clazz == Double.class) { - return double.class; - } else { - return clazz; - } - } - - private Class promote(Class c) { - if (c == byte.class || c == char.class || c == short.class || c == int.class) { - return long.class; - } else if (c == float.class) { - return double.class; - } else { - return c; - } - } - - private Class normalize(Class c) { - c = unboxIfNeeded(c); - if (c.isPrimitive()) { - return promote(c); - } - if (MemoryAddress.class.isAssignableFrom(c)) { - return MemoryAddress.class; - } - if (MemorySegment.class.isAssignableFrom(c)) { - return MemorySegment.class; - } - throw new IllegalArgumentException("Invalid type for ABI: " + c.getTypeName()); - } - - private MemoryLayout variadicLayout(Class c) { - if (c == long.class) { - return JAVA_LONG; - } else if (c == double.class) { - return JAVA_DOUBLE; - } else if (MemoryAddress.class.isAssignableFrom(c)) { - return ADDRESS; - } else { - throw new IllegalArgumentException("Unhandled variadic argument class: " + c); - } - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/ole32/constants$0.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/ole32/constants$0.java deleted file mode 100644 index b42663eb..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/ole32/constants$0.java +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.ole32; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.invoke.MethodHandle; -class constants$0 { - - static final FunctionDescriptor CLSIDFromString$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle CLSIDFromString$MH = RuntimeHelper.downcallHandle( - "CLSIDFromString", - constants$0.CLSIDFromString$FUNC - ); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/Constants$root.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/Constants$root.java deleted file mode 100644 index b6bb6431..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/Constants$root.java +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.setupapi; - -import static java.lang.foreign.ValueLayout.*; -public class Constants$root { - - static final OfBoolean C_BOOL$LAYOUT = JAVA_BOOLEAN; - static final OfByte C_CHAR$LAYOUT = JAVA_BYTE; - static final OfShort C_SHORT$LAYOUT = JAVA_SHORT.withBitAlignment(16); - static final OfInt C_INT$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfInt C_LONG$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfLong C_LONG_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfFloat C_FLOAT$LAYOUT = JAVA_FLOAT.withBitAlignment(32); - static final OfDouble C_DOUBLE$LAYOUT = JAVA_DOUBLE.withBitAlignment(64); - static final OfAddress C_POINTER$LAYOUT = ADDRESS.withBitAlignment(64); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/RuntimeHelper.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/RuntimeHelper.java deleted file mode 100644 index 55f8e04a..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/RuntimeHelper.java +++ /dev/null @@ -1,216 +0,0 @@ -package net.codecrete.usb.windows.gen.setupapi; -// Generated by jextract - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import static java.lang.foreign.ValueLayout.*; - -final class RuntimeHelper { - - private RuntimeHelper() {} - private final static Linker LINKER = Linker.nativeLinker(); - private final static ClassLoader LOADER = RuntimeHelper.class.getClassLoader(); - private final static MethodHandles.Lookup MH_LOOKUP = MethodHandles.lookup(); - private final static SymbolLookup SYMBOL_LOOKUP; - - final static SegmentAllocator CONSTANT_ALLOCATOR = - (size, align) -> MemorySegment.allocateNative(size, align, MemorySession.openImplicit()); - - static { - System.loadLibrary("SetupAPI"); - SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); - SYMBOL_LOOKUP = name -> loaderLookup.lookup(name).or(() -> LINKER.defaultLookup().lookup(name)); - } - - static T requireNonNull(T obj, String symbolName) { - if (obj == null) { - throw new UnsatisfiedLinkError("unresolved symbol: " + symbolName); - } - return obj; - } - - private final static SegmentAllocator THROWING_ALLOCATOR = (x, y) -> { throw new AssertionError("should not reach here"); }; - - static final MemorySegment lookupGlobalVariable(String name, MemoryLayout layout) { - return SYMBOL_LOOKUP.lookup(name).map(symbol -> MemorySegment.ofAddress(symbol.address(), layout.byteSize(), MemorySession.openShared())).orElse(null); - } - - static final MethodHandle downcallHandle(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> LINKER.downcallHandle(addr, fdesc)). - orElse(null); - } - - static final MethodHandle downcallHandle(FunctionDescriptor fdesc) { - return LINKER.downcallHandle(fdesc); - } - - static final MethodHandle downcallHandleVariadic(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> VarargsInvoker.make(addr, fdesc)). - orElse(null); - } - - static final MemorySegment upcallStub(Class fi, Z z, FunctionDescriptor fdesc, MemorySession session) { - try { - MethodHandle handle = MH_LOOKUP.findVirtual(fi, "apply", Linker.upcallType(fdesc)); - handle = handle.bindTo(z); - return LINKER.upcallStub(handle, fdesc, session); - } catch (Throwable ex) { - throw new AssertionError(ex); - } - } - - static MemorySegment asArray(MemoryAddress addr, MemoryLayout layout, int numElements, MemorySession session) { - return MemorySegment.ofAddress(addr, numElements * layout.byteSize(), session); - } - - // Internals only below this point - - private static class VarargsInvoker { - private static final MethodHandle INVOKE_MH; - private final MemorySegment symbol; - private final FunctionDescriptor function; - - private VarargsInvoker(MemorySegment symbol, FunctionDescriptor function) { - this.symbol = symbol; - this.function = function; - } - - static { - try { - INVOKE_MH = MethodHandles.lookup().findVirtual(VarargsInvoker.class, "invoke", MethodType.methodType(Object.class, SegmentAllocator.class, Object[].class)); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - static MethodHandle make(MemorySegment symbol, FunctionDescriptor function) { - VarargsInvoker invoker = new VarargsInvoker(symbol, function); - MethodHandle handle = INVOKE_MH.bindTo(invoker).asCollector(Object[].class, function.argumentLayouts().size() + 1); - MethodType mtype = MethodType.methodType(function.returnLayout().isPresent() ? carrier(function.returnLayout().get(), true) : void.class); - for (MemoryLayout layout : function.argumentLayouts()) { - mtype = mtype.appendParameterTypes(carrier(layout, false)); - } - mtype = mtype.appendParameterTypes(Object[].class); - if (mtype.returnType().equals(MemorySegment.class)) { - mtype = mtype.insertParameterTypes(0, SegmentAllocator.class); - } else { - handle = MethodHandles.insertArguments(handle, 0, THROWING_ALLOCATOR); - } - return handle.asType(mtype); - } - - static Class carrier(MemoryLayout layout, boolean ret) { - if (layout instanceof ValueLayout valueLayout) { - return (ret || valueLayout.carrier() != MemoryAddress.class) ? - valueLayout.carrier() : Addressable.class; - } else if (layout instanceof GroupLayout) { - return MemorySegment.class; - } else { - throw new AssertionError("Cannot get here!"); - } - } - - private Object invoke(SegmentAllocator allocator, Object[] args) throws Throwable { - // one trailing Object[] - int nNamedArgs = function.argumentLayouts().size(); - assert(args.length == nNamedArgs + 1); - // The last argument is the array of vararg collector - Object[] unnamedArgs = (Object[]) args[args.length - 1]; - - int argsCount = nNamedArgs + unnamedArgs.length; - Class[] argTypes = new Class[argsCount]; - MemoryLayout[] argLayouts = new MemoryLayout[nNamedArgs + unnamedArgs.length]; - - int pos = 0; - for (pos = 0; pos < nNamedArgs; pos++) { - argLayouts[pos] = function.argumentLayouts().get(pos); - } - - assert pos == nNamedArgs; - for (Object o: unnamedArgs) { - argLayouts[pos] = variadicLayout(normalize(o.getClass())); - pos++; - } - assert pos == argsCount; - - FunctionDescriptor f = (function.returnLayout().isEmpty()) ? - FunctionDescriptor.ofVoid(argLayouts) : - FunctionDescriptor.of(function.returnLayout().get(), argLayouts); - MethodHandle mh = LINKER.downcallHandle(symbol, f); - if (mh.type().returnType() == MemorySegment.class) { - mh = mh.bindTo(allocator); - } - // flatten argument list so that it can be passed to an asSpreader MH - Object[] allArgs = new Object[nNamedArgs + unnamedArgs.length]; - System.arraycopy(args, 0, allArgs, 0, nNamedArgs); - System.arraycopy(unnamedArgs, 0, allArgs, nNamedArgs, unnamedArgs.length); - - return mh.asSpreader(Object[].class, argsCount).invoke(allArgs); - } - - private static Class unboxIfNeeded(Class clazz) { - if (clazz == Boolean.class) { - return boolean.class; - } else if (clazz == Void.class) { - return void.class; - } else if (clazz == Byte.class) { - return byte.class; - } else if (clazz == Character.class) { - return char.class; - } else if (clazz == Short.class) { - return short.class; - } else if (clazz == Integer.class) { - return int.class; - } else if (clazz == Long.class) { - return long.class; - } else if (clazz == Float.class) { - return float.class; - } else if (clazz == Double.class) { - return double.class; - } else { - return clazz; - } - } - - private Class promote(Class c) { - if (c == byte.class || c == char.class || c == short.class || c == int.class) { - return long.class; - } else if (c == float.class) { - return double.class; - } else { - return c; - } - } - - private Class normalize(Class c) { - c = unboxIfNeeded(c); - if (c.isPrimitive()) { - return promote(c); - } - if (MemoryAddress.class.isAssignableFrom(c)) { - return MemoryAddress.class; - } - if (MemorySegment.class.isAssignableFrom(c)) { - return MemorySegment.class; - } - throw new IllegalArgumentException("Invalid type for ABI: " + c.getTypeName()); - } - - private MemoryLayout variadicLayout(Class c) { - if (c == long.class) { - return JAVA_LONG; - } else if (c == double.class) { - return JAVA_DOUBLE; - } else if (MemoryAddress.class.isAssignableFrom(c)) { - return ADDRESS; - } else { - throw new IllegalArgumentException("Unhandled variadic argument class: " + c); - } - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/SP_DEVICE_INTERFACE_DATA.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/SP_DEVICE_INTERFACE_DATA.java deleted file mode 100644 index 95322b6e..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/SP_DEVICE_INTERFACE_DATA.java +++ /dev/null @@ -1,9 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.setupapi; - -public class SP_DEVICE_INTERFACE_DATA extends _SP_DEVICE_INTERFACE_DATA { - -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/SP_DEVICE_INTERFACE_DETAIL_DATA_W.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/SP_DEVICE_INTERFACE_DETAIL_DATA_W.java deleted file mode 100644 index 77a1e06d..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/SP_DEVICE_INTERFACE_DETAIL_DATA_W.java +++ /dev/null @@ -1,9 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.setupapi; - -public class SP_DEVICE_INTERFACE_DETAIL_DATA_W extends _SP_DEVICE_INTERFACE_DETAIL_DATA_W { - -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/SP_DEVINFO_DATA.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/SP_DEVINFO_DATA.java deleted file mode 100644 index 89a49f0d..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/SP_DEVINFO_DATA.java +++ /dev/null @@ -1,9 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.setupapi; - -public class SP_DEVINFO_DATA extends _SP_DEVINFO_DATA { - -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/SetupAPI.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/SetupAPI.java deleted file mode 100644 index f860b4ec..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/SetupAPI.java +++ /dev/null @@ -1,179 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.setupapi; - -import java.lang.foreign.Addressable; -import java.lang.foreign.MemoryAddress; -import java.lang.invoke.MethodHandle; - -import static java.lang.foreign.ValueLayout.*; -public class SetupAPI { - - /* package-private */ SetupAPI() {} - public static OfByte C_CHAR = Constants$root.C_CHAR$LAYOUT; - public static OfShort C_SHORT = Constants$root.C_SHORT$LAYOUT; - public static OfInt C_INT = Constants$root.C_LONG$LAYOUT; - public static OfInt C_LONG = Constants$root.C_LONG$LAYOUT; - public static OfLong C_LONG_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfFloat C_FLOAT = Constants$root.C_FLOAT$LAYOUT; - public static OfDouble C_DOUBLE = Constants$root.C_DOUBLE$LAYOUT; - public static OfAddress C_POINTER = Constants$root.C_POINTER$LAYOUT; - public static int DEVPROP_TYPEMOD_LIST() { - return (int)8192L; - } - public static int DEVPROP_TYPE_UINT32() { - return (int)7L; - } - public static int DEVPROP_TYPE_STRING() { - return (int)18L; - } - public static int DICS_FLAG_GLOBAL() { - return (int)1L; - } - public static int DIGCF_PRESENT() { - return (int)2L; - } - public static int DIGCF_DEVICEINTERFACE() { - return (int)16L; - } - public static int DIREG_DEV() { - return (int)1L; - } - public static MethodHandle SetupDiCreateDeviceInfoList$MH() { - return RuntimeHelper.requireNonNull(constants$0.SetupDiCreateDeviceInfoList$MH,"SetupDiCreateDeviceInfoList"); - } - public static MemoryAddress SetupDiCreateDeviceInfoList ( Addressable ClassGuid, Addressable hwndParent) { - var mh$ = SetupDiCreateDeviceInfoList$MH(); - try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(ClassGuid, hwndParent); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle SetupDiOpenDeviceInfoW$MH() { - return RuntimeHelper.requireNonNull(constants$0.SetupDiOpenDeviceInfoW$MH,"SetupDiOpenDeviceInfoW"); - } - public static int SetupDiOpenDeviceInfoW ( Addressable DeviceInfoSet, Addressable DeviceInstanceId, Addressable hwndParent, int OpenFlags, Addressable DeviceInfoData) { - var mh$ = SetupDiOpenDeviceInfoW$MH(); - try { - return (int)mh$.invokeExact(DeviceInfoSet, DeviceInstanceId, hwndParent, OpenFlags, DeviceInfoData); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle SetupDiEnumDeviceInfo$MH() { - return RuntimeHelper.requireNonNull(constants$0.SetupDiEnumDeviceInfo$MH,"SetupDiEnumDeviceInfo"); - } - public static int SetupDiEnumDeviceInfo ( Addressable DeviceInfoSet, int MemberIndex, Addressable DeviceInfoData) { - var mh$ = SetupDiEnumDeviceInfo$MH(); - try { - return (int)mh$.invokeExact(DeviceInfoSet, MemberIndex, DeviceInfoData); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle SetupDiDestroyDeviceInfoList$MH() { - return RuntimeHelper.requireNonNull(constants$0.SetupDiDestroyDeviceInfoList$MH,"SetupDiDestroyDeviceInfoList"); - } - public static int SetupDiDestroyDeviceInfoList ( Addressable DeviceInfoSet) { - var mh$ = SetupDiDestroyDeviceInfoList$MH(); - try { - return (int)mh$.invokeExact(DeviceInfoSet); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle SetupDiEnumDeviceInterfaces$MH() { - return RuntimeHelper.requireNonNull(constants$0.SetupDiEnumDeviceInterfaces$MH,"SetupDiEnumDeviceInterfaces"); - } - public static int SetupDiEnumDeviceInterfaces ( Addressable DeviceInfoSet, Addressable DeviceInfoData, Addressable InterfaceClassGuid, int MemberIndex, Addressable DeviceInterfaceData) { - var mh$ = SetupDiEnumDeviceInterfaces$MH(); - try { - return (int)mh$.invokeExact(DeviceInfoSet, DeviceInfoData, InterfaceClassGuid, MemberIndex, DeviceInterfaceData); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle SetupDiOpenDeviceInterfaceW$MH() { - return RuntimeHelper.requireNonNull(constants$0.SetupDiOpenDeviceInterfaceW$MH,"SetupDiOpenDeviceInterfaceW"); - } - public static int SetupDiOpenDeviceInterfaceW ( Addressable DeviceInfoSet, Addressable DevicePath, int OpenFlags, Addressable DeviceInterfaceData) { - var mh$ = SetupDiOpenDeviceInterfaceW$MH(); - try { - return (int)mh$.invokeExact(DeviceInfoSet, DevicePath, OpenFlags, DeviceInterfaceData); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle SetupDiDeleteDeviceInterfaceData$MH() { - return RuntimeHelper.requireNonNull(constants$1.SetupDiDeleteDeviceInterfaceData$MH,"SetupDiDeleteDeviceInterfaceData"); - } - public static int SetupDiDeleteDeviceInterfaceData ( Addressable DeviceInfoSet, Addressable DeviceInterfaceData) { - var mh$ = SetupDiDeleteDeviceInterfaceData$MH(); - try { - return (int)mh$.invokeExact(DeviceInfoSet, DeviceInterfaceData); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle SetupDiGetDeviceInterfaceDetailW$MH() { - return RuntimeHelper.requireNonNull(constants$1.SetupDiGetDeviceInterfaceDetailW$MH,"SetupDiGetDeviceInterfaceDetailW"); - } - public static int SetupDiGetDeviceInterfaceDetailW ( Addressable DeviceInfoSet, Addressable DeviceInterfaceData, Addressable DeviceInterfaceDetailData, int DeviceInterfaceDetailDataSize, Addressable RequiredSize, Addressable DeviceInfoData) { - var mh$ = SetupDiGetDeviceInterfaceDetailW$MH(); - try { - return (int)mh$.invokeExact(DeviceInfoSet, DeviceInterfaceData, DeviceInterfaceDetailData, DeviceInterfaceDetailDataSize, RequiredSize, DeviceInfoData); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle SetupDiGetClassDevsW$MH() { - return RuntimeHelper.requireNonNull(constants$1.SetupDiGetClassDevsW$MH,"SetupDiGetClassDevsW"); - } - public static MemoryAddress SetupDiGetClassDevsW ( Addressable ClassGuid, Addressable Enumerator, Addressable hwndParent, int Flags) { - var mh$ = SetupDiGetClassDevsW$MH(); - try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(ClassGuid, Enumerator, hwndParent, Flags); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle SetupDiOpenDevRegKey$MH() { - return RuntimeHelper.requireNonNull(constants$1.SetupDiOpenDevRegKey$MH,"SetupDiOpenDevRegKey"); - } - public static MemoryAddress SetupDiOpenDevRegKey ( Addressable DeviceInfoSet, Addressable DeviceInfoData, int Scope, int HwProfile, int KeyType, int samDesired) { - var mh$ = SetupDiOpenDevRegKey$MH(); - try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(DeviceInfoSet, DeviceInfoData, Scope, HwProfile, KeyType, samDesired); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle SetupDiGetDevicePropertyW$MH() { - return RuntimeHelper.requireNonNull(constants$1.SetupDiGetDevicePropertyW$MH,"SetupDiGetDevicePropertyW"); - } - public static int SetupDiGetDevicePropertyW ( Addressable DeviceInfoSet, Addressable DeviceInfoData, Addressable PropertyKey, Addressable PropertyType, Addressable PropertyBuffer, int PropertyBufferSize, Addressable RequiredSize, int Flags) { - var mh$ = SetupDiGetDevicePropertyW$MH(); - try { - return (int)mh$.invokeExact(DeviceInfoSet, DeviceInfoData, PropertyKey, PropertyType, PropertyBuffer, PropertyBufferSize, RequiredSize, Flags); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle SetupDiGetDeviceRegistryPropertyW$MH() { - return RuntimeHelper.requireNonNull(constants$1.SetupDiGetDeviceRegistryPropertyW$MH,"SetupDiGetDeviceRegistryPropertyW"); - } - public static int SetupDiGetDeviceRegistryPropertyW ( Addressable DeviceInfoSet, Addressable DeviceInfoData, int Property, Addressable PropertyRegDataType, Addressable PropertyBuffer, int PropertyBufferSize, Addressable RequiredSize) { - var mh$ = SetupDiGetDeviceRegistryPropertyW$MH(); - try { - return (int)mh$.invokeExact(DeviceInfoSet, DeviceInfoData, Property, PropertyRegDataType, PropertyBuffer, PropertyBufferSize, RequiredSize); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static int SPDRP_ADDRESS() { - return (int)28L; - } -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/_SP_DEVICE_INTERFACE_DATA.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/_SP_DEVICE_INTERFACE_DATA.java deleted file mode 100644 index de057457..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/_SP_DEVICE_INTERFACE_DATA.java +++ /dev/null @@ -1,82 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.setupapi; - -import java.lang.foreign.*; -import java.lang.invoke.VarHandle; -public class _SP_DEVICE_INTERFACE_DATA { - - static final GroupLayout $struct$LAYOUT = MemoryLayout.structLayout( - Constants$root.C_LONG$LAYOUT.withName("cbSize"), - MemoryLayout.structLayout( - Constants$root.C_LONG$LAYOUT.withName("Data1"), - Constants$root.C_SHORT$LAYOUT.withName("Data2"), - Constants$root.C_SHORT$LAYOUT.withName("Data3"), - MemoryLayout.sequenceLayout(8, Constants$root.C_CHAR$LAYOUT).withName("Data4") - ).withName("InterfaceClassGuid"), - Constants$root.C_LONG$LAYOUT.withName("Flags"), - Constants$root.C_LONG_LONG$LAYOUT.withName("Reserved") - ).withName("_SP_DEVICE_INTERFACE_DATA"); - public static MemoryLayout $LAYOUT() { - return _SP_DEVICE_INTERFACE_DATA.$struct$LAYOUT; - } - static final VarHandle cbSize$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("cbSize")); - public static VarHandle cbSize$VH() { - return _SP_DEVICE_INTERFACE_DATA.cbSize$VH; - } - public static int cbSize$get(MemorySegment seg) { - return (int)_SP_DEVICE_INTERFACE_DATA.cbSize$VH.get(seg); - } - public static void cbSize$set( MemorySegment seg, int x) { - _SP_DEVICE_INTERFACE_DATA.cbSize$VH.set(seg, x); - } - public static int cbSize$get(MemorySegment seg, long index) { - return (int)_SP_DEVICE_INTERFACE_DATA.cbSize$VH.get(seg.asSlice(index*sizeof())); - } - public static void cbSize$set(MemorySegment seg, long index, int x) { - _SP_DEVICE_INTERFACE_DATA.cbSize$VH.set(seg.asSlice(index*sizeof()), x); - } - public static MemorySegment InterfaceClassGuid$slice(MemorySegment seg) { - return seg.asSlice(4, 16); - } - static final VarHandle Flags$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("Flags")); - public static VarHandle Flags$VH() { - return _SP_DEVICE_INTERFACE_DATA.Flags$VH; - } - public static int Flags$get(MemorySegment seg) { - return (int)_SP_DEVICE_INTERFACE_DATA.Flags$VH.get(seg); - } - public static void Flags$set( MemorySegment seg, int x) { - _SP_DEVICE_INTERFACE_DATA.Flags$VH.set(seg, x); - } - public static int Flags$get(MemorySegment seg, long index) { - return (int)_SP_DEVICE_INTERFACE_DATA.Flags$VH.get(seg.asSlice(index*sizeof())); - } - public static void Flags$set(MemorySegment seg, long index, int x) { - _SP_DEVICE_INTERFACE_DATA.Flags$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle Reserved$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("Reserved")); - public static VarHandle Reserved$VH() { - return _SP_DEVICE_INTERFACE_DATA.Reserved$VH; - } - public static long Reserved$get(MemorySegment seg) { - return (long)_SP_DEVICE_INTERFACE_DATA.Reserved$VH.get(seg); - } - public static void Reserved$set( MemorySegment seg, long x) { - _SP_DEVICE_INTERFACE_DATA.Reserved$VH.set(seg, x); - } - public static long Reserved$get(MemorySegment seg, long index) { - return (long)_SP_DEVICE_INTERFACE_DATA.Reserved$VH.get(seg.asSlice(index*sizeof())); - } - public static void Reserved$set(MemorySegment seg, long index, long x) { - _SP_DEVICE_INTERFACE_DATA.Reserved$VH.set(seg.asSlice(index*sizeof()), x); - } - public static long sizeof() { return $LAYOUT().byteSize(); } - public static MemorySegment allocate(SegmentAllocator allocator) { return allocator.allocate($LAYOUT()); } - public static MemorySegment allocateArray(int len, SegmentAllocator allocator) { - return allocator.allocate(MemoryLayout.sequenceLayout(len, $LAYOUT())); - } - public static MemorySegment ofAddress(MemoryAddress addr, MemorySession session) { return RuntimeHelper.asArray(addr, $LAYOUT(), 1, session); } -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/_SP_DEVICE_INTERFACE_DETAIL_DATA_W.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/_SP_DEVICE_INTERFACE_DETAIL_DATA_W.java deleted file mode 100644 index bc3ac979..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/_SP_DEVICE_INTERFACE_DETAIL_DATA_W.java +++ /dev/null @@ -1,44 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.setupapi; - -import java.lang.foreign.*; -import java.lang.invoke.VarHandle; -public class _SP_DEVICE_INTERFACE_DETAIL_DATA_W { - - static final GroupLayout $struct$LAYOUT = MemoryLayout.structLayout( - Constants$root.C_LONG$LAYOUT.withName("cbSize"), - MemoryLayout.sequenceLayout(1, Constants$root.C_SHORT$LAYOUT).withName("DevicePath"), - MemoryLayout.paddingLayout(16) - ).withName("_SP_DEVICE_INTERFACE_DETAIL_DATA_W"); - public static MemoryLayout $LAYOUT() { - return _SP_DEVICE_INTERFACE_DETAIL_DATA_W.$struct$LAYOUT; - } - static final VarHandle cbSize$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("cbSize")); - public static VarHandle cbSize$VH() { - return _SP_DEVICE_INTERFACE_DETAIL_DATA_W.cbSize$VH; - } - public static int cbSize$get(MemorySegment seg) { - return (int)_SP_DEVICE_INTERFACE_DETAIL_DATA_W.cbSize$VH.get(seg); - } - public static void cbSize$set( MemorySegment seg, int x) { - _SP_DEVICE_INTERFACE_DETAIL_DATA_W.cbSize$VH.set(seg, x); - } - public static int cbSize$get(MemorySegment seg, long index) { - return (int)_SP_DEVICE_INTERFACE_DETAIL_DATA_W.cbSize$VH.get(seg.asSlice(index*sizeof())); - } - public static void cbSize$set(MemorySegment seg, long index, int x) { - _SP_DEVICE_INTERFACE_DETAIL_DATA_W.cbSize$VH.set(seg.asSlice(index*sizeof()), x); - } - public static MemorySegment DevicePath$slice(MemorySegment seg) { - return seg.asSlice(4, 2); - } - public static long sizeof() { return $LAYOUT().byteSize(); } - public static MemorySegment allocate(SegmentAllocator allocator) { return allocator.allocate($LAYOUT()); } - public static MemorySegment allocateArray(int len, SegmentAllocator allocator) { - return allocator.allocate(MemoryLayout.sequenceLayout(len, $LAYOUT())); - } - public static MemorySegment ofAddress(MemoryAddress addr, MemorySession session) { return RuntimeHelper.asArray(addr, $LAYOUT(), 1, session); } -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/_SP_DEVINFO_DATA.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/_SP_DEVINFO_DATA.java deleted file mode 100644 index dbba0ab5..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/_SP_DEVINFO_DATA.java +++ /dev/null @@ -1,82 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.setupapi; - -import java.lang.foreign.*; -import java.lang.invoke.VarHandle; -public class _SP_DEVINFO_DATA { - - static final GroupLayout $struct$LAYOUT = MemoryLayout.structLayout( - Constants$root.C_LONG$LAYOUT.withName("cbSize"), - MemoryLayout.structLayout( - Constants$root.C_LONG$LAYOUT.withName("Data1"), - Constants$root.C_SHORT$LAYOUT.withName("Data2"), - Constants$root.C_SHORT$LAYOUT.withName("Data3"), - MemoryLayout.sequenceLayout(8, Constants$root.C_CHAR$LAYOUT).withName("Data4") - ).withName("ClassGuid"), - Constants$root.C_LONG$LAYOUT.withName("DevInst"), - Constants$root.C_LONG_LONG$LAYOUT.withName("Reserved") - ).withName("_SP_DEVINFO_DATA"); - public static MemoryLayout $LAYOUT() { - return _SP_DEVINFO_DATA.$struct$LAYOUT; - } - static final VarHandle cbSize$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("cbSize")); - public static VarHandle cbSize$VH() { - return _SP_DEVINFO_DATA.cbSize$VH; - } - public static int cbSize$get(MemorySegment seg) { - return (int)_SP_DEVINFO_DATA.cbSize$VH.get(seg); - } - public static void cbSize$set( MemorySegment seg, int x) { - _SP_DEVINFO_DATA.cbSize$VH.set(seg, x); - } - public static int cbSize$get(MemorySegment seg, long index) { - return (int)_SP_DEVINFO_DATA.cbSize$VH.get(seg.asSlice(index*sizeof())); - } - public static void cbSize$set(MemorySegment seg, long index, int x) { - _SP_DEVINFO_DATA.cbSize$VH.set(seg.asSlice(index*sizeof()), x); - } - public static MemorySegment ClassGuid$slice(MemorySegment seg) { - return seg.asSlice(4, 16); - } - static final VarHandle DevInst$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("DevInst")); - public static VarHandle DevInst$VH() { - return _SP_DEVINFO_DATA.DevInst$VH; - } - public static int DevInst$get(MemorySegment seg) { - return (int)_SP_DEVINFO_DATA.DevInst$VH.get(seg); - } - public static void DevInst$set( MemorySegment seg, int x) { - _SP_DEVINFO_DATA.DevInst$VH.set(seg, x); - } - public static int DevInst$get(MemorySegment seg, long index) { - return (int)_SP_DEVINFO_DATA.DevInst$VH.get(seg.asSlice(index*sizeof())); - } - public static void DevInst$set(MemorySegment seg, long index, int x) { - _SP_DEVINFO_DATA.DevInst$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle Reserved$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("Reserved")); - public static VarHandle Reserved$VH() { - return _SP_DEVINFO_DATA.Reserved$VH; - } - public static long Reserved$get(MemorySegment seg) { - return (long)_SP_DEVINFO_DATA.Reserved$VH.get(seg); - } - public static void Reserved$set( MemorySegment seg, long x) { - _SP_DEVINFO_DATA.Reserved$VH.set(seg, x); - } - public static long Reserved$get(MemorySegment seg, long index) { - return (long)_SP_DEVINFO_DATA.Reserved$VH.get(seg.asSlice(index*sizeof())); - } - public static void Reserved$set(MemorySegment seg, long index, long x) { - _SP_DEVINFO_DATA.Reserved$VH.set(seg.asSlice(index*sizeof()), x); - } - public static long sizeof() { return $LAYOUT().byteSize(); } - public static MemorySegment allocate(SegmentAllocator allocator) { return allocator.allocate($LAYOUT()); } - public static MemorySegment allocateArray(int len, SegmentAllocator allocator) { - return allocator.allocate(MemoryLayout.sequenceLayout(len, $LAYOUT())); - } - public static MemorySegment ofAddress(MemoryAddress addr, MemorySession session) { return RuntimeHelper.asArray(addr, $LAYOUT(), 1, session); } -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/constants$0.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/constants$0.java deleted file mode 100644 index 60b0522a..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/constants$0.java +++ /dev/null @@ -1,67 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.setupapi; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.invoke.MethodHandle; -class constants$0 { - - static final FunctionDescriptor SetupDiCreateDeviceInfoList$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle SetupDiCreateDeviceInfoList$MH = RuntimeHelper.downcallHandle( - "SetupDiCreateDeviceInfoList", - constants$0.SetupDiCreateDeviceInfoList$FUNC - ); - static final FunctionDescriptor SetupDiOpenDeviceInfoW$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle SetupDiOpenDeviceInfoW$MH = RuntimeHelper.downcallHandle( - "SetupDiOpenDeviceInfoW", - constants$0.SetupDiOpenDeviceInfoW$FUNC - ); - static final FunctionDescriptor SetupDiEnumDeviceInfo$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle SetupDiEnumDeviceInfo$MH = RuntimeHelper.downcallHandle( - "SetupDiEnumDeviceInfo", - constants$0.SetupDiEnumDeviceInfo$FUNC - ); - static final FunctionDescriptor SetupDiDestroyDeviceInfoList$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle SetupDiDestroyDeviceInfoList$MH = RuntimeHelper.downcallHandle( - "SetupDiDestroyDeviceInfoList", - constants$0.SetupDiDestroyDeviceInfoList$FUNC - ); - static final FunctionDescriptor SetupDiEnumDeviceInterfaces$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle SetupDiEnumDeviceInterfaces$MH = RuntimeHelper.downcallHandle( - "SetupDiEnumDeviceInterfaces", - constants$0.SetupDiEnumDeviceInterfaces$FUNC - ); - static final FunctionDescriptor SetupDiOpenDeviceInterfaceW$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle SetupDiOpenDeviceInterfaceW$MH = RuntimeHelper.downcallHandle( - "SetupDiOpenDeviceInterfaceW", - constants$0.SetupDiOpenDeviceInterfaceW$FUNC - ); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/constants$1.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/constants$1.java deleted file mode 100644 index 1cadf471..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/setupapi/constants$1.java +++ /dev/null @@ -1,80 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.setupapi; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.invoke.MethodHandle; -class constants$1 { - - static final FunctionDescriptor SetupDiDeleteDeviceInterfaceData$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle SetupDiDeleteDeviceInterfaceData$MH = RuntimeHelper.downcallHandle( - "SetupDiDeleteDeviceInterfaceData", - constants$1.SetupDiDeleteDeviceInterfaceData$FUNC - ); - static final FunctionDescriptor SetupDiGetDeviceInterfaceDetailW$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle SetupDiGetDeviceInterfaceDetailW$MH = RuntimeHelper.downcallHandle( - "SetupDiGetDeviceInterfaceDetailW", - constants$1.SetupDiGetDeviceInterfaceDetailW$FUNC - ); - static final FunctionDescriptor SetupDiGetClassDevsW$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT - ); - static final MethodHandle SetupDiGetClassDevsW$MH = RuntimeHelper.downcallHandle( - "SetupDiGetClassDevsW", - constants$1.SetupDiGetClassDevsW$FUNC - ); - static final FunctionDescriptor SetupDiOpenDevRegKey$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_LONG$LAYOUT - ); - static final MethodHandle SetupDiOpenDevRegKey$MH = RuntimeHelper.downcallHandle( - "SetupDiOpenDevRegKey", - constants$1.SetupDiOpenDevRegKey$FUNC - ); - static final FunctionDescriptor SetupDiGetDevicePropertyW$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT - ); - static final MethodHandle SetupDiGetDevicePropertyW$MH = RuntimeHelper.downcallHandle( - "SetupDiGetDevicePropertyW", - constants$1.SetupDiGetDevicePropertyW$FUNC - ); - static final FunctionDescriptor SetupDiGetDeviceRegistryPropertyW$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle SetupDiGetDeviceRegistryPropertyW$MH = RuntimeHelper.downcallHandle( - "SetupDiGetDeviceRegistryPropertyW", - constants$1.SetupDiGetDeviceRegistryPropertyW$FUNC - ); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/stdlib/Constants$root.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/stdlib/Constants$root.java deleted file mode 100644 index 80f6c451..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/stdlib/Constants$root.java +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.stdlib; - -import static java.lang.foreign.ValueLayout.*; -public class Constants$root { - - static final OfBoolean C_BOOL$LAYOUT = JAVA_BOOLEAN; - static final OfByte C_CHAR$LAYOUT = JAVA_BYTE; - static final OfShort C_SHORT$LAYOUT = JAVA_SHORT.withBitAlignment(16); - static final OfInt C_INT$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfInt C_LONG$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfLong C_LONG_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfFloat C_FLOAT$LAYOUT = JAVA_FLOAT.withBitAlignment(32); - static final OfDouble C_DOUBLE$LAYOUT = JAVA_DOUBLE.withBitAlignment(64); - static final OfAddress C_POINTER$LAYOUT = ADDRESS.withBitAlignment(64); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/stdlib/RuntimeHelper.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/stdlib/RuntimeHelper.java deleted file mode 100644 index ffabd418..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/stdlib/RuntimeHelper.java +++ /dev/null @@ -1,216 +0,0 @@ -package net.codecrete.usb.windows.gen.stdlib; -// Generated by jextract - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import static java.lang.foreign.ValueLayout.*; - -final class RuntimeHelper { - - private RuntimeHelper() {} - private final static Linker LINKER = Linker.nativeLinker(); - private final static ClassLoader LOADER = RuntimeHelper.class.getClassLoader(); - private final static MethodHandles.Lookup MH_LOOKUP = MethodHandles.lookup(); - private final static SymbolLookup SYMBOL_LOOKUP; - - final static SegmentAllocator CONSTANT_ALLOCATOR = - (size, align) -> MemorySegment.allocateNative(size, align, MemorySession.openImplicit()); - - static { - - SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); - SYMBOL_LOOKUP = name -> loaderLookup.lookup(name).or(() -> LINKER.defaultLookup().lookup(name)); - } - - static T requireNonNull(T obj, String symbolName) { - if (obj == null) { - throw new UnsatisfiedLinkError("unresolved symbol: " + symbolName); - } - return obj; - } - - private final static SegmentAllocator THROWING_ALLOCATOR = (x, y) -> { throw new AssertionError("should not reach here"); }; - - static final MemorySegment lookupGlobalVariable(String name, MemoryLayout layout) { - return SYMBOL_LOOKUP.lookup(name).map(symbol -> MemorySegment.ofAddress(symbol.address(), layout.byteSize(), MemorySession.openShared())).orElse(null); - } - - static final MethodHandle downcallHandle(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> LINKER.downcallHandle(addr, fdesc)). - orElse(null); - } - - static final MethodHandle downcallHandle(FunctionDescriptor fdesc) { - return LINKER.downcallHandle(fdesc); - } - - static final MethodHandle downcallHandleVariadic(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> VarargsInvoker.make(addr, fdesc)). - orElse(null); - } - - static final MemorySegment upcallStub(Class fi, Z z, FunctionDescriptor fdesc, MemorySession session) { - try { - MethodHandle handle = MH_LOOKUP.findVirtual(fi, "apply", Linker.upcallType(fdesc)); - handle = handle.bindTo(z); - return LINKER.upcallStub(handle, fdesc, session); - } catch (Throwable ex) { - throw new AssertionError(ex); - } - } - - static MemorySegment asArray(MemoryAddress addr, MemoryLayout layout, int numElements, MemorySession session) { - return MemorySegment.ofAddress(addr, numElements * layout.byteSize(), session); - } - - // Internals only below this point - - private static class VarargsInvoker { - private static final MethodHandle INVOKE_MH; - private final MemorySegment symbol; - private final FunctionDescriptor function; - - private VarargsInvoker(MemorySegment symbol, FunctionDescriptor function) { - this.symbol = symbol; - this.function = function; - } - - static { - try { - INVOKE_MH = MethodHandles.lookup().findVirtual(VarargsInvoker.class, "invoke", MethodType.methodType(Object.class, SegmentAllocator.class, Object[].class)); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - static MethodHandle make(MemorySegment symbol, FunctionDescriptor function) { - VarargsInvoker invoker = new VarargsInvoker(symbol, function); - MethodHandle handle = INVOKE_MH.bindTo(invoker).asCollector(Object[].class, function.argumentLayouts().size() + 1); - MethodType mtype = MethodType.methodType(function.returnLayout().isPresent() ? carrier(function.returnLayout().get(), true) : void.class); - for (MemoryLayout layout : function.argumentLayouts()) { - mtype = mtype.appendParameterTypes(carrier(layout, false)); - } - mtype = mtype.appendParameterTypes(Object[].class); - if (mtype.returnType().equals(MemorySegment.class)) { - mtype = mtype.insertParameterTypes(0, SegmentAllocator.class); - } else { - handle = MethodHandles.insertArguments(handle, 0, THROWING_ALLOCATOR); - } - return handle.asType(mtype); - } - - static Class carrier(MemoryLayout layout, boolean ret) { - if (layout instanceof ValueLayout valueLayout) { - return (ret || valueLayout.carrier() != MemoryAddress.class) ? - valueLayout.carrier() : Addressable.class; - } else if (layout instanceof GroupLayout) { - return MemorySegment.class; - } else { - throw new AssertionError("Cannot get here!"); - } - } - - private Object invoke(SegmentAllocator allocator, Object[] args) throws Throwable { - // one trailing Object[] - int nNamedArgs = function.argumentLayouts().size(); - assert(args.length == nNamedArgs + 1); - // The last argument is the array of vararg collector - Object[] unnamedArgs = (Object[]) args[args.length - 1]; - - int argsCount = nNamedArgs + unnamedArgs.length; - Class[] argTypes = new Class[argsCount]; - MemoryLayout[] argLayouts = new MemoryLayout[nNamedArgs + unnamedArgs.length]; - - int pos = 0; - for (pos = 0; pos < nNamedArgs; pos++) { - argLayouts[pos] = function.argumentLayouts().get(pos); - } - - assert pos == nNamedArgs; - for (Object o: unnamedArgs) { - argLayouts[pos] = variadicLayout(normalize(o.getClass())); - pos++; - } - assert pos == argsCount; - - FunctionDescriptor f = (function.returnLayout().isEmpty()) ? - FunctionDescriptor.ofVoid(argLayouts) : - FunctionDescriptor.of(function.returnLayout().get(), argLayouts); - MethodHandle mh = LINKER.downcallHandle(symbol, f); - if (mh.type().returnType() == MemorySegment.class) { - mh = mh.bindTo(allocator); - } - // flatten argument list so that it can be passed to an asSpreader MH - Object[] allArgs = new Object[nNamedArgs + unnamedArgs.length]; - System.arraycopy(args, 0, allArgs, 0, nNamedArgs); - System.arraycopy(unnamedArgs, 0, allArgs, nNamedArgs, unnamedArgs.length); - - return mh.asSpreader(Object[].class, argsCount).invoke(allArgs); - } - - private static Class unboxIfNeeded(Class clazz) { - if (clazz == Boolean.class) { - return boolean.class; - } else if (clazz == Void.class) { - return void.class; - } else if (clazz == Byte.class) { - return byte.class; - } else if (clazz == Character.class) { - return char.class; - } else if (clazz == Short.class) { - return short.class; - } else if (clazz == Integer.class) { - return int.class; - } else if (clazz == Long.class) { - return long.class; - } else if (clazz == Float.class) { - return float.class; - } else if (clazz == Double.class) { - return double.class; - } else { - return clazz; - } - } - - private Class promote(Class c) { - if (c == byte.class || c == char.class || c == short.class || c == int.class) { - return long.class; - } else if (c == float.class) { - return double.class; - } else { - return c; - } - } - - private Class normalize(Class c) { - c = unboxIfNeeded(c); - if (c.isPrimitive()) { - return promote(c); - } - if (MemoryAddress.class.isAssignableFrom(c)) { - return MemoryAddress.class; - } - if (MemorySegment.class.isAssignableFrom(c)) { - return MemorySegment.class; - } - throw new IllegalArgumentException("Invalid type for ABI: " + c.getTypeName()); - } - - private MemoryLayout variadicLayout(Class c) { - if (c == long.class) { - return JAVA_LONG; - } else if (c == double.class) { - return JAVA_DOUBLE; - } else if (MemoryAddress.class.isAssignableFrom(c)) { - return ADDRESS; - } else { - throw new IllegalArgumentException("Unhandled variadic argument class: " + c); - } - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/stdlib/StdLib.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/stdlib/StdLib.java deleted file mode 100644 index e76fe363..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/stdlib/StdLib.java +++ /dev/null @@ -1,33 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.stdlib; - -import java.lang.foreign.Addressable; -import java.lang.invoke.MethodHandle; - -import static java.lang.foreign.ValueLayout.*; -public class StdLib { - - /* package-private */ StdLib() {} - public static OfByte C_CHAR = Constants$root.C_CHAR$LAYOUT; - public static OfShort C_SHORT = Constants$root.C_SHORT$LAYOUT; - public static OfInt C_INT = Constants$root.C_LONG$LAYOUT; - public static OfInt C_LONG = Constants$root.C_LONG$LAYOUT; - public static OfLong C_LONG_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfFloat C_FLOAT = Constants$root.C_FLOAT$LAYOUT; - public static OfDouble C_DOUBLE = Constants$root.C_DOUBLE$LAYOUT; - public static OfAddress C_POINTER = Constants$root.C_POINTER$LAYOUT; - public static MethodHandle wcslen$MH() { - return RuntimeHelper.requireNonNull(constants$0.wcslen$MH,"wcslen"); - } - public static long wcslen ( Addressable _String) { - var mh$ = wcslen$MH(); - try { - return (long)mh$.invokeExact(_String); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/stdlib/constants$0.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/stdlib/constants$0.java deleted file mode 100644 index 5ca00b1c..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/stdlib/constants$0.java +++ /dev/null @@ -1,18 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.stdlib; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.invoke.MethodHandle; -class constants$0 { - - static final FunctionDescriptor wcslen$FUNC = FunctionDescriptor.of(Constants$root.C_LONG_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle wcslen$MH = RuntimeHelper.downcallHandle( - "wcslen", - constants$0.wcslen$FUNC - ); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/usbioctl/Constants$root.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/usbioctl/Constants$root.java deleted file mode 100644 index 66ce20c5..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/usbioctl/Constants$root.java +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.usbioctl; - -import static java.lang.foreign.ValueLayout.*; -public class Constants$root { - - static final OfBoolean C_BOOL$LAYOUT = JAVA_BOOLEAN; - static final OfByte C_CHAR$LAYOUT = JAVA_BYTE; - static final OfShort C_SHORT$LAYOUT = JAVA_SHORT.withBitAlignment(16); - static final OfInt C_INT$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfInt C_LONG$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfLong C_LONG_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfFloat C_FLOAT$LAYOUT = JAVA_FLOAT.withBitAlignment(32); - static final OfDouble C_DOUBLE$LAYOUT = JAVA_DOUBLE.withBitAlignment(64); - static final OfAddress C_POINTER$LAYOUT = ADDRESS.withBitAlignment(64); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/usbioctl/RuntimeHelper.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/usbioctl/RuntimeHelper.java deleted file mode 100644 index 5e1c6f71..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/usbioctl/RuntimeHelper.java +++ /dev/null @@ -1,216 +0,0 @@ -package net.codecrete.usb.windows.gen.usbioctl; -// Generated by jextract - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import static java.lang.foreign.ValueLayout.*; - -final class RuntimeHelper { - - private RuntimeHelper() {} - private final static Linker LINKER = Linker.nativeLinker(); - private final static ClassLoader LOADER = RuntimeHelper.class.getClassLoader(); - private final static MethodHandles.Lookup MH_LOOKUP = MethodHandles.lookup(); - private final static SymbolLookup SYMBOL_LOOKUP; - - final static SegmentAllocator CONSTANT_ALLOCATOR = - (size, align) -> MemorySegment.allocateNative(size, align, MemorySession.openImplicit()); - - static { - - SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); - SYMBOL_LOOKUP = name -> loaderLookup.lookup(name).or(() -> LINKER.defaultLookup().lookup(name)); - } - - static T requireNonNull(T obj, String symbolName) { - if (obj == null) { - throw new UnsatisfiedLinkError("unresolved symbol: " + symbolName); - } - return obj; - } - - private final static SegmentAllocator THROWING_ALLOCATOR = (x, y) -> { throw new AssertionError("should not reach here"); }; - - static final MemorySegment lookupGlobalVariable(String name, MemoryLayout layout) { - return SYMBOL_LOOKUP.lookup(name).map(symbol -> MemorySegment.ofAddress(symbol.address(), layout.byteSize(), MemorySession.openShared())).orElse(null); - } - - static final MethodHandle downcallHandle(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> LINKER.downcallHandle(addr, fdesc)). - orElse(null); - } - - static final MethodHandle downcallHandle(FunctionDescriptor fdesc) { - return LINKER.downcallHandle(fdesc); - } - - static final MethodHandle downcallHandleVariadic(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> VarargsInvoker.make(addr, fdesc)). - orElse(null); - } - - static final MemorySegment upcallStub(Class fi, Z z, FunctionDescriptor fdesc, MemorySession session) { - try { - MethodHandle handle = MH_LOOKUP.findVirtual(fi, "apply", Linker.upcallType(fdesc)); - handle = handle.bindTo(z); - return LINKER.upcallStub(handle, fdesc, session); - } catch (Throwable ex) { - throw new AssertionError(ex); - } - } - - static MemorySegment asArray(MemoryAddress addr, MemoryLayout layout, int numElements, MemorySession session) { - return MemorySegment.ofAddress(addr, numElements * layout.byteSize(), session); - } - - // Internals only below this point - - private static class VarargsInvoker { - private static final MethodHandle INVOKE_MH; - private final MemorySegment symbol; - private final FunctionDescriptor function; - - private VarargsInvoker(MemorySegment symbol, FunctionDescriptor function) { - this.symbol = symbol; - this.function = function; - } - - static { - try { - INVOKE_MH = MethodHandles.lookup().findVirtual(VarargsInvoker.class, "invoke", MethodType.methodType(Object.class, SegmentAllocator.class, Object[].class)); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - static MethodHandle make(MemorySegment symbol, FunctionDescriptor function) { - VarargsInvoker invoker = new VarargsInvoker(symbol, function); - MethodHandle handle = INVOKE_MH.bindTo(invoker).asCollector(Object[].class, function.argumentLayouts().size() + 1); - MethodType mtype = MethodType.methodType(function.returnLayout().isPresent() ? carrier(function.returnLayout().get(), true) : void.class); - for (MemoryLayout layout : function.argumentLayouts()) { - mtype = mtype.appendParameterTypes(carrier(layout, false)); - } - mtype = mtype.appendParameterTypes(Object[].class); - if (mtype.returnType().equals(MemorySegment.class)) { - mtype = mtype.insertParameterTypes(0, SegmentAllocator.class); - } else { - handle = MethodHandles.insertArguments(handle, 0, THROWING_ALLOCATOR); - } - return handle.asType(mtype); - } - - static Class carrier(MemoryLayout layout, boolean ret) { - if (layout instanceof ValueLayout valueLayout) { - return (ret || valueLayout.carrier() != MemoryAddress.class) ? - valueLayout.carrier() : Addressable.class; - } else if (layout instanceof GroupLayout) { - return MemorySegment.class; - } else { - throw new AssertionError("Cannot get here!"); - } - } - - private Object invoke(SegmentAllocator allocator, Object[] args) throws Throwable { - // one trailing Object[] - int nNamedArgs = function.argumentLayouts().size(); - assert(args.length == nNamedArgs + 1); - // The last argument is the array of vararg collector - Object[] unnamedArgs = (Object[]) args[args.length - 1]; - - int argsCount = nNamedArgs + unnamedArgs.length; - Class[] argTypes = new Class[argsCount]; - MemoryLayout[] argLayouts = new MemoryLayout[nNamedArgs + unnamedArgs.length]; - - int pos = 0; - for (pos = 0; pos < nNamedArgs; pos++) { - argLayouts[pos] = function.argumentLayouts().get(pos); - } - - assert pos == nNamedArgs; - for (Object o: unnamedArgs) { - argLayouts[pos] = variadicLayout(normalize(o.getClass())); - pos++; - } - assert pos == argsCount; - - FunctionDescriptor f = (function.returnLayout().isEmpty()) ? - FunctionDescriptor.ofVoid(argLayouts) : - FunctionDescriptor.of(function.returnLayout().get(), argLayouts); - MethodHandle mh = LINKER.downcallHandle(symbol, f); - if (mh.type().returnType() == MemorySegment.class) { - mh = mh.bindTo(allocator); - } - // flatten argument list so that it can be passed to an asSpreader MH - Object[] allArgs = new Object[nNamedArgs + unnamedArgs.length]; - System.arraycopy(args, 0, allArgs, 0, nNamedArgs); - System.arraycopy(unnamedArgs, 0, allArgs, nNamedArgs, unnamedArgs.length); - - return mh.asSpreader(Object[].class, argsCount).invoke(allArgs); - } - - private static Class unboxIfNeeded(Class clazz) { - if (clazz == Boolean.class) { - return boolean.class; - } else if (clazz == Void.class) { - return void.class; - } else if (clazz == Byte.class) { - return byte.class; - } else if (clazz == Character.class) { - return char.class; - } else if (clazz == Short.class) { - return short.class; - } else if (clazz == Integer.class) { - return int.class; - } else if (clazz == Long.class) { - return long.class; - } else if (clazz == Float.class) { - return float.class; - } else if (clazz == Double.class) { - return double.class; - } else { - return clazz; - } - } - - private Class promote(Class c) { - if (c == byte.class || c == char.class || c == short.class || c == int.class) { - return long.class; - } else if (c == float.class) { - return double.class; - } else { - return c; - } - } - - private Class normalize(Class c) { - c = unboxIfNeeded(c); - if (c.isPrimitive()) { - return promote(c); - } - if (MemoryAddress.class.isAssignableFrom(c)) { - return MemoryAddress.class; - } - if (MemorySegment.class.isAssignableFrom(c)) { - return MemorySegment.class; - } - throw new IllegalArgumentException("Invalid type for ABI: " + c.getTypeName()); - } - - private MemoryLayout variadicLayout(Class c) { - if (c == long.class) { - return JAVA_LONG; - } else if (c == double.class) { - return JAVA_DOUBLE; - } else if (MemoryAddress.class.isAssignableFrom(c)) { - return ADDRESS; - } else { - throw new IllegalArgumentException("Unhandled variadic argument class: " + c); - } - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/usbioctl/USBIoctl.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/usbioctl/USBIoctl.java deleted file mode 100644 index bbb4e966..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/usbioctl/USBIoctl.java +++ /dev/null @@ -1,25 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.usbioctl; - -import static java.lang.foreign.ValueLayout.*; -public class USBIoctl { - - /* package-private */ USBIoctl() {} - public static OfByte C_CHAR = Constants$root.C_CHAR$LAYOUT; - public static OfShort C_SHORT = Constants$root.C_SHORT$LAYOUT; - public static OfInt C_INT = Constants$root.C_LONG$LAYOUT; - public static OfInt C_LONG = Constants$root.C_LONG$LAYOUT; - public static OfLong C_LONG_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfFloat C_FLOAT = Constants$root.C_FLOAT$LAYOUT; - public static OfDouble C_DOUBLE = Constants$root.C_DOUBLE$LAYOUT; - public static OfAddress C_POINTER = Constants$root.C_POINTER$LAYOUT; - public static int IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION() { - return (int)2229264L; - } - public static int IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX() { - return (int)2229320L; - } -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/Constants$root.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/Constants$root.java deleted file mode 100644 index 91462c49..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/Constants$root.java +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.user32; - -import static java.lang.foreign.ValueLayout.*; -public class Constants$root { - - static final OfBoolean C_BOOL$LAYOUT = JAVA_BOOLEAN; - static final OfByte C_CHAR$LAYOUT = JAVA_BYTE; - static final OfShort C_SHORT$LAYOUT = JAVA_SHORT.withBitAlignment(16); - static final OfInt C_INT$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfInt C_LONG$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfLong C_LONG_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfFloat C_FLOAT$LAYOUT = JAVA_FLOAT.withBitAlignment(32); - static final OfDouble C_DOUBLE$LAYOUT = JAVA_DOUBLE.withBitAlignment(64); - static final OfAddress C_POINTER$LAYOUT = ADDRESS.withBitAlignment(64); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/DEV_BROADCAST_DEVICEINTERFACE_W.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/DEV_BROADCAST_DEVICEINTERFACE_W.java deleted file mode 100644 index 42c68946..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/DEV_BROADCAST_DEVICEINTERFACE_W.java +++ /dev/null @@ -1,9 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.user32; - -public class DEV_BROADCAST_DEVICEINTERFACE_W extends _DEV_BROADCAST_DEVICEINTERFACE_W { - -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/DEV_BROADCAST_HDR.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/DEV_BROADCAST_HDR.java deleted file mode 100644 index bc2a60af..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/DEV_BROADCAST_HDR.java +++ /dev/null @@ -1,9 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.user32; - -public class DEV_BROADCAST_HDR extends _DEV_BROADCAST_HDR { - -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/MSG.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/MSG.java deleted file mode 100644 index 2cff5412..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/MSG.java +++ /dev/null @@ -1,9 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.user32; - -public class MSG extends tagMSG { - -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/RuntimeHelper.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/RuntimeHelper.java deleted file mode 100644 index 6da1ee8a..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/RuntimeHelper.java +++ /dev/null @@ -1,216 +0,0 @@ -package net.codecrete.usb.windows.gen.user32; -// Generated by jextract - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import static java.lang.foreign.ValueLayout.*; - -final class RuntimeHelper { - - private RuntimeHelper() {} - private final static Linker LINKER = Linker.nativeLinker(); - private final static ClassLoader LOADER = RuntimeHelper.class.getClassLoader(); - private final static MethodHandles.Lookup MH_LOOKUP = MethodHandles.lookup(); - private final static SymbolLookup SYMBOL_LOOKUP; - - final static SegmentAllocator CONSTANT_ALLOCATOR = - (size, align) -> MemorySegment.allocateNative(size, align, MemorySession.openImplicit()); - - static { - System.loadLibrary("User32"); - SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); - SYMBOL_LOOKUP = name -> loaderLookup.lookup(name).or(() -> LINKER.defaultLookup().lookup(name)); - } - - static T requireNonNull(T obj, String symbolName) { - if (obj == null) { - throw new UnsatisfiedLinkError("unresolved symbol: " + symbolName); - } - return obj; - } - - private final static SegmentAllocator THROWING_ALLOCATOR = (x, y) -> { throw new AssertionError("should not reach here"); }; - - static final MemorySegment lookupGlobalVariable(String name, MemoryLayout layout) { - return SYMBOL_LOOKUP.lookup(name).map(symbol -> MemorySegment.ofAddress(symbol.address(), layout.byteSize(), MemorySession.openShared())).orElse(null); - } - - static final MethodHandle downcallHandle(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> LINKER.downcallHandle(addr, fdesc)). - orElse(null); - } - - static final MethodHandle downcallHandle(FunctionDescriptor fdesc) { - return LINKER.downcallHandle(fdesc); - } - - static final MethodHandle downcallHandleVariadic(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> VarargsInvoker.make(addr, fdesc)). - orElse(null); - } - - static final MemorySegment upcallStub(Class fi, Z z, FunctionDescriptor fdesc, MemorySession session) { - try { - MethodHandle handle = MH_LOOKUP.findVirtual(fi, "apply", Linker.upcallType(fdesc)); - handle = handle.bindTo(z); - return LINKER.upcallStub(handle, fdesc, session); - } catch (Throwable ex) { - throw new AssertionError(ex); - } - } - - static MemorySegment asArray(MemoryAddress addr, MemoryLayout layout, int numElements, MemorySession session) { - return MemorySegment.ofAddress(addr, numElements * layout.byteSize(), session); - } - - // Internals only below this point - - private static class VarargsInvoker { - private static final MethodHandle INVOKE_MH; - private final MemorySegment symbol; - private final FunctionDescriptor function; - - private VarargsInvoker(MemorySegment symbol, FunctionDescriptor function) { - this.symbol = symbol; - this.function = function; - } - - static { - try { - INVOKE_MH = MethodHandles.lookup().findVirtual(VarargsInvoker.class, "invoke", MethodType.methodType(Object.class, SegmentAllocator.class, Object[].class)); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - static MethodHandle make(MemorySegment symbol, FunctionDescriptor function) { - VarargsInvoker invoker = new VarargsInvoker(symbol, function); - MethodHandle handle = INVOKE_MH.bindTo(invoker).asCollector(Object[].class, function.argumentLayouts().size() + 1); - MethodType mtype = MethodType.methodType(function.returnLayout().isPresent() ? carrier(function.returnLayout().get(), true) : void.class); - for (MemoryLayout layout : function.argumentLayouts()) { - mtype = mtype.appendParameterTypes(carrier(layout, false)); - } - mtype = mtype.appendParameterTypes(Object[].class); - if (mtype.returnType().equals(MemorySegment.class)) { - mtype = mtype.insertParameterTypes(0, SegmentAllocator.class); - } else { - handle = MethodHandles.insertArguments(handle, 0, THROWING_ALLOCATOR); - } - return handle.asType(mtype); - } - - static Class carrier(MemoryLayout layout, boolean ret) { - if (layout instanceof ValueLayout valueLayout) { - return (ret || valueLayout.carrier() != MemoryAddress.class) ? - valueLayout.carrier() : Addressable.class; - } else if (layout instanceof GroupLayout) { - return MemorySegment.class; - } else { - throw new AssertionError("Cannot get here!"); - } - } - - private Object invoke(SegmentAllocator allocator, Object[] args) throws Throwable { - // one trailing Object[] - int nNamedArgs = function.argumentLayouts().size(); - assert(args.length == nNamedArgs + 1); - // The last argument is the array of vararg collector - Object[] unnamedArgs = (Object[]) args[args.length - 1]; - - int argsCount = nNamedArgs + unnamedArgs.length; - Class[] argTypes = new Class[argsCount]; - MemoryLayout[] argLayouts = new MemoryLayout[nNamedArgs + unnamedArgs.length]; - - int pos = 0; - for (pos = 0; pos < nNamedArgs; pos++) { - argLayouts[pos] = function.argumentLayouts().get(pos); - } - - assert pos == nNamedArgs; - for (Object o: unnamedArgs) { - argLayouts[pos] = variadicLayout(normalize(o.getClass())); - pos++; - } - assert pos == argsCount; - - FunctionDescriptor f = (function.returnLayout().isEmpty()) ? - FunctionDescriptor.ofVoid(argLayouts) : - FunctionDescriptor.of(function.returnLayout().get(), argLayouts); - MethodHandle mh = LINKER.downcallHandle(symbol, f); - if (mh.type().returnType() == MemorySegment.class) { - mh = mh.bindTo(allocator); - } - // flatten argument list so that it can be passed to an asSpreader MH - Object[] allArgs = new Object[nNamedArgs + unnamedArgs.length]; - System.arraycopy(args, 0, allArgs, 0, nNamedArgs); - System.arraycopy(unnamedArgs, 0, allArgs, nNamedArgs, unnamedArgs.length); - - return mh.asSpreader(Object[].class, argsCount).invoke(allArgs); - } - - private static Class unboxIfNeeded(Class clazz) { - if (clazz == Boolean.class) { - return boolean.class; - } else if (clazz == Void.class) { - return void.class; - } else if (clazz == Byte.class) { - return byte.class; - } else if (clazz == Character.class) { - return char.class; - } else if (clazz == Short.class) { - return short.class; - } else if (clazz == Integer.class) { - return int.class; - } else if (clazz == Long.class) { - return long.class; - } else if (clazz == Float.class) { - return float.class; - } else if (clazz == Double.class) { - return double.class; - } else { - return clazz; - } - } - - private Class promote(Class c) { - if (c == byte.class || c == char.class || c == short.class || c == int.class) { - return long.class; - } else if (c == float.class) { - return double.class; - } else { - return c; - } - } - - private Class normalize(Class c) { - c = unboxIfNeeded(c); - if (c.isPrimitive()) { - return promote(c); - } - if (MemoryAddress.class.isAssignableFrom(c)) { - return MemoryAddress.class; - } - if (MemorySegment.class.isAssignableFrom(c)) { - return MemorySegment.class; - } - throw new IllegalArgumentException("Invalid type for ABI: " + c.getTypeName()); - } - - private MemoryLayout variadicLayout(Class c) { - if (c == long.class) { - return JAVA_LONG; - } else if (c == double.class) { - return JAVA_DOUBLE; - } else if (MemoryAddress.class.isAssignableFrom(c)) { - return ADDRESS; - } else { - throw new IllegalArgumentException("Unhandled variadic argument class: " + c); - } - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/User32.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/User32.java deleted file mode 100644 index 92a80196..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/User32.java +++ /dev/null @@ -1,96 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.user32; - -import java.lang.foreign.Addressable; -import java.lang.foreign.MemoryAddress; -import java.lang.invoke.MethodHandle; - -import static java.lang.foreign.ValueLayout.*; -public class User32 { - - /* package-private */ User32() {} - public static OfByte C_CHAR = Constants$root.C_CHAR$LAYOUT; - public static OfShort C_SHORT = Constants$root.C_SHORT$LAYOUT; - public static OfInt C_INT = Constants$root.C_LONG$LAYOUT; - public static OfInt C_LONG = Constants$root.C_LONG$LAYOUT; - public static OfLong C_LONG_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfFloat C_FLOAT = Constants$root.C_FLOAT$LAYOUT; - public static OfDouble C_DOUBLE = Constants$root.C_DOUBLE$LAYOUT; - public static OfAddress C_POINTER = Constants$root.C_POINTER$LAYOUT; - public static int WM_DEVICECHANGE() { - return (int)537L; - } - public static int DEVICE_NOTIFY_WINDOW_HANDLE() { - return (int)0L; - } - public static int DBT_DEVICEARRIVAL() { - return (int)32768L; - } - public static int DBT_DEVICEREMOVECOMPLETE() { - return (int)32772L; - } - public static int DBT_DEVTYP_DEVICEINTERFACE() { - return (int)5L; - } - public static MethodHandle GetMessageW$MH() { - return RuntimeHelper.requireNonNull(constants$0.GetMessageW$MH,"GetMessageW"); - } - public static int GetMessageW ( Addressable lpMsg, Addressable hWnd, int wMsgFilterMin, int wMsgFilterMax) { - var mh$ = GetMessageW$MH(); - try { - return (int)mh$.invokeExact(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle RegisterDeviceNotificationW$MH() { - return RuntimeHelper.requireNonNull(constants$0.RegisterDeviceNotificationW$MH,"RegisterDeviceNotificationW"); - } - public static MemoryAddress RegisterDeviceNotificationW ( Addressable hRecipient, Addressable NotificationFilter, int Flags) { - var mh$ = RegisterDeviceNotificationW$MH(); - try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(hRecipient, NotificationFilter, Flags); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle DefWindowProcW$MH() { - return RuntimeHelper.requireNonNull(constants$0.DefWindowProcW$MH,"DefWindowProcW"); - } - public static long DefWindowProcW ( Addressable hWnd, int Msg, long wParam, long lParam) { - var mh$ = DefWindowProcW$MH(); - try { - return (long)mh$.invokeExact(hWnd, Msg, wParam, lParam); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle RegisterClassExW$MH() { - return RuntimeHelper.requireNonNull(constants$0.RegisterClassExW$MH,"RegisterClassExW"); - } - public static short RegisterClassExW ( Addressable x0) { - var mh$ = RegisterClassExW$MH(); - try { - return (short)mh$.invokeExact(x0); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle CreateWindowExW$MH() { - return RuntimeHelper.requireNonNull(constants$0.CreateWindowExW$MH,"CreateWindowExW"); - } - public static MemoryAddress CreateWindowExW ( int dwExStyle, Addressable lpClassName, Addressable lpWindowName, int dwStyle, int X, int Y, int nWidth, int nHeight, Addressable hWndParent, Addressable hMenu, Addressable hInstance, Addressable lpParam) { - var mh$ = CreateWindowExW$MH(); - try { - return (java.lang.foreign.MemoryAddress)mh$.invokeExact(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MemoryAddress HWND_MESSAGE() { - return constants$0.HWND_MESSAGE$ADDR; - } -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/WNDCLASSEXW.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/WNDCLASSEXW.java deleted file mode 100644 index b09b0cfc..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/WNDCLASSEXW.java +++ /dev/null @@ -1,9 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.user32; - -public class WNDCLASSEXW extends tagWNDCLASSEXW { - -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/_DEV_BROADCAST_DEVICEINTERFACE_W.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/_DEV_BROADCAST_DEVICEINTERFACE_W.java deleted file mode 100644 index 3a3548fc..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/_DEV_BROADCAST_DEVICEINTERFACE_W.java +++ /dev/null @@ -1,87 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.user32; - -import java.lang.foreign.*; -import java.lang.invoke.VarHandle; -public class _DEV_BROADCAST_DEVICEINTERFACE_W { - - static final GroupLayout $struct$LAYOUT = MemoryLayout.structLayout( - Constants$root.C_LONG$LAYOUT.withName("dbcc_size"), - Constants$root.C_LONG$LAYOUT.withName("dbcc_devicetype"), - Constants$root.C_LONG$LAYOUT.withName("dbcc_reserved"), - MemoryLayout.structLayout( - Constants$root.C_LONG$LAYOUT.withName("Data1"), - Constants$root.C_SHORT$LAYOUT.withName("Data2"), - Constants$root.C_SHORT$LAYOUT.withName("Data3"), - MemoryLayout.sequenceLayout(8, Constants$root.C_CHAR$LAYOUT).withName("Data4") - ).withName("dbcc_classguid"), - MemoryLayout.sequenceLayout(1, Constants$root.C_SHORT$LAYOUT).withName("dbcc_name"), - MemoryLayout.paddingLayout(16) - ).withName("_DEV_BROADCAST_DEVICEINTERFACE_W"); - public static MemoryLayout $LAYOUT() { - return _DEV_BROADCAST_DEVICEINTERFACE_W.$struct$LAYOUT; - } - static final VarHandle dbcc_size$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("dbcc_size")); - public static VarHandle dbcc_size$VH() { - return _DEV_BROADCAST_DEVICEINTERFACE_W.dbcc_size$VH; - } - public static int dbcc_size$get(MemorySegment seg) { - return (int)_DEV_BROADCAST_DEVICEINTERFACE_W.dbcc_size$VH.get(seg); - } - public static void dbcc_size$set( MemorySegment seg, int x) { - _DEV_BROADCAST_DEVICEINTERFACE_W.dbcc_size$VH.set(seg, x); - } - public static int dbcc_size$get(MemorySegment seg, long index) { - return (int)_DEV_BROADCAST_DEVICEINTERFACE_W.dbcc_size$VH.get(seg.asSlice(index*sizeof())); - } - public static void dbcc_size$set(MemorySegment seg, long index, int x) { - _DEV_BROADCAST_DEVICEINTERFACE_W.dbcc_size$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle dbcc_devicetype$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("dbcc_devicetype")); - public static VarHandle dbcc_devicetype$VH() { - return _DEV_BROADCAST_DEVICEINTERFACE_W.dbcc_devicetype$VH; - } - public static int dbcc_devicetype$get(MemorySegment seg) { - return (int)_DEV_BROADCAST_DEVICEINTERFACE_W.dbcc_devicetype$VH.get(seg); - } - public static void dbcc_devicetype$set( MemorySegment seg, int x) { - _DEV_BROADCAST_DEVICEINTERFACE_W.dbcc_devicetype$VH.set(seg, x); - } - public static int dbcc_devicetype$get(MemorySegment seg, long index) { - return (int)_DEV_BROADCAST_DEVICEINTERFACE_W.dbcc_devicetype$VH.get(seg.asSlice(index*sizeof())); - } - public static void dbcc_devicetype$set(MemorySegment seg, long index, int x) { - _DEV_BROADCAST_DEVICEINTERFACE_W.dbcc_devicetype$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle dbcc_reserved$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("dbcc_reserved")); - public static VarHandle dbcc_reserved$VH() { - return _DEV_BROADCAST_DEVICEINTERFACE_W.dbcc_reserved$VH; - } - public static int dbcc_reserved$get(MemorySegment seg) { - return (int)_DEV_BROADCAST_DEVICEINTERFACE_W.dbcc_reserved$VH.get(seg); - } - public static void dbcc_reserved$set( MemorySegment seg, int x) { - _DEV_BROADCAST_DEVICEINTERFACE_W.dbcc_reserved$VH.set(seg, x); - } - public static int dbcc_reserved$get(MemorySegment seg, long index) { - return (int)_DEV_BROADCAST_DEVICEINTERFACE_W.dbcc_reserved$VH.get(seg.asSlice(index*sizeof())); - } - public static void dbcc_reserved$set(MemorySegment seg, long index, int x) { - _DEV_BROADCAST_DEVICEINTERFACE_W.dbcc_reserved$VH.set(seg.asSlice(index*sizeof()), x); - } - public static MemorySegment dbcc_classguid$slice(MemorySegment seg) { - return seg.asSlice(12, 16); - } - public static MemorySegment dbcc_name$slice(MemorySegment seg) { - return seg.asSlice(28, 2); - } - public static long sizeof() { return $LAYOUT().byteSize(); } - public static MemorySegment allocate(SegmentAllocator allocator) { return allocator.allocate($LAYOUT()); } - public static MemorySegment allocateArray(int len, SegmentAllocator allocator) { - return allocator.allocate(MemoryLayout.sequenceLayout(len, $LAYOUT())); - } - public static MemorySegment ofAddress(MemoryAddress addr, MemorySession session) { return RuntimeHelper.asArray(addr, $LAYOUT(), 1, session); } -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/_DEV_BROADCAST_HDR.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/_DEV_BROADCAST_HDR.java deleted file mode 100644 index 85d5840e..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/_DEV_BROADCAST_HDR.java +++ /dev/null @@ -1,73 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.user32; - -import java.lang.foreign.*; -import java.lang.invoke.VarHandle; -public class _DEV_BROADCAST_HDR { - - static final GroupLayout $struct$LAYOUT = MemoryLayout.structLayout( - Constants$root.C_LONG$LAYOUT.withName("dbch_size"), - Constants$root.C_LONG$LAYOUT.withName("dbch_devicetype"), - Constants$root.C_LONG$LAYOUT.withName("dbch_reserved") - ).withName("_DEV_BROADCAST_HDR"); - public static MemoryLayout $LAYOUT() { - return _DEV_BROADCAST_HDR.$struct$LAYOUT; - } - static final VarHandle dbch_size$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("dbch_size")); - public static VarHandle dbch_size$VH() { - return _DEV_BROADCAST_HDR.dbch_size$VH; - } - public static int dbch_size$get(MemorySegment seg) { - return (int)_DEV_BROADCAST_HDR.dbch_size$VH.get(seg); - } - public static void dbch_size$set( MemorySegment seg, int x) { - _DEV_BROADCAST_HDR.dbch_size$VH.set(seg, x); - } - public static int dbch_size$get(MemorySegment seg, long index) { - return (int)_DEV_BROADCAST_HDR.dbch_size$VH.get(seg.asSlice(index*sizeof())); - } - public static void dbch_size$set(MemorySegment seg, long index, int x) { - _DEV_BROADCAST_HDR.dbch_size$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle dbch_devicetype$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("dbch_devicetype")); - public static VarHandle dbch_devicetype$VH() { - return _DEV_BROADCAST_HDR.dbch_devicetype$VH; - } - public static int dbch_devicetype$get(MemorySegment seg) { - return (int)_DEV_BROADCAST_HDR.dbch_devicetype$VH.get(seg); - } - public static void dbch_devicetype$set( MemorySegment seg, int x) { - _DEV_BROADCAST_HDR.dbch_devicetype$VH.set(seg, x); - } - public static int dbch_devicetype$get(MemorySegment seg, long index) { - return (int)_DEV_BROADCAST_HDR.dbch_devicetype$VH.get(seg.asSlice(index*sizeof())); - } - public static void dbch_devicetype$set(MemorySegment seg, long index, int x) { - _DEV_BROADCAST_HDR.dbch_devicetype$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle dbch_reserved$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("dbch_reserved")); - public static VarHandle dbch_reserved$VH() { - return _DEV_BROADCAST_HDR.dbch_reserved$VH; - } - public static int dbch_reserved$get(MemorySegment seg) { - return (int)_DEV_BROADCAST_HDR.dbch_reserved$VH.get(seg); - } - public static void dbch_reserved$set( MemorySegment seg, int x) { - _DEV_BROADCAST_HDR.dbch_reserved$VH.set(seg, x); - } - public static int dbch_reserved$get(MemorySegment seg, long index) { - return (int)_DEV_BROADCAST_HDR.dbch_reserved$VH.get(seg.asSlice(index*sizeof())); - } - public static void dbch_reserved$set(MemorySegment seg, long index, int x) { - _DEV_BROADCAST_HDR.dbch_reserved$VH.set(seg.asSlice(index*sizeof()), x); - } - public static long sizeof() { return $LAYOUT().byteSize(); } - public static MemorySegment allocate(SegmentAllocator allocator) { return allocator.allocate($LAYOUT()); } - public static MemorySegment allocateArray(int len, SegmentAllocator allocator) { - return allocator.allocate(MemoryLayout.sequenceLayout(len, $LAYOUT())); - } - public static MemorySegment ofAddress(MemoryAddress addr, MemorySession session) { return RuntimeHelper.asArray(addr, $LAYOUT(), 1, session); } -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/constants$0.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/constants$0.java deleted file mode 100644 index f137ae31..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/constants$0.java +++ /dev/null @@ -1,67 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.user32; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryAddress; -import java.lang.invoke.MethodHandle; -class constants$0 { - - static final FunctionDescriptor GetMessageW$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_LONG$LAYOUT - ); - static final MethodHandle GetMessageW$MH = RuntimeHelper.downcallHandle( - "GetMessageW", - constants$0.GetMessageW$FUNC - ); - static final FunctionDescriptor RegisterDeviceNotificationW$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT - ); - static final MethodHandle RegisterDeviceNotificationW$MH = RuntimeHelper.downcallHandle( - "RegisterDeviceNotificationW", - constants$0.RegisterDeviceNotificationW$FUNC - ); - static final FunctionDescriptor DefWindowProcW$FUNC = FunctionDescriptor.of(Constants$root.C_LONG_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_LONG_LONG$LAYOUT, - Constants$root.C_LONG_LONG$LAYOUT - ); - static final MethodHandle DefWindowProcW$MH = RuntimeHelper.downcallHandle( - "DefWindowProcW", - constants$0.DefWindowProcW$FUNC - ); - static final FunctionDescriptor RegisterClassExW$FUNC = FunctionDescriptor.of(Constants$root.C_SHORT$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle RegisterClassExW$MH = RuntimeHelper.downcallHandle( - "RegisterClassExW", - constants$0.RegisterClassExW$FUNC - ); - static final FunctionDescriptor CreateWindowExW$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle CreateWindowExW$MH = RuntimeHelper.downcallHandle( - "CreateWindowExW", - constants$0.CreateWindowExW$FUNC - ); - static final MemoryAddress HWND_MESSAGE$ADDR = MemoryAddress.ofLong(-3L); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/tagMSG.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/tagMSG.java deleted file mode 100644 index 1fad57f8..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/tagMSG.java +++ /dev/null @@ -1,116 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.user32; - -import java.lang.foreign.*; -import java.lang.invoke.VarHandle; -public class tagMSG { - - static final GroupLayout $struct$LAYOUT = MemoryLayout.structLayout( - Constants$root.C_POINTER$LAYOUT.withName("hwnd"), - Constants$root.C_LONG$LAYOUT.withName("message"), - MemoryLayout.paddingLayout(32), - Constants$root.C_LONG_LONG$LAYOUT.withName("wParam"), - Constants$root.C_LONG_LONG$LAYOUT.withName("lParam"), - Constants$root.C_LONG$LAYOUT.withName("time"), - MemoryLayout.structLayout( - Constants$root.C_LONG$LAYOUT.withName("x"), - Constants$root.C_LONG$LAYOUT.withName("y") - ).withName("pt"), - MemoryLayout.paddingLayout(32) - ).withName("tagMSG"); - public static MemoryLayout $LAYOUT() { - return tagMSG.$struct$LAYOUT; - } - static final VarHandle hwnd$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("hwnd")); - public static VarHandle hwnd$VH() { - return tagMSG.hwnd$VH; - } - public static MemoryAddress hwnd$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)tagMSG.hwnd$VH.get(seg); - } - public static void hwnd$set( MemorySegment seg, MemoryAddress x) { - tagMSG.hwnd$VH.set(seg, x); - } - public static MemoryAddress hwnd$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)tagMSG.hwnd$VH.get(seg.asSlice(index*sizeof())); - } - public static void hwnd$set(MemorySegment seg, long index, MemoryAddress x) { - tagMSG.hwnd$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle message$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("message")); - public static VarHandle message$VH() { - return tagMSG.message$VH; - } - public static int message$get(MemorySegment seg) { - return (int)tagMSG.message$VH.get(seg); - } - public static void message$set( MemorySegment seg, int x) { - tagMSG.message$VH.set(seg, x); - } - public static int message$get(MemorySegment seg, long index) { - return (int)tagMSG.message$VH.get(seg.asSlice(index*sizeof())); - } - public static void message$set(MemorySegment seg, long index, int x) { - tagMSG.message$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle wParam$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("wParam")); - public static VarHandle wParam$VH() { - return tagMSG.wParam$VH; - } - public static long wParam$get(MemorySegment seg) { - return (long)tagMSG.wParam$VH.get(seg); - } - public static void wParam$set( MemorySegment seg, long x) { - tagMSG.wParam$VH.set(seg, x); - } - public static long wParam$get(MemorySegment seg, long index) { - return (long)tagMSG.wParam$VH.get(seg.asSlice(index*sizeof())); - } - public static void wParam$set(MemorySegment seg, long index, long x) { - tagMSG.wParam$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle lParam$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("lParam")); - public static VarHandle lParam$VH() { - return tagMSG.lParam$VH; - } - public static long lParam$get(MemorySegment seg) { - return (long)tagMSG.lParam$VH.get(seg); - } - public static void lParam$set( MemorySegment seg, long x) { - tagMSG.lParam$VH.set(seg, x); - } - public static long lParam$get(MemorySegment seg, long index) { - return (long)tagMSG.lParam$VH.get(seg.asSlice(index*sizeof())); - } - public static void lParam$set(MemorySegment seg, long index, long x) { - tagMSG.lParam$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle time$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("time")); - public static VarHandle time$VH() { - return tagMSG.time$VH; - } - public static int time$get(MemorySegment seg) { - return (int)tagMSG.time$VH.get(seg); - } - public static void time$set( MemorySegment seg, int x) { - tagMSG.time$VH.set(seg, x); - } - public static int time$get(MemorySegment seg, long index) { - return (int)tagMSG.time$VH.get(seg.asSlice(index*sizeof())); - } - public static void time$set(MemorySegment seg, long index, int x) { - tagMSG.time$VH.set(seg.asSlice(index*sizeof()), x); - } - public static MemorySegment pt$slice(MemorySegment seg) { - return seg.asSlice(36, 8); - } - public static long sizeof() { return $LAYOUT().byteSize(); } - public static MemorySegment allocate(SegmentAllocator allocator) { return allocator.allocate($LAYOUT()); } - public static MemorySegment allocateArray(int len, SegmentAllocator allocator) { - return allocator.allocate(MemoryLayout.sequenceLayout(len, $LAYOUT())); - } - public static MemorySegment ofAddress(MemoryAddress addr, MemorySession session) { return RuntimeHelper.asArray(addr, $LAYOUT(), 1, session); } -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/tagWNDCLASSEXW.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/tagWNDCLASSEXW.java deleted file mode 100644 index 9612b1a7..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/user32/tagWNDCLASSEXW.java +++ /dev/null @@ -1,226 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.user32; - -import java.lang.foreign.*; -import java.lang.invoke.VarHandle; -public class tagWNDCLASSEXW { - - static final GroupLayout $struct$LAYOUT = MemoryLayout.structLayout( - Constants$root.C_LONG$LAYOUT.withName("cbSize"), - Constants$root.C_LONG$LAYOUT.withName("style"), - Constants$root.C_POINTER$LAYOUT.withName("lpfnWndProc"), - Constants$root.C_LONG$LAYOUT.withName("cbClsExtra"), - Constants$root.C_LONG$LAYOUT.withName("cbWndExtra"), - Constants$root.C_POINTER$LAYOUT.withName("hInstance"), - Constants$root.C_POINTER$LAYOUT.withName("hIcon"), - Constants$root.C_POINTER$LAYOUT.withName("hCursor"), - Constants$root.C_POINTER$LAYOUT.withName("hbrBackground"), - Constants$root.C_POINTER$LAYOUT.withName("lpszMenuName"), - Constants$root.C_POINTER$LAYOUT.withName("lpszClassName"), - Constants$root.C_POINTER$LAYOUT.withName("hIconSm") - ).withName("tagWNDCLASSEXW"); - public static MemoryLayout $LAYOUT() { - return tagWNDCLASSEXW.$struct$LAYOUT; - } - static final VarHandle cbSize$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("cbSize")); - public static VarHandle cbSize$VH() { - return tagWNDCLASSEXW.cbSize$VH; - } - public static int cbSize$get(MemorySegment seg) { - return (int)tagWNDCLASSEXW.cbSize$VH.get(seg); - } - public static void cbSize$set( MemorySegment seg, int x) { - tagWNDCLASSEXW.cbSize$VH.set(seg, x); - } - public static int cbSize$get(MemorySegment seg, long index) { - return (int)tagWNDCLASSEXW.cbSize$VH.get(seg.asSlice(index*sizeof())); - } - public static void cbSize$set(MemorySegment seg, long index, int x) { - tagWNDCLASSEXW.cbSize$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle style$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("style")); - public static VarHandle style$VH() { - return tagWNDCLASSEXW.style$VH; - } - public static int style$get(MemorySegment seg) { - return (int)tagWNDCLASSEXW.style$VH.get(seg); - } - public static void style$set( MemorySegment seg, int x) { - tagWNDCLASSEXW.style$VH.set(seg, x); - } - public static int style$get(MemorySegment seg, long index) { - return (int)tagWNDCLASSEXW.style$VH.get(seg.asSlice(index*sizeof())); - } - public static void style$set(MemorySegment seg, long index, int x) { - tagWNDCLASSEXW.style$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle lpfnWndProc$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("lpfnWndProc")); - public static VarHandle lpfnWndProc$VH() { - return tagWNDCLASSEXW.lpfnWndProc$VH; - } - public static MemoryAddress lpfnWndProc$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)tagWNDCLASSEXW.lpfnWndProc$VH.get(seg); - } - public static void lpfnWndProc$set( MemorySegment seg, MemoryAddress x) { - tagWNDCLASSEXW.lpfnWndProc$VH.set(seg, x); - } - public static MemoryAddress lpfnWndProc$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)tagWNDCLASSEXW.lpfnWndProc$VH.get(seg.asSlice(index*sizeof())); - } - public static void lpfnWndProc$set(MemorySegment seg, long index, MemoryAddress x) { - tagWNDCLASSEXW.lpfnWndProc$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle cbClsExtra$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("cbClsExtra")); - public static VarHandle cbClsExtra$VH() { - return tagWNDCLASSEXW.cbClsExtra$VH; - } - public static int cbClsExtra$get(MemorySegment seg) { - return (int)tagWNDCLASSEXW.cbClsExtra$VH.get(seg); - } - public static void cbClsExtra$set( MemorySegment seg, int x) { - tagWNDCLASSEXW.cbClsExtra$VH.set(seg, x); - } - public static int cbClsExtra$get(MemorySegment seg, long index) { - return (int)tagWNDCLASSEXW.cbClsExtra$VH.get(seg.asSlice(index*sizeof())); - } - public static void cbClsExtra$set(MemorySegment seg, long index, int x) { - tagWNDCLASSEXW.cbClsExtra$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle cbWndExtra$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("cbWndExtra")); - public static VarHandle cbWndExtra$VH() { - return tagWNDCLASSEXW.cbWndExtra$VH; - } - public static int cbWndExtra$get(MemorySegment seg) { - return (int)tagWNDCLASSEXW.cbWndExtra$VH.get(seg); - } - public static void cbWndExtra$set( MemorySegment seg, int x) { - tagWNDCLASSEXW.cbWndExtra$VH.set(seg, x); - } - public static int cbWndExtra$get(MemorySegment seg, long index) { - return (int)tagWNDCLASSEXW.cbWndExtra$VH.get(seg.asSlice(index*sizeof())); - } - public static void cbWndExtra$set(MemorySegment seg, long index, int x) { - tagWNDCLASSEXW.cbWndExtra$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle hInstance$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("hInstance")); - public static VarHandle hInstance$VH() { - return tagWNDCLASSEXW.hInstance$VH; - } - public static MemoryAddress hInstance$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)tagWNDCLASSEXW.hInstance$VH.get(seg); - } - public static void hInstance$set( MemorySegment seg, MemoryAddress x) { - tagWNDCLASSEXW.hInstance$VH.set(seg, x); - } - public static MemoryAddress hInstance$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)tagWNDCLASSEXW.hInstance$VH.get(seg.asSlice(index*sizeof())); - } - public static void hInstance$set(MemorySegment seg, long index, MemoryAddress x) { - tagWNDCLASSEXW.hInstance$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle hIcon$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("hIcon")); - public static VarHandle hIcon$VH() { - return tagWNDCLASSEXW.hIcon$VH; - } - public static MemoryAddress hIcon$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)tagWNDCLASSEXW.hIcon$VH.get(seg); - } - public static void hIcon$set( MemorySegment seg, MemoryAddress x) { - tagWNDCLASSEXW.hIcon$VH.set(seg, x); - } - public static MemoryAddress hIcon$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)tagWNDCLASSEXW.hIcon$VH.get(seg.asSlice(index*sizeof())); - } - public static void hIcon$set(MemorySegment seg, long index, MemoryAddress x) { - tagWNDCLASSEXW.hIcon$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle hCursor$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("hCursor")); - public static VarHandle hCursor$VH() { - return tagWNDCLASSEXW.hCursor$VH; - } - public static MemoryAddress hCursor$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)tagWNDCLASSEXW.hCursor$VH.get(seg); - } - public static void hCursor$set( MemorySegment seg, MemoryAddress x) { - tagWNDCLASSEXW.hCursor$VH.set(seg, x); - } - public static MemoryAddress hCursor$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)tagWNDCLASSEXW.hCursor$VH.get(seg.asSlice(index*sizeof())); - } - public static void hCursor$set(MemorySegment seg, long index, MemoryAddress x) { - tagWNDCLASSEXW.hCursor$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle hbrBackground$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("hbrBackground")); - public static VarHandle hbrBackground$VH() { - return tagWNDCLASSEXW.hbrBackground$VH; - } - public static MemoryAddress hbrBackground$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)tagWNDCLASSEXW.hbrBackground$VH.get(seg); - } - public static void hbrBackground$set( MemorySegment seg, MemoryAddress x) { - tagWNDCLASSEXW.hbrBackground$VH.set(seg, x); - } - public static MemoryAddress hbrBackground$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)tagWNDCLASSEXW.hbrBackground$VH.get(seg.asSlice(index*sizeof())); - } - public static void hbrBackground$set(MemorySegment seg, long index, MemoryAddress x) { - tagWNDCLASSEXW.hbrBackground$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle lpszMenuName$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("lpszMenuName")); - public static VarHandle lpszMenuName$VH() { - return tagWNDCLASSEXW.lpszMenuName$VH; - } - public static MemoryAddress lpszMenuName$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)tagWNDCLASSEXW.lpszMenuName$VH.get(seg); - } - public static void lpszMenuName$set( MemorySegment seg, MemoryAddress x) { - tagWNDCLASSEXW.lpszMenuName$VH.set(seg, x); - } - public static MemoryAddress lpszMenuName$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)tagWNDCLASSEXW.lpszMenuName$VH.get(seg.asSlice(index*sizeof())); - } - public static void lpszMenuName$set(MemorySegment seg, long index, MemoryAddress x) { - tagWNDCLASSEXW.lpszMenuName$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle lpszClassName$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("lpszClassName")); - public static VarHandle lpszClassName$VH() { - return tagWNDCLASSEXW.lpszClassName$VH; - } - public static MemoryAddress lpszClassName$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)tagWNDCLASSEXW.lpszClassName$VH.get(seg); - } - public static void lpszClassName$set( MemorySegment seg, MemoryAddress x) { - tagWNDCLASSEXW.lpszClassName$VH.set(seg, x); - } - public static MemoryAddress lpszClassName$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)tagWNDCLASSEXW.lpszClassName$VH.get(seg.asSlice(index*sizeof())); - } - public static void lpszClassName$set(MemorySegment seg, long index, MemoryAddress x) { - tagWNDCLASSEXW.lpszClassName$VH.set(seg.asSlice(index*sizeof()), x); - } - static final VarHandle hIconSm$VH = $struct$LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("hIconSm")); - public static VarHandle hIconSm$VH() { - return tagWNDCLASSEXW.hIconSm$VH; - } - public static MemoryAddress hIconSm$get(MemorySegment seg) { - return (java.lang.foreign.MemoryAddress)tagWNDCLASSEXW.hIconSm$VH.get(seg); - } - public static void hIconSm$set( MemorySegment seg, MemoryAddress x) { - tagWNDCLASSEXW.hIconSm$VH.set(seg, x); - } - public static MemoryAddress hIconSm$get(MemorySegment seg, long index) { - return (java.lang.foreign.MemoryAddress)tagWNDCLASSEXW.hIconSm$VH.get(seg.asSlice(index*sizeof())); - } - public static void hIconSm$set(MemorySegment seg, long index, MemoryAddress x) { - tagWNDCLASSEXW.hIconSm$VH.set(seg.asSlice(index*sizeof()), x); - } - public static long sizeof() { return $LAYOUT().byteSize(); } - public static MemorySegment allocate(SegmentAllocator allocator) { return allocator.allocate($LAYOUT()); } - public static MemorySegment allocateArray(int len, SegmentAllocator allocator) { - return allocator.allocate(MemoryLayout.sequenceLayout(len, $LAYOUT())); - } - public static MemorySegment ofAddress(MemoryAddress addr, MemorySession session) { return RuntimeHelper.asArray(addr, $LAYOUT(), 1, session); } -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/winusb/Constants$root.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/winusb/Constants$root.java deleted file mode 100644 index 3568e394..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/winusb/Constants$root.java +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.winusb; - -import static java.lang.foreign.ValueLayout.*; -public class Constants$root { - - static final OfBoolean C_BOOL$LAYOUT = JAVA_BOOLEAN; - static final OfByte C_CHAR$LAYOUT = JAVA_BYTE; - static final OfShort C_SHORT$LAYOUT = JAVA_SHORT.withBitAlignment(16); - static final OfInt C_INT$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfInt C_LONG$LAYOUT = JAVA_INT.withBitAlignment(32); - static final OfLong C_LONG_LONG$LAYOUT = JAVA_LONG.withBitAlignment(64); - static final OfFloat C_FLOAT$LAYOUT = JAVA_FLOAT.withBitAlignment(32); - static final OfDouble C_DOUBLE$LAYOUT = JAVA_DOUBLE.withBitAlignment(64); - static final OfAddress C_POINTER$LAYOUT = ADDRESS.withBitAlignment(64); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/winusb/RuntimeHelper.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/winusb/RuntimeHelper.java deleted file mode 100644 index f4a55475..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/winusb/RuntimeHelper.java +++ /dev/null @@ -1,216 +0,0 @@ -package net.codecrete.usb.windows.gen.winusb; -// Generated by jextract - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -import static java.lang.foreign.ValueLayout.*; - -final class RuntimeHelper { - - private RuntimeHelper() {} - private final static Linker LINKER = Linker.nativeLinker(); - private final static ClassLoader LOADER = RuntimeHelper.class.getClassLoader(); - private final static MethodHandles.Lookup MH_LOOKUP = MethodHandles.lookup(); - private final static SymbolLookup SYMBOL_LOOKUP; - - final static SegmentAllocator CONSTANT_ALLOCATOR = - (size, align) -> MemorySegment.allocateNative(size, align, MemorySession.openImplicit()); - - static { - System.loadLibrary("Winusb"); - SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); - SYMBOL_LOOKUP = name -> loaderLookup.lookup(name).or(() -> LINKER.defaultLookup().lookup(name)); - } - - static T requireNonNull(T obj, String symbolName) { - if (obj == null) { - throw new UnsatisfiedLinkError("unresolved symbol: " + symbolName); - } - return obj; - } - - private final static SegmentAllocator THROWING_ALLOCATOR = (x, y) -> { throw new AssertionError("should not reach here"); }; - - static final MemorySegment lookupGlobalVariable(String name, MemoryLayout layout) { - return SYMBOL_LOOKUP.lookup(name).map(symbol -> MemorySegment.ofAddress(symbol.address(), layout.byteSize(), MemorySession.openShared())).orElse(null); - } - - static final MethodHandle downcallHandle(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> LINKER.downcallHandle(addr, fdesc)). - orElse(null); - } - - static final MethodHandle downcallHandle(FunctionDescriptor fdesc) { - return LINKER.downcallHandle(fdesc); - } - - static final MethodHandle downcallHandleVariadic(String name, FunctionDescriptor fdesc) { - return SYMBOL_LOOKUP.lookup(name). - map(addr -> VarargsInvoker.make(addr, fdesc)). - orElse(null); - } - - static final MemorySegment upcallStub(Class fi, Z z, FunctionDescriptor fdesc, MemorySession session) { - try { - MethodHandle handle = MH_LOOKUP.findVirtual(fi, "apply", Linker.upcallType(fdesc)); - handle = handle.bindTo(z); - return LINKER.upcallStub(handle, fdesc, session); - } catch (Throwable ex) { - throw new AssertionError(ex); - } - } - - static MemorySegment asArray(MemoryAddress addr, MemoryLayout layout, int numElements, MemorySession session) { - return MemorySegment.ofAddress(addr, numElements * layout.byteSize(), session); - } - - // Internals only below this point - - private static class VarargsInvoker { - private static final MethodHandle INVOKE_MH; - private final MemorySegment symbol; - private final FunctionDescriptor function; - - private VarargsInvoker(MemorySegment symbol, FunctionDescriptor function) { - this.symbol = symbol; - this.function = function; - } - - static { - try { - INVOKE_MH = MethodHandles.lookup().findVirtual(VarargsInvoker.class, "invoke", MethodType.methodType(Object.class, SegmentAllocator.class, Object[].class)); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - static MethodHandle make(MemorySegment symbol, FunctionDescriptor function) { - VarargsInvoker invoker = new VarargsInvoker(symbol, function); - MethodHandle handle = INVOKE_MH.bindTo(invoker).asCollector(Object[].class, function.argumentLayouts().size() + 1); - MethodType mtype = MethodType.methodType(function.returnLayout().isPresent() ? carrier(function.returnLayout().get(), true) : void.class); - for (MemoryLayout layout : function.argumentLayouts()) { - mtype = mtype.appendParameterTypes(carrier(layout, false)); - } - mtype = mtype.appendParameterTypes(Object[].class); - if (mtype.returnType().equals(MemorySegment.class)) { - mtype = mtype.insertParameterTypes(0, SegmentAllocator.class); - } else { - handle = MethodHandles.insertArguments(handle, 0, THROWING_ALLOCATOR); - } - return handle.asType(mtype); - } - - static Class carrier(MemoryLayout layout, boolean ret) { - if (layout instanceof ValueLayout valueLayout) { - return (ret || valueLayout.carrier() != MemoryAddress.class) ? - valueLayout.carrier() : Addressable.class; - } else if (layout instanceof GroupLayout) { - return MemorySegment.class; - } else { - throw new AssertionError("Cannot get here!"); - } - } - - private Object invoke(SegmentAllocator allocator, Object[] args) throws Throwable { - // one trailing Object[] - int nNamedArgs = function.argumentLayouts().size(); - assert(args.length == nNamedArgs + 1); - // The last argument is the array of vararg collector - Object[] unnamedArgs = (Object[]) args[args.length - 1]; - - int argsCount = nNamedArgs + unnamedArgs.length; - Class[] argTypes = new Class[argsCount]; - MemoryLayout[] argLayouts = new MemoryLayout[nNamedArgs + unnamedArgs.length]; - - int pos = 0; - for (pos = 0; pos < nNamedArgs; pos++) { - argLayouts[pos] = function.argumentLayouts().get(pos); - } - - assert pos == nNamedArgs; - for (Object o: unnamedArgs) { - argLayouts[pos] = variadicLayout(normalize(o.getClass())); - pos++; - } - assert pos == argsCount; - - FunctionDescriptor f = (function.returnLayout().isEmpty()) ? - FunctionDescriptor.ofVoid(argLayouts) : - FunctionDescriptor.of(function.returnLayout().get(), argLayouts); - MethodHandle mh = LINKER.downcallHandle(symbol, f); - if (mh.type().returnType() == MemorySegment.class) { - mh = mh.bindTo(allocator); - } - // flatten argument list so that it can be passed to an asSpreader MH - Object[] allArgs = new Object[nNamedArgs + unnamedArgs.length]; - System.arraycopy(args, 0, allArgs, 0, nNamedArgs); - System.arraycopy(unnamedArgs, 0, allArgs, nNamedArgs, unnamedArgs.length); - - return mh.asSpreader(Object[].class, argsCount).invoke(allArgs); - } - - private static Class unboxIfNeeded(Class clazz) { - if (clazz == Boolean.class) { - return boolean.class; - } else if (clazz == Void.class) { - return void.class; - } else if (clazz == Byte.class) { - return byte.class; - } else if (clazz == Character.class) { - return char.class; - } else if (clazz == Short.class) { - return short.class; - } else if (clazz == Integer.class) { - return int.class; - } else if (clazz == Long.class) { - return long.class; - } else if (clazz == Float.class) { - return float.class; - } else if (clazz == Double.class) { - return double.class; - } else { - return clazz; - } - } - - private Class promote(Class c) { - if (c == byte.class || c == char.class || c == short.class || c == int.class) { - return long.class; - } else if (c == float.class) { - return double.class; - } else { - return c; - } - } - - private Class normalize(Class c) { - c = unboxIfNeeded(c); - if (c.isPrimitive()) { - return promote(c); - } - if (MemoryAddress.class.isAssignableFrom(c)) { - return MemoryAddress.class; - } - if (MemorySegment.class.isAssignableFrom(c)) { - return MemorySegment.class; - } - throw new IllegalArgumentException("Invalid type for ABI: " + c.getTypeName()); - } - - private MemoryLayout variadicLayout(Class c) { - if (c == long.class) { - return JAVA_LONG; - } else if (c == double.class) { - return JAVA_DOUBLE; - } else if (MemoryAddress.class.isAssignableFrom(c)) { - return ADDRESS; - } else { - throw new IllegalArgumentException("Unhandled variadic argument class: " + c); - } - } - } -} diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/winusb/WinUSB.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/winusb/WinUSB.java deleted file mode 100644 index f3cc4111..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/winusb/WinUSB.java +++ /dev/null @@ -1,100 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.winusb; - -import java.lang.foreign.Addressable; -import java.lang.foreign.MemorySegment; -import java.lang.invoke.MethodHandle; - -import static java.lang.foreign.ValueLayout.*; -public class WinUSB { - - /* package-private */ WinUSB() {} - public static OfByte C_CHAR = Constants$root.C_CHAR$LAYOUT; - public static OfShort C_SHORT = Constants$root.C_SHORT$LAYOUT; - public static OfInt C_INT = Constants$root.C_LONG$LAYOUT; - public static OfInt C_LONG = Constants$root.C_LONG$LAYOUT; - public static OfLong C_LONG_LONG = Constants$root.C_LONG_LONG$LAYOUT; - public static OfFloat C_FLOAT = Constants$root.C_FLOAT$LAYOUT; - public static OfDouble C_DOUBLE = Constants$root.C_DOUBLE$LAYOUT; - public static OfAddress C_POINTER = Constants$root.C_POINTER$LAYOUT; - public static MethodHandle WinUsb_Initialize$MH() { - return RuntimeHelper.requireNonNull(constants$0.WinUsb_Initialize$MH,"WinUsb_Initialize"); - } - public static int WinUsb_Initialize ( Addressable DeviceHandle, Addressable InterfaceHandle) { - var mh$ = WinUsb_Initialize$MH(); - try { - return (int)mh$.invokeExact(DeviceHandle, InterfaceHandle); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle WinUsb_Free$MH() { - return RuntimeHelper.requireNonNull(constants$0.WinUsb_Free$MH,"WinUsb_Free"); - } - public static int WinUsb_Free ( Addressable InterfaceHandle) { - var mh$ = WinUsb_Free$MH(); - try { - return (int)mh$.invokeExact(InterfaceHandle); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle WinUsb_GetAssociatedInterface$MH() { - return RuntimeHelper.requireNonNull(constants$0.WinUsb_GetAssociatedInterface$MH,"WinUsb_GetAssociatedInterface"); - } - public static int WinUsb_GetAssociatedInterface ( Addressable InterfaceHandle, byte AssociatedInterfaceIndex, Addressable AssociatedInterfaceHandle) { - var mh$ = WinUsb_GetAssociatedInterface$MH(); - try { - return (int)mh$.invokeExact(InterfaceHandle, AssociatedInterfaceIndex, AssociatedInterfaceHandle); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle WinUsb_GetDescriptor$MH() { - return RuntimeHelper.requireNonNull(constants$0.WinUsb_GetDescriptor$MH,"WinUsb_GetDescriptor"); - } - public static int WinUsb_GetDescriptor ( Addressable InterfaceHandle, byte DescriptorType, byte Index, short LanguageID, Addressable Buffer, int BufferLength, Addressable LengthTransferred) { - var mh$ = WinUsb_GetDescriptor$MH(); - try { - return (int)mh$.invokeExact(InterfaceHandle, DescriptorType, Index, LanguageID, Buffer, BufferLength, LengthTransferred); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle WinUsb_ReadPipe$MH() { - return RuntimeHelper.requireNonNull(constants$0.WinUsb_ReadPipe$MH,"WinUsb_ReadPipe"); - } - public static int WinUsb_ReadPipe ( Addressable InterfaceHandle, byte PipeID, Addressable Buffer, int BufferLength, Addressable LengthTransferred, Addressable Overlapped) { - var mh$ = WinUsb_ReadPipe$MH(); - try { - return (int)mh$.invokeExact(InterfaceHandle, PipeID, Buffer, BufferLength, LengthTransferred, Overlapped); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle WinUsb_WritePipe$MH() { - return RuntimeHelper.requireNonNull(constants$0.WinUsb_WritePipe$MH,"WinUsb_WritePipe"); - } - public static int WinUsb_WritePipe ( Addressable InterfaceHandle, byte PipeID, Addressable Buffer, int BufferLength, Addressable LengthTransferred, Addressable Overlapped) { - var mh$ = WinUsb_WritePipe$MH(); - try { - return (int)mh$.invokeExact(InterfaceHandle, PipeID, Buffer, BufferLength, LengthTransferred, Overlapped); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static MethodHandle WinUsb_ControlTransfer$MH() { - return RuntimeHelper.requireNonNull(constants$1.WinUsb_ControlTransfer$MH,"WinUsb_ControlTransfer"); - } - public static int WinUsb_ControlTransfer ( Addressable InterfaceHandle, MemorySegment SetupPacket, Addressable Buffer, int BufferLength, Addressable LengthTransferred, Addressable Overlapped) { - var mh$ = WinUsb_ControlTransfer$MH(); - try { - return (int)mh$.invokeExact(InterfaceHandle, SetupPacket, Buffer, BufferLength, LengthTransferred, Overlapped); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/winusb/constants$0.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/winusb/constants$0.java deleted file mode 100644 index 66a14d35..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/winusb/constants$0.java +++ /dev/null @@ -1,72 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.winusb; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.invoke.MethodHandle; -class constants$0 { - - static final FunctionDescriptor WinUsb_Initialize$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle WinUsb_Initialize$MH = RuntimeHelper.downcallHandle( - "WinUsb_Initialize", - constants$0.WinUsb_Initialize$FUNC - ); - static final FunctionDescriptor WinUsb_Free$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle WinUsb_Free$MH = RuntimeHelper.downcallHandle( - "WinUsb_Free", - constants$0.WinUsb_Free$FUNC - ); - static final FunctionDescriptor WinUsb_GetAssociatedInterface$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle WinUsb_GetAssociatedInterface$MH = RuntimeHelper.downcallHandle( - "WinUsb_GetAssociatedInterface", - constants$0.WinUsb_GetAssociatedInterface$FUNC - ); - static final FunctionDescriptor WinUsb_GetDescriptor$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_SHORT$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle WinUsb_GetDescriptor$MH = RuntimeHelper.downcallHandle( - "WinUsb_GetDescriptor", - constants$0.WinUsb_GetDescriptor$FUNC - ); - static final FunctionDescriptor WinUsb_ReadPipe$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle WinUsb_ReadPipe$MH = RuntimeHelper.downcallHandle( - "WinUsb_ReadPipe", - constants$0.WinUsb_ReadPipe$FUNC - ); - static final FunctionDescriptor WinUsb_WritePipe$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_CHAR$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle WinUsb_WritePipe$MH = RuntimeHelper.downcallHandle( - "WinUsb_WritePipe", - constants$0.WinUsb_WritePipe$FUNC - ); -} - - diff --git a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/winusb/constants$1.java b/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/winusb/constants$1.java deleted file mode 100644 index 123b8fad..00000000 --- a/java-does-usb/src/main/java/net/codecrete/usb/windows/gen/winusb/constants$1.java +++ /dev/null @@ -1,30 +0,0 @@ -// Generated by jextract - -package net.codecrete.usb.windows.gen.winusb; - -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryLayout; -import java.lang.invoke.MethodHandle; -class constants$1 { - - static final FunctionDescriptor WinUsb_ControlTransfer$FUNC = FunctionDescriptor.of(Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - MemoryLayout.structLayout( - Constants$root.C_CHAR$LAYOUT.withName("RequestType"), - Constants$root.C_CHAR$LAYOUT.withName("Request"), - Constants$root.C_SHORT$LAYOUT.withName("Value"), - Constants$root.C_SHORT$LAYOUT.withName("Index"), - Constants$root.C_SHORT$LAYOUT.withName("Length") - ).withName("_WINUSB_SETUP_PACKET"), - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_LONG$LAYOUT, - Constants$root.C_POINTER$LAYOUT, - Constants$root.C_POINTER$LAYOUT - ); - static final MethodHandle WinUsb_ControlTransfer$MH = RuntimeHelper.downcallHandle( - "WinUsb_ControlTransfer", - constants$1.WinUsb_ControlTransfer$FUNC - ); -} - - diff --git a/java-does-usb/src/test/java/net/codecrete/usb/AlternateInterfaceTest.java b/java-does-usb/src/test/java/net/codecrete/usb/AlternateInterfaceTest.java new file mode 100644 index 00000000..11f53b2a --- /dev/null +++ b/java-does-usb/src/test/java/net/codecrete/usb/AlternateInterfaceTest.java @@ -0,0 +1,70 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Unit test for changing alternate interface setting +// + +package net.codecrete.usb; + +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class AlternateInterfaceTest extends TestDeviceBase { + + @BeforeAll + static void precondition() { + Assumptions.assumeTrue(isLoopbackDevice(), + "Alternate interface only supported by loopback test device"); + } + + @Test + void selectAlternateIntf_succeeds() { + + testDevice.selectAlternateSetting(config.interfaceNumber(), 1); + + var altIntf = testDevice.getInterface(config.interfaceNumber()).getCurrentAlternate(); + assertNotNull(altIntf); + assertEquals(2, altIntf.getEndpoints().size()); + assertEquals(0xff, altIntf.getClassCode()); + + testDevice.selectAlternateSetting(config.interfaceNumber(), 0); + } + + @Test + void selectInvalidAlternateIntf_fails() { + assertThrows(UsbException.class, () -> testDevice.selectAlternateSetting(1, 0)); + + var interfaceNumber = config.interfaceNumber(); + assertThrows(UsbException.class, () -> testDevice.selectAlternateSetting(interfaceNumber, 2)); + } + + @Test + void transferOnValidEndpoint_succeeds() { + testDevice.selectAlternateSetting(config.interfaceNumber(), 1); + + var sampleData = generateRandomBytes(12, 293872394); + testDevice.transferOut(config.endpointLoopbackOut(), sampleData); + var received = testDevice.transferIn(config.endpointLoopbackIn()); + assertArrayEquals(sampleData, received); + } + + @Test + void transferOnInvalidEndpoint_fails() { + testDevice.selectAlternateSetting(config.interfaceNumber(), 1); + + var endpointOut = config.endpointEchoOut(); + assertThrows(UsbException.class, () -> testDevice.transferOut(endpointOut, new byte[]{1, 2, 3})); + + var endpointIn = config.endpointEchoIn(); + assertThrows(UsbException.class, () -> testDevice.transferIn(endpointIn)); + } +} diff --git a/java-does-usb/src/test/java/net/codecrete/usb/BulkTransferTest.java b/java-does-usb/src/test/java/net/codecrete/usb/BulkTransferTest.java index db0aafad..1323b380 100644 --- a/java-does-usb/src/test/java/net/codecrete/usb/BulkTransferTest.java +++ b/java-does-usb/src/test/java/net/codecrete/usb/BulkTransferTest.java @@ -16,14 +16,16 @@ import java.util.concurrent.CompletableFuture; import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; -public class BulkTransferTest extends TestDeviceBase { +class BulkTransferTest extends TestDeviceBase { @Test void smallTransfer_succeeds() { - byte[] sampleData = generateRandomBytes(12, 293872394); + var sampleData = generateRandomBytes(12, 293872394); writeBytes(sampleData); - byte[] data = readBytes(sampleData.length); + var data = readBytes(sampleData.length); assertArrayEquals(sampleData, data); } @@ -31,16 +33,29 @@ void smallTransfer_succeeds() { void mediumTransfer_succeeds() { // This synchronous approach should work as the test device // has an internal buffer of about 500 bytes. - byte[] sampleData = generateRandomBytes(140, 97333894); + var sampleData = generateRandomBytes(140, 97333894); writeBytes(sampleData); - byte[] data = readBytes(sampleData.length); + var data = readBytes(sampleData.length); assertArrayEquals(sampleData, data); } + @Test + void transferWithZLP_succeeds() { + var inEndpoint = testDevice.getEndpoint(UsbDirection.IN, config.endpointLoopbackIn()); + var sampleData = generateRandomBytes(inEndpoint.getPacketSize(), 97333894); + testDevice.transferOut(config.endpointLoopbackOut(), sampleData); + testDevice.transferOut(config.endpointLoopbackOut(), new byte[0]); + var data = testDevice.transferIn(config.endpointLoopbackIn()); + assertArrayEquals(sampleData, data); + data = testDevice.transferIn(config.endpointLoopbackIn()); + assertNotNull(data); + assertEquals(0, data.length); + } + @Test void largeTransfer_succeeds() throws Throwable { - final int numBytes = 230763; - byte[] sampleData = generateRandomBytes(numBytes, 3829007493L); + final var numBytes = 230763; + var sampleData = generateRandomBytes(numBytes, 3829007493L); var writer = CompletableFuture.runAsync(() -> writeBytes(sampleData)); var reader = CompletableFuture.supplyAsync(() -> readBytes(numBytes)); CompletableFuture.allOf(writer, reader).join(); @@ -50,19 +65,20 @@ void largeTransfer_succeeds() throws Throwable { } static void writeBytes(byte[] data) { - final int chunkSize = 100; - int numBytes = 0; + final var chunkSize = 100; + var numBytes = 0; while (numBytes < data.length) { - int size = Math.min(chunkSize, data.length - numBytes); - testDevice.transferOut(LOOPBACK_EP_OUT, Arrays.copyOfRange(data, numBytes, numBytes + size)); + var size = Math.min(chunkSize, data.length - numBytes); + testDevice.transferOut(config.endpointLoopbackOut(), Arrays.copyOfRange(data, numBytes, numBytes + size)); numBytes += size; } } + static byte[] readBytes(int numBytes) { var buffer = new ByteArrayOutputStream(); - int bytesRead = 0; + var bytesRead = 0; while (bytesRead < numBytes) { - byte[] data = testDevice.transferIn(LOOPBACK_EP_IN, LOOPBACK_MAX_PACKET_SIZE); + var data = testDevice.transferIn(config.endpointLoopbackIn()); buffer.writeBytes(data); bytesRead += data.length; } diff --git a/java-does-usb/src/test/java/net/codecrete/usb/ControlTransferTest.java b/java-does-usb/src/test/java/net/codecrete/usb/ControlTransferTest.java index b0d2840f..8a653679 100644 --- a/java-does-usb/src/test/java/net/codecrete/usb/ControlTransferTest.java +++ b/java-does-usb/src/test/java/net/codecrete/usb/ControlTransferTest.java @@ -12,21 +12,24 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * Tests control transfers */ -public class ControlTransferTest extends TestDeviceBase { +class ControlTransferTest extends TestDeviceBase { @Test void storeValue_succeeds() { - testDevice.controlTransferOut(new USBControlTransfer(USBRequestType.VENDOR, USBRecipient.INTERFACE, (byte) 0x01, (short) 10730, (short) interfaceNumber), null); + var setup = new UsbControlTransfer(UsbRequestType.VENDOR, UsbRecipient.INTERFACE, (byte) 0x01, (short) 10730, (short) config.interfaceNumber()); + assertDoesNotThrow(() -> testDevice.controlTransferOut(setup, null)); } @Test void retrieveValue_isSameAsStored() { - testDevice.controlTransferOut(new USBControlTransfer(USBRequestType.VENDOR, USBRecipient.INTERFACE, (byte) 0x01, (short) 0x9a41, (short) interfaceNumber), null); - var valueBytes = testDevice.controlTransferIn(new USBControlTransfer(USBRequestType.VENDOR, USBRecipient.INTERFACE, (byte) 0x03, (short) 0, (short) interfaceNumber), 4); + testDevice.controlTransferOut(new UsbControlTransfer(UsbRequestType.VENDOR, UsbRecipient.INTERFACE, (byte) 0x01, (short) 0x9a41, (short) config.interfaceNumber()), null); + var valueBytes = testDevice.controlTransferIn(new UsbControlTransfer(UsbRequestType.VENDOR, UsbRecipient.INTERFACE, (byte) 0x03, (short) 0, (short) config.interfaceNumber()), 4); var expectedBytes = new byte[]{(byte) 0x41, (byte) 0x9a, (byte) 0x00, (byte) 0x00}; assertArrayEquals(expectedBytes, valueBytes); } @@ -34,8 +37,21 @@ void retrieveValue_isSameAsStored() { @Test void storeValueInDataStage_canBeRetrieved() { var sentValue = new byte[]{(byte) 0x83, (byte) 0x03, (byte) 0xda, (byte) 0x3e}; - testDevice.controlTransferOut(new USBControlTransfer(USBRequestType.VENDOR, USBRecipient.INTERFACE, (byte) 0x02, (short) 0, (short) interfaceNumber), sentValue); - var retrievedValue = testDevice.controlTransferIn(new USBControlTransfer(USBRequestType.VENDOR, USBRecipient.INTERFACE, (byte) 0x03, (short) 0, (short) interfaceNumber), 4); + testDevice.controlTransferOut(new UsbControlTransfer(UsbRequestType.VENDOR, UsbRecipient.INTERFACE, (byte) 0x02, (short) 0, (short) config.interfaceNumber()), sentValue); + var retrievedValue = testDevice.controlTransferIn(new UsbControlTransfer(UsbRequestType.VENDOR, UsbRecipient.INTERFACE, (byte) 0x03, (short) 0, (short) config.interfaceNumber()), 4); assertArrayEquals(sentValue, retrievedValue); } + + @Test + void interfaceNumber_canBeRetrieved() { + var response = testDevice.controlTransferIn(new UsbControlTransfer(UsbRequestType.VENDOR, UsbRecipient.INTERFACE, (byte) 0x05, (short) 0, (short) config.interfaceNumber()), 1); + assertEquals(config.interfaceNumber(), response[0] & 0xff); + + if (isCompositeDevce()) { + testDevice.claimInterface(2); + response = testDevice.controlTransferIn(new UsbControlTransfer(UsbRequestType.VENDOR, UsbRecipient.INTERFACE, (byte) 0x05, (short) 0, (short) 2), 1); + assertEquals(2, response[0] & 0xff); + testDevice.releaseInterface(2); + } + } } diff --git a/java-does-usb/src/test/java/net/codecrete/usb/DescriptionTest.java b/java-does-usb/src/test/java/net/codecrete/usb/DescriptionTest.java index d30433b3..42097c2f 100644 --- a/java-does-usb/src/test/java/net/codecrete/usb/DescriptionTest.java +++ b/java-does-usb/src/test/java/net/codecrete/usb/DescriptionTest.java @@ -4,102 +4,160 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Unit tests for USB device enumeration +// Unit tests for checking descriptive device information // package net.codecrete.usb; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Tests the interface, alternate settings and endpoint descriptions. */ -public class DescriptionTest extends TestDeviceBase { +class DescriptionTest extends TestDeviceBase { @Test void deviceInfo_isCorrect() { - assertEquals("JavaDoesUSB", testDevice.manufacturer()); - assertEquals("Loopback", testDevice.product()); - assertEquals(12, testDevice.serialNumber().length()); + assertEquals("JavaDoesUSB", testDevice.getManufacturer()); + assertEquals(isLoopbackDevice() ? "Loopback" : "Composite", testDevice.getProduct()); + assertEquals(12, testDevice.getSerialNumber().length()); - if (interfaceNumber == 2) { - // composite device - assertEquals(0xef, testDevice.classCode()); - assertEquals(0x02, testDevice.subclassCode()); - assertEquals(0x01, testDevice.protocolCode()); - } else { + if (isLoopbackDevice()) { // simple device - assertEquals(0xff, testDevice.classCode()); - assertEquals(0x00, testDevice.subclassCode()); - assertEquals(0x00, testDevice.protocolCode()); + assertEquals(0xff, testDevice.getClassCode()); + assertEquals(0x00, testDevice.getSubclassCode()); + assertEquals(0x00, testDevice.getProtocolCode()); + } else { + // composite device + assertEquals(0xef, testDevice.getClassCode()); + assertEquals(0x02, testDevice.getSubclassCode()); + assertEquals(0x01, testDevice.getProtocolCode()); } - boolean isComposite = pid == TestDeviceBase.PID_COMPOSITE; + var isComposite = isCompositeDevce(); - assertEquals(2, testDevice.usbVersion().major()); - assertEquals(isComposite ? 1 : 0, testDevice.usbVersion().minor()); - assertEquals(0, testDevice.usbVersion().subminor()); + assertEquals(2, testDevice.getUsbVersion().getMajor()); + assertEquals(isComposite ? 1 : 0, testDevice.getUsbVersion().getMinor()); + assertEquals(0, testDevice.getUsbVersion().getSubminor()); - assertEquals(0, testDevice.deviceVersion().major()); - assertEquals(isComposite ? 3 : 7, testDevice.deviceVersion().minor()); - assertEquals(isComposite ? 4 : 1, testDevice.deviceVersion().subminor()); + assertEquals(0, testDevice.getDeviceVersion().getMajor()); + assertEquals(isComposite ? 3 : 7, testDevice.getDeviceVersion().getMinor()); + assertEquals(isComposite ? 6 : 4, testDevice.getDeviceVersion().getSubminor()); } @Test void interfaceDescriptor_isCorrect() { - assertNotNull(testDevice.interfaces()); - assertEquals(interfaceNumber + 1, testDevice.interfaces().size()); + assertNotNull(testDevice.getInterfaces()); + assertEquals(config.interfaceNumber() + 1, testDevice.getInterfaces().size()); - var intf = testDevice.interfaces().get(interfaceNumber); - assertEquals(interfaceNumber, intf.number()); - assertNotNull(intf.alternate()); + var intf = testDevice.getInterfaces().get(config.interfaceNumber()); + assertEquals(config.interfaceNumber(), intf.getNumber()); + assertNotNull(intf.getCurrentAlternate()); assertTrue(intf.isClaimed()); } + @Test + void invalidInterfaceNumber_shouldThrow() { + assertThrows(UsbException.class, () -> testDevice.getInterface(4)); + } + @Test void alternateInterfaceDescriptor_isCorrect() { - var intf = testDevice.interfaces().get(interfaceNumber); - var altIntf = intf.alternate(); - assertNotNull(intf.alternates()); - assertEquals(1, intf.alternates().size()); - assertSame(intf.alternates().get(0), altIntf); - assertEquals(0, altIntf.number()); - - assertEquals(0xff, altIntf.classCode()); - assertEquals(0x00, altIntf.subclassCode()); - assertEquals(0x00, altIntf.protocolCode()); + var intf = testDevice.getInterfaces().get(config.interfaceNumber()); + var altIntf = intf.getCurrentAlternate(); + assertNotNull(intf.getAlternates()); + assertEquals(isLoopbackDevice() ? 2 : 1, intf.getAlternates().size()); + assertSame(intf.getAlternates().getFirst(), altIntf); + assertEquals(0, altIntf.getNumber()); + + assertEquals(0xff, altIntf.getClassCode()); + assertEquals(0x00, altIntf.getSubclassCode()); + assertEquals(0x00, altIntf.getProtocolCode()); + + if (isLoopbackDevice()) { + altIntf = intf.getAlternates().get(1); + assertEquals(1, altIntf.getNumber()); + + assertEquals(0xff, altIntf.getClassCode()); + assertEquals(0x00, altIntf.getSubclassCode()); + assertEquals(0x00, altIntf.getProtocolCode()); + } } + @SuppressWarnings("java:S5961") @Test void endpointDescriptors_areCorrect() { - var altIntf = testDevice.interfaces().get(interfaceNumber).alternate(); - assertNotNull(altIntf.endpoints()); - assertEquals(4, altIntf.endpoints().size()); - - var endpoint = altIntf.endpoints().get(0); - assertEquals(1, endpoint.number()); - assertEquals(USBDirection.OUT, endpoint.direction()); - assertEquals(USBTransferType.BULK, endpoint.transferType()); - assertEquals(64, endpoint.packetSize()); - - endpoint = altIntf.endpoints().get(1); - assertEquals(2, endpoint.number()); - assertEquals(USBDirection.IN, endpoint.direction()); - assertEquals(USBTransferType.BULK, endpoint.transferType()); - assertEquals(64, endpoint.packetSize()); - - endpoint = altIntf.endpoints().get(2); - assertEquals(3, endpoint.number()); - assertEquals(USBDirection.OUT, endpoint.direction()); - assertEquals(USBTransferType.INTERRUPT, endpoint.transferType()); - assertEquals(16, endpoint.packetSize()); - - endpoint = altIntf.endpoints().get(3); - assertEquals(3, endpoint.number()); - assertEquals(USBDirection.IN, endpoint.direction()); - assertEquals(USBTransferType.INTERRUPT, endpoint.transferType()); - assertEquals(16, endpoint.packetSize()); + var altIntf = testDevice.getInterfaces().get(config.interfaceNumber()).getCurrentAlternate(); + assertNotNull(altIntf.getEndpoints()); + assertEquals(isLoopbackDevice() ? 4 : 2, altIntf.getEndpoints().size()); + + var endpoint = altIntf.getEndpoints().getFirst(); + assertEquals(1, endpoint.getNumber()); + assertEquals(UsbDirection.OUT, endpoint.getDirection()); + assertEquals(UsbTransferType.BULK, endpoint.getTransferType()); + assertTrue(endpoint.getPacketSize() == 64 || endpoint.getPacketSize() == 512); + + endpoint = altIntf.getEndpoints().get(1); + assertEquals(2, endpoint.getNumber()); + assertEquals(UsbDirection.IN, endpoint.getDirection()); + assertEquals(UsbTransferType.BULK, endpoint.getTransferType()); + assertTrue(endpoint.getPacketSize() == 64 || endpoint.getPacketSize() == 512); + + if (isLoopbackDevice()) { + endpoint = altIntf.getEndpoints().get(2); + assertEquals(3, endpoint.getNumber()); + assertEquals(UsbDirection.OUT, endpoint.getDirection()); + assertEquals(UsbTransferType.INTERRUPT, endpoint.getTransferType()); + assertEquals(16, endpoint.getPacketSize()); + + endpoint = altIntf.getEndpoints().get(3); + assertEquals(3, endpoint.getNumber()); + assertEquals(UsbDirection.IN, endpoint.getDirection()); + assertEquals(UsbTransferType.INTERRUPT, endpoint.getTransferType()); + assertEquals(16, endpoint.getPacketSize()); + + // test alternate interface 1 + altIntf = testDevice.getInterfaces().get(config.interfaceNumber()).getAlternates().get(1); + assertEquals(2, altIntf.getEndpoints().size()); + + endpoint = altIntf.getEndpoints().getFirst(); + assertEquals(1, endpoint.getNumber()); + assertEquals(UsbDirection.OUT, endpoint.getDirection()); + assertEquals(UsbTransferType.BULK, endpoint.getTransferType()); + assertTrue(endpoint.getPacketSize() == 64 || endpoint.getPacketSize() == 512); + + endpoint = altIntf.getEndpoints().get(1); + assertEquals(2, endpoint.getNumber()); + assertEquals(UsbDirection.IN, endpoint.getDirection()); + assertEquals(UsbTransferType.BULK, endpoint.getTransferType()); + assertTrue(endpoint.getPacketSize() == 64 || endpoint.getPacketSize() == 512); + } + } + + @Test + void invalidEndpoint_shouldThrow() { + int nonExistentInEndpoint = isLoopbackDevice() ? 1 : 4; + assertThrows(UsbException.class, () -> testDevice.getEndpoint(UsbDirection.IN, nonExistentInEndpoint)); + assertThrows(UsbException.class, () -> testDevice.getEndpoint(UsbDirection.OUT, 4)); + assertThrows(UsbException.class, () -> testDevice.getEndpoint(UsbDirection.IN, 0)); + assertThrows(UsbException.class, () -> testDevice.getEndpoint(UsbDirection.OUT, 0)); + } + + @Test + void configurationDescription_isAvailable() { + var expectedLength = isLoopbackDevice() ? 69 : 115; + + var configDesc = testDevice.getConfigurationDescriptor(); + assertNotNull(configDesc); + assertEquals(expectedLength, configDesc.length); + assertEquals(2, configDesc[1]); + assertEquals((byte) expectedLength, configDesc[2]); + assertEquals((byte) 0, configDesc[3]); } } diff --git a/java-does-usb/src/test/java/net/codecrete/usb/DescriptorTest.java b/java-does-usb/src/test/java/net/codecrete/usb/DescriptorTest.java new file mode 100644 index 00000000..4ad8b7d7 --- /dev/null +++ b/java-does-usb/src/test/java/net/codecrete/usb/DescriptorTest.java @@ -0,0 +1,22 @@ +package net.codecrete.usb; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class DescriptorTest extends TestDeviceBase { + + @Test + void deviceDescriptor_isAvailable() { + var desc = testDevice.getDeviceDescriptor(); + assertThat(desc).hasSize(18); + assertThat(desc[1]).isEqualTo((byte) 0x01); + } + + @Test + void configurationDescriptor_isAvailable() { + var desc = testDevice.getConfigurationDescriptor(); + assertThat(desc).hasSizeGreaterThan(60); + assertThat(desc[1]).isEqualTo((byte) 0x02); + } +} diff --git a/java-does-usb/src/test/java/net/codecrete/usb/DeviceEnumerationTest.java b/java-does-usb/src/test/java/net/codecrete/usb/DeviceEnumerationTest.java index 78eb349a..7d3c259f 100644 --- a/java-does-usb/src/test/java/net/codecrete/usb/DeviceEnumerationTest.java +++ b/java-does-usb/src/test/java/net/codecrete/usb/DeviceEnumerationTest.java @@ -11,53 +11,39 @@ import org.junit.jupiter.api.Test; -import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class DeviceEnumerationTest extends TestDeviceBase { +class DeviceEnumerationTest extends TestDeviceBase { @Test void getAllDevices_includesLoopback() { - var found = USB.getAllDevices().stream() - .anyMatch(device -> device.vendorId() == vid && device.productId() == pid); - assertTrue(found); - } - - @Test - void getDevicesWithFilter_returnsLoopback() { - var result = USB.getDevices(new USBDeviceFilter(vid, pid)); - assertEquals(1, result.size()); - assertEquals(vid, result.get(0).vendorId()); - assertEquals(pid, result.get(0).productId()); + var deviceList = Usb.getDevices(); + assertThat(deviceList) + .isNotEmpty() + .anyMatch(device -> device.getVendorId() == config.vid() && device.getProductId() == config.pid()); } @Test - void getDevicesWithMultipleFilters_returnsLoopback() { - var result = USB.getDevices(List.of( - new USBDeviceFilter(vid, pid), - new USBDeviceFilter(0x0000, 0xffff) - )); - assertEquals(1, result.size()); - assertEquals(vid, result.get(0).vendorId()); - assertEquals(pid, result.get(0).productId()); + void getDevices_includesLoopback() { + var deviceList = Usb.findDevices(device -> device.getVendorId() == config.vid() && device.getProductId() == config.pid()); + assertThat(deviceList) + .isNotEmpty() + .anyMatch(device -> device.getVendorId() == config.vid() && device.getProductId() == config.pid()); } @Test - void getDeviceWithFilter_returnsLoopback() { - var device = USB.getDevice(new USBDeviceFilter(vid, pid)); - assertEquals(vid, device.vendorId()); - assertEquals(pid, device.productId()); + void getDevicePredicate_returnsLoopback() { + var device = Usb.findDevice(dev -> dev.getVendorId() == config.vid() && dev.getProductId() == config.pid()); + assertThat(device).isPresent(); + assertThat(device.get().getProductId()).isEqualTo(config.pid()); + assertThat(device.get().getVendorId()).isEqualTo(config.vid()); } @Test - void getDeviceWithMultipleFilters_returnsLoopback() { - var device = USB.getDevice(List.of( - new USBDeviceFilter(vid, pid), - new USBDeviceFilter(0x0000, 0xffff) - )); - assertEquals(vid, device.vendorId()); - assertEquals(pid, device.productId()); + void getDeviceVidPid_returnsLoopback() { + var device = Usb.findDevice(config.vid(), config.pid()); + assertThat(device).isPresent(); + assertThat(device.get().getProductId()).isEqualTo(config.pid()); + assertThat(device.get().getVendorId()).isEqualTo(config.vid()); } } diff --git a/java-does-usb/src/test/java/net/codecrete/usb/DeviceLifecycleTest.java b/java-does-usb/src/test/java/net/codecrete/usb/DeviceLifecycleTest.java index c9aa23be..da9b6ee1 100644 --- a/java-does-usb/src/test/java/net/codecrete/usb/DeviceLifecycleTest.java +++ b/java-does-usb/src/test/java/net/codecrete/usb/DeviceLifecycleTest.java @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Unit test for bulk transfer +// Unit test for device lifecycle (open/close, claim/release) // package net.codecrete.usb; @@ -12,52 +12,55 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; -public class DeviceLifecycleTest { +class DeviceLifecycleTest { - private USBDevice device; + private UsbDevice device; @Test void lifecycle_showsValidState() { device = TestDeviceBase.getDevice(); - int interfaceNumber = TestDeviceBase.getInterfaceNumber(device); + var interfaceNumber = TestDeviceBase.getDeviceConfig().interfaceNumber(); - var intf = device.interfaces().get(interfaceNumber); - assertEquals(interfaceNumber, intf.number()); + var intf = device.getInterfaces().get(interfaceNumber); + assertEquals(interfaceNumber, intf.getNumber()); - assertFalse(device.isOpen()); + assertFalse(device.isOpened()); assertFalse(intf.isClaimed()); - assertThrows(USBException.class, () -> device.claimInterface(interfaceNumber)); - assertThrows(USBException.class, () -> device.releaseInterface(interfaceNumber)); + assertThrows(UsbException.class, () -> device.claimInterface(interfaceNumber)); + assertThrows(UsbException.class, () -> device.releaseInterface(interfaceNumber)); device.open(); - assertTrue(device.isOpen()); + assertTrue(device.isOpened()); assertFalse(intf.isClaimed()); - assertThrows(USBException.class, () -> device.open()); - assertThrows(USBException.class, () -> device.releaseInterface(interfaceNumber)); + assertThrows(UsbException.class, () -> device.open()); + assertThrows(UsbException.class, () -> device.releaseInterface(interfaceNumber)); device.claimInterface(interfaceNumber); - assertTrue(device.isOpen()); + assertTrue(device.isOpened()); assertTrue(intf.isClaimed()); - assertThrows(USBException.class, () -> device.open()); - assertThrows(USBException.class, () -> device.claimInterface(interfaceNumber)); + assertThrows(UsbException.class, () -> device.open()); + assertThrows(UsbException.class, () -> device.claimInterface(interfaceNumber)); device.releaseInterface(interfaceNumber); - assertTrue(device.isOpen()); + assertTrue(device.isOpened()); assertFalse(intf.isClaimed()); - assertThrows(USBException.class, () -> device.open()); - assertThrows(USBException.class, () -> device.releaseInterface(interfaceNumber)); + assertThrows(UsbException.class, () -> device.open()); + assertThrows(UsbException.class, () -> device.releaseInterface(interfaceNumber)); device.close(); - assertFalse(device.isOpen()); + assertFalse(device.isOpened()); assertFalse(intf.isClaimed()); - assertThrows(USBException.class, () -> device.claimInterface(interfaceNumber)); - assertThrows(USBException.class, () -> device.releaseInterface(interfaceNumber)); + assertThrows(UsbException.class, () -> device.claimInterface(interfaceNumber)); + assertThrows(UsbException.class, () -> device.releaseInterface(interfaceNumber)); } @AfterEach diff --git a/java-does-usb/src/test/java/net/codecrete/usb/InterruptTransferTest.java b/java-does-usb/src/test/java/net/codecrete/usb/InterruptTransferTest.java index 6298e637..f59ab124 100644 --- a/java-does-usb/src/test/java/net/codecrete/usb/InterruptTransferTest.java +++ b/java-does-usb/src/test/java/net/codecrete/usb/InterruptTransferTest.java @@ -4,28 +4,32 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Unit test for bulk transfer +// Unit test for interrupt transfers // package net.codecrete.usb; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; -public class InterruptTransferTest extends TestDeviceBase { +class InterruptTransferTest extends TestDeviceBase { @Test void smallTransfer_succeeds() { - byte[] sampleData = generateRandomBytes(12, 293872394); - testDevice.transferOut(ECHO_EP_OUT, sampleData); + Assumptions.assumeTrue(isLoopbackDevice(), + "Interrupt transfer only supported by loopback test device"); + + var sampleData = generateRandomBytes(12, 293872394); + testDevice.transferOut(config.endpointEchoOut(), sampleData); // receive first echo - byte[] echo = testDevice.transferIn(ECHO_EP_IN, ECHO_MAX_PACKET_SIZE); + var echo = testDevice.transferIn(config.endpointEchoIn()); assertArrayEquals(sampleData, echo); // receive second echo - echo = testDevice.transferIn(ECHO_EP_IN, ECHO_MAX_PACKET_SIZE); + echo = testDevice.transferIn(config.endpointEchoIn()); assertArrayEquals(sampleData, echo); } } diff --git a/java-does-usb/src/test/java/net/codecrete/usb/InvalidOperationTest.java b/java-does-usb/src/test/java/net/codecrete/usb/InvalidOperationTest.java index e2d5326e..4cc8634f 100644 --- a/java-does-usb/src/test/java/net/codecrete/usb/InvalidOperationTest.java +++ b/java-does-usb/src/test/java/net/codecrete/usb/InvalidOperationTest.java @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Unit test for bulk transfer +// Unit test for invalid operations (invalid interface number, invalid endpoint number etc.) // package net.codecrete.usb; @@ -12,38 +12,39 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -public class InvalidOperationTest extends TestDeviceBase { +class InvalidOperationTest extends TestDeviceBase { @Test void claimInvalidInterface_throws() { + var interfaceNumber = config.interfaceNumber(); // throws error because it's already claimed - Assertions.assertThrows(USBException.class, () -> testDevice.claimInterface(interfaceNumber)); + Assertions.assertThrows(UsbException.class, () -> testDevice.claimInterface(interfaceNumber)); // throws error because it's an invalid interface number - Assertions.assertThrows(USBException.class, () -> testDevice.claimInterface(3)); + Assertions.assertThrows(UsbException.class, () -> testDevice.claimInterface(3)); // throws error because it's an invalid interface number - Assertions.assertThrows(USBException.class, () -> testDevice.claimInterface(888)); + Assertions.assertThrows(UsbException.class, () -> testDevice.claimInterface(888)); } @Test void releaseInvalidInterface_throws() { - Assertions.assertThrows(USBException.class, () -> testDevice.releaseInterface(1)); + Assertions.assertThrows(UsbException.class, () -> testDevice.releaseInterface(1)); } @Test void invalidEndpoint_throws() { - var data = new byte[] { 34, 23, 99, 0, 17 }; + var data = new byte[]{34, 23, 99, 0, 17}; - Assertions.assertThrows(USBException.class, () -> testDevice.transferOut(2, data)); + Assertions.assertThrows(UsbException.class, () -> testDevice.transferOut(2, data)); - Assertions.assertThrows(USBException.class, () -> testDevice.transferOut(0, data)); + Assertions.assertThrows(UsbException.class, () -> testDevice.transferOut(0, data)); - Assertions.assertThrows(USBException.class, () -> testDevice.transferOut(4, data)); + Assertions.assertThrows(UsbException.class, () -> testDevice.transferOut(4, data)); - Assertions.assertThrows(USBException.class, () -> testDevice.transferIn(1, 64)); + Assertions.assertThrows(UsbException.class, () -> testDevice.transferIn(1)); - Assertions.assertThrows(USBException.class, () -> testDevice.transferIn(0, 64)); + Assertions.assertThrows(UsbException.class, () -> testDevice.transferIn(0)); - Assertions.assertThrows(USBException.class, () -> testDevice.transferIn(5, 64)); + Assertions.assertThrows(UsbException.class, () -> testDevice.transferIn(5)); } } diff --git a/java-does-usb/src/test/java/net/codecrete/usb/SpeedTest.java b/java-does-usb/src/test/java/net/codecrete/usb/SpeedTest.java new file mode 100644 index 00000000..0c3b68c2 --- /dev/null +++ b/java-does-usb/src/test/java/net/codecrete/usb/SpeedTest.java @@ -0,0 +1,73 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Unit test to measure loopback speed +// + +package net.codecrete.usb; + +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.concurrent.CompletableFuture; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class SpeedTest extends TestDeviceBase { + + @Test + void loopback_isFast() throws Throwable { + final var isHighSpeed = testDevice.getEndpoint(UsbDirection.IN, config.endpointLoopbackIn()).getPacketSize() == 512; + final var numBytes = isHighSpeed ? 5000000 : 500000; + + var sampleData = generateRandomBytes(numBytes, 7219937602343L); + + var start = System.currentTimeMillis(); + var writer = CompletableFuture.runAsync(() -> writeBytes(sampleData)); + var reader = CompletableFuture.supplyAsync(() -> readBytes(numBytes)); + CompletableFuture.allOf(writer, reader).join(); + var end = System.currentTimeMillis(); + if (reader.isCompletedExceptionally()) + throw reader.exceptionNow(); + + assertArrayEquals(sampleData, reader.resultNow()); + + var throughput = 2.0 * numBytes / (end - start); + var expectedThroughput = isHighSpeed ? 15000.0 : 500.0; + System.out.printf("Throughput: expected ≥%,.0f KB/s, actual %,.0f KB/s%n", expectedThroughput, throughput); + + assertTrue(throughput >= expectedThroughput, "Expected throughput not achieved"); + } + + static void writeBytes(byte[] data) { + try (var os = testDevice.openOutputStream(config.endpointLoopbackOut())) { + os.write(data); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + static byte[] readBytes(int numBytes) { + try (var is = testDevice.openInputStream(config.endpointLoopbackIn())) { + var buffer = new byte[numBytes]; + var bytesRead = 0; + while (bytesRead < numBytes) { + var n = is.read(buffer, bytesRead, numBytes - bytesRead); + if (n <= 0) + throw new RuntimeException("unexpected end of input stream"); + bytesRead += n; + } + return buffer; + + } catch (IOException e) { + throw new RuntimeException(e); + } catch (Throwable e) { + e.printStackTrace(System.err); + throw e; + } + } +} diff --git a/java-does-usb/src/test/java/net/codecrete/usb/StallTest.java b/java-does-usb/src/test/java/net/codecrete/usb/StallTest.java new file mode 100644 index 00000000..e79903b8 --- /dev/null +++ b/java-does-usb/src/test/java/net/codecrete/usb/StallTest.java @@ -0,0 +1,64 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Unit test for data overruns +// + +package net.codecrete.usb; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class StallTest extends TestDeviceBase { + + @Test + void stalledBulkTransferOut_recovers() { + var endpointIn = config.endpointLoopbackIn(); + var endpointOut = config.endpointLoopbackOut(); + haltEndpoint(UsbDirection.OUT, endpointOut); + + var data = new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + assertThrows(UsbStallException.class, () -> testDevice.transferOut(endpointOut, data)); + + testDevice.clearHalt(UsbDirection.OUT, endpointOut); + + testDevice.transferOut(endpointOut, data); + var receivedData = testDevice.transferIn(endpointIn); + assertArrayEquals(data, receivedData); + } + + @Test + void stalledBulkTransferIn_recovers() { + var endpointIn = config.endpointLoopbackIn(); + var endpointOut = config.endpointLoopbackOut(); + haltEndpoint(UsbDirection.IN, endpointIn); + + assertThrows(UsbStallException.class, () -> testDevice.transferIn(endpointIn)); + + testDevice.clearHalt(UsbDirection.IN, endpointIn); + + var data = new byte[]{9, 8, 7, 6, 5, 4, 3, 2}; + testDevice.transferOut(endpointOut, data); + var receivedData = testDevice.transferIn(endpointIn); + assertArrayEquals(data, receivedData); + } + + @Test + void invalidControlTransfer_throws() { + var request = new UsbControlTransfer(UsbRequestType.VENDOR, UsbRecipient.INTERFACE, (byte) 0x08, + (short) 0, (short) config.interfaceNumber()); + assertThrows(UsbStallException.class, () -> testDevice.controlTransferIn(request, 2)); + } + + void haltEndpoint(UsbDirection direction, int endpointNumber) { + final var SET_FEATURE = 0x03; + final var ENDPOINT_HALT = 0x00; + var endpointAddress = (direction == UsbDirection.IN ? 0x80 : 0x00) | endpointNumber; + testDevice.controlTransferOut(new UsbControlTransfer(UsbRequestType.STANDARD, UsbRecipient.ENDPOINT, SET_FEATURE, ENDPOINT_HALT, endpointAddress), null); + } +} diff --git a/java-does-usb/src/test/java/net/codecrete/usb/StreamTest.java b/java-does-usb/src/test/java/net/codecrete/usb/StreamTest.java new file mode 100644 index 00000000..4109f7b6 --- /dev/null +++ b/java-does-usb/src/test/java/net/codecrete/usb/StreamTest.java @@ -0,0 +1,221 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Unit test for bulk transfer with input/output streams +// + +package net.codecrete.usb; + +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class StreamTest extends TestDeviceBase { + + @Test + void smallTransfer_succeeds() { + var sampleData = generateRandomBytes(12, 293872394); + writeBytes(sampleData, 100); + var data = readBytes(sampleData.length); + assertArrayEquals(sampleData, data); + } + + @Test + void mediumTransfer_succeeds() { + // This synchronous approach should work as the test device + // has an internal buffer of about 500 bytes. + var sampleData = generateRandomBytes(140, 97333894); + writeBytes(sampleData, 30); + var data = readBytes(sampleData.length); + assertArrayEquals(sampleData, data); + } + + @Test + void transferWithZLP_succeeds() { + var maxPacketSize = testDevice.getEndpoint(UsbDirection.OUT, config.endpointLoopbackOut()).getPacketSize(); + final var sampleData = generateRandomBytes(2 * maxPacketSize, 197007894); + var writer = CompletableFuture.runAsync(() -> { + testDevice.transferOut(config.endpointLoopbackOut(), Arrays.copyOfRange(sampleData, 0, maxPacketSize)); + sleep(200); + testDevice.transferOut(config.endpointLoopbackOut(), Arrays.copyOfRange(sampleData, maxPacketSize, 2 * maxPacketSize)); + }); + + var reader = CompletableFuture.supplyAsync(() -> readBytes(sampleData.length)); + + CompletableFuture.allOf(writer, reader).join(); + + assertArrayEquals(sampleData, reader.resultNow()); + } + + @Test + void largeTransferSmallChunks_succeeds() { + final var numBytes = 23076; + var sampleData = generateRandomBytes(numBytes, 3829007493L); + var writer = CompletableFuture.runAsync(() -> writeBytes(sampleData, 20)); + var reader = CompletableFuture.supplyAsync(() -> readBytes(numBytes)); + var allFutures = CompletableFuture.allOf(writer, reader); + allFutures.join(); + assertArrayEquals(sampleData, reader.resultNow()); + } + + @Test + void largeTransferBigChunks_succeeds() { + final var numBytes = 230763; + var sampleData = generateRandomBytes(numBytes, 3829007493L); + var writer = CompletableFuture.runAsync(() -> writeBytes(sampleData, 150)); + var reader = CompletableFuture.supplyAsync(() -> readBytes(numBytes)); + CompletableFuture.allOf(writer, reader).join(); + assertArrayEquals(sampleData, reader.resultNow()); + } + + @Test + @SuppressWarnings({"java:S2925", "BusyWait"}) + void blockedWriter_canBeAborted() throws InterruptedException { + // A writer that fills the pipe faster than it is drained eventually blocks in write(). + // Aborting the outstanding transfers from another thread must terminate it promptly and + // safely (with an IOException wrapping the USB error) rather than leave it wedged forever. + + final var data = generateRandomBytes(1_000_000, 0x5c7f10ebL); + + final var bytesWritten = new AtomicLong(0); + final var writerError = new AtomicReference(); + final var readerStream = new AtomicReference(); + + // Thread 1: write to the loopback OUT endpoint until it blocks (nothing keeps draining it). + var writer = new Thread(() -> { + try (var os = testDevice.openOutputStream(config.endpointLoopbackOut())) { + var offset = 0; + while (offset < data.length) { + var size = Math.min(100, data.length - offset); + os.write(data, offset, size); + offset += size; + bytesWritten.set(offset); + } + } catch (Throwable t) { + writerError.set(t); + } + }, "loopback-writer"); + + // Thread 2: read a limited amount from the loopback IN endpoint, then stop draining + // (the stream stays open, modelling a stalled consumer that applies back pressure). + var reader = new Thread(() -> { + try { + var is = testDevice.openInputStream(config.endpointLoopbackIn()); + readerStream.set(is); + var buffer = new byte[64]; + var n = is.read(buffer); + assertTrue(n > 0); + } catch (IOException e) { + throw new RuntimeException(e); + } + }, "loopback-reader"); + + try { + writer.start(); + reader.start(); + reader.join(); + + // Wait until the writer is actually blocked: its progress must stall for 300 ms. + var giveUp = System.currentTimeMillis() + 5000; + var lastCount = -1L; + var stableSince = System.currentTimeMillis(); + while (writer.isAlive() && System.currentTimeMillis() < giveUp) { + var count = bytesWritten.get(); + var now = System.currentTimeMillis(); + if (count != lastCount) { + lastCount = count; + stableSince = now; + } else if (now - stableSince >= 300) { + break; + } + Thread.sleep(20); + } + assertTrue(writer.isAlive(), "writer terminated before it could block"); + + // Abort the outstanding OUT transfers from this thread; the blocked writer must unwind. + testDevice.abortTransfers(UsbDirection.OUT, config.endpointLoopbackOut()); + + writer.join(1500); + assertFalse(writer.isAlive(), "writer thread did not terminate within 1.5 s after abort"); + + // It must have unwound because of the abort, not by finishing or dying some other way. + // The stream surfaces the USB error as an IOException (with the UsbException as cause). + var err = writerError.get(); + assertInstanceOf(IOException.class, err); + assertInstanceOf(UsbException.class, err.getCause()); + + } finally { + // Restore a clean device state so subsequent tests are not affected. + var is = readerStream.get(); + if (is != null) { + try { + is.close(); + } catch (IOException _) { + // ignore + } + } + if (writer.isAlive()) { + try { + testDevice.abortTransfers(UsbDirection.OUT, config.endpointLoopbackOut()); + } catch (Exception _) { + // ignore + } + writer.join(1500); + } + resetBuffers(); + drainData(config.endpointLoopbackIn()); + } + } + + static void writeBytes(byte[] data, int chunkSize) { + try (var os = testDevice.openOutputStream(config.endpointLoopbackOut())) { + var numBytes = 0; + while (numBytes < data.length) { + var size = Math.min(chunkSize, data.length - numBytes); + os.write(data, numBytes, size); + numBytes += size; + } + + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + static byte[] readBytes(int numBytes) { + var buffer = new byte[numBytes]; + try (var is = testDevice.openInputStream(config.endpointLoopbackIn())) { + var bytesRead = 0; + while (bytesRead < numBytes) { + var n = is.read(buffer, bytesRead, numBytes - bytesRead); + assertTrue(n > 0); + bytesRead += n; + } + + } catch (IOException e) { + throw new RuntimeException(e); + } + return buffer; + } + + @SuppressWarnings({"java:S2925", "SameParameterValue"}) + private static void sleep(int millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException _) { + Thread.currentThread().interrupt(); + } + } +} diff --git a/java-does-usb/src/test/java/net/codecrete/usb/TestDeviceBase.java b/java-does-usb/src/test/java/net/codecrete/usb/TestDeviceBase.java index 94275a5e..c0a35a0f 100644 --- a/java-does-usb/src/test/java/net/codecrete/usb/TestDeviceBase.java +++ b/java-does-usb/src/test/java/net/codecrete/usb/TestDeviceBase.java @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Unit tests for USB device enumeration +// Base class for unit tests using the connected test device // package net.codecrete.usb; @@ -18,66 +18,30 @@ * Base class for tests using the test device. */ public class TestDeviceBase { - /** - * Simple test device vendor ID - */ - static final int VID_SIMPLE = 0xcafe; - /** - * Simple test device product ID - */ - static final int PID_SIMPLE = 0xceaf; - /** - * Simple test device loopback interface number - */ - static final int LOOPBACK_INTF_SIMPLE = 0; - /** - * Composite test device vendor ID - */ - static final int VID_COMPOSITE = 0xcafe; - /** - * Composite test device product ID - */ - static final int PID_COMPOSITE = 0xcea0; - /** - * Composite test device loopback interface number - */ - static final int LOOPBACK_INTF_COMPOSITE = 2; - /** - * Interface number of connected test device - */ - protected static int vid = -1; - protected static int pid = -1; - protected static int interfaceNumber = -1; - protected static final int LOOPBACK_EP_OUT = 1; - protected static final int LOOPBACK_EP_IN = 2; - protected static final int LOOPBACK_MAX_PACKET_SIZE = 64; - protected static final int ECHO_EP_OUT = 3; - protected static final int ECHO_EP_IN = 3; - protected static final int ECHO_MAX_PACKET_SIZE = 16; - protected static USBDevice testDevice; - - static USBDevice getDevice() { - var device = USB.getDevice(new USBDeviceFilter(VID_COMPOSITE, PID_COMPOSITE)); - if (device == null) - device = USB.getDevice(new USBDeviceFilter(VID_SIMPLE, PID_SIMPLE)); - if (device == null) + + protected static UsbDevice testDevice; + protected static TestDeviceConfig config; + + static UsbDevice getDevice() { + var device = Usb.findDevice(dev -> TestDeviceConfig.getConfig(dev).isPresent()); + if (device.isEmpty()) throw new IllegalStateException("No test device connected"); - return device; + return device.get(); } - static int getInterfaceNumber(USBDevice device) { - return device.productId() == PID_COMPOSITE ? LOOPBACK_INTF_COMPOSITE : LOOPBACK_INTF_SIMPLE; + static TestDeviceConfig getDeviceConfig() { + return TestDeviceConfig.getConfig(getDevice()).orElse(null); } @BeforeAll static void openDevice() { testDevice = getDevice(); - vid = testDevice.vendorId(); - pid = testDevice.productId(); - interfaceNumber = getInterfaceNumber(testDevice); + config = getDeviceConfig(); testDevice.open(); - testDevice.claimInterface(interfaceNumber); + testDevice.claimInterface(config.interfaceNumber()); + + resetDevice(); } @AfterAll @@ -88,6 +52,47 @@ static void closeDevice() { } } + static boolean isLoopbackDevice() { + return !config.isComposite(); + } + + static boolean isCompositeDevce() { + return config.isComposite(); + } + + private static void resetDevice() { + if (isLoopbackDevice()) + testDevice.selectAlternateSetting(config.interfaceNumber(), 0); + + // reset buffers + resetBuffers(); + + // drain loopback data + drainData(config.endpointLoopbackIn()); + + // drain interrupt data + if (isLoopbackDevice()) + drainData(config.endpointEchoIn()); + + // reset buffers again + resetBuffers(); + } + + static void resetBuffers() { + testDevice.controlTransferOut(new UsbControlTransfer(UsbRequestType.VENDOR, UsbRecipient.INTERFACE, + (byte) 0x04, (short) 0, (short) config.interfaceNumber()), null); + } + + static void drainData(int endpointNumber) { + while (true) { + try { + testDevice.transferIn(endpointNumber, 5); + } catch (UsbTimeoutException _) { + break; + } + } + } + static byte[] generateRandomBytes(int numBytes, long seed) { var random = new Random(seed); var bytes = new byte[numBytes]; diff --git a/java-does-usb/src/test/java/net/codecrete/usb/TestDeviceConfig.java b/java-does-usb/src/test/java/net/codecrete/usb/TestDeviceConfig.java new file mode 100644 index 00000000..9e386734 --- /dev/null +++ b/java-does-usb/src/test/java/net/codecrete/usb/TestDeviceConfig.java @@ -0,0 +1,66 @@ +// +// Java Does USB +// Copyright (c) 2024 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Configuration information about test device +// + +package net.codecrete.usb; + +import java.util.Optional; +import java.util.stream.Stream; + +/** + * Test device configuration + * @param vid vendor ID + * @param pid product ID + * @param isComposite indicates if this is the composite test device + * @param interfaceNumber interface number for loopback and echo endpoints + * @param endpointLoopbackOut loopback OUT endpoint number + * @param endpointLoopbackIn loopback IN endpoint number + * @param endpointEchoOut echo OUT endpoint number + * @param endpointEchoIn echo IN endpoint number + */ +public record TestDeviceConfig(int vid, int pid, + boolean isComposite, + int interfaceNumber, + int endpointLoopbackOut, int endpointLoopbackIn, + int endpointEchoOut, int endpointEchoIn +) { + + private static final TestDeviceConfig LOOPBACK_DEVICE = new TestDeviceConfig( + 0xcafe, + 0xceaf, + false, + 0, + 1, + 2, + 3, + 3 + ); + + private static final TestDeviceConfig COMPOSITE_DEVICE = new TestDeviceConfig( + 0xcafe, + 0xcea0, + true, + 3, + 1, + 2, + -1, + -1 + ); + + + /** + * Gets the configuration fo the specified USB device. + * @param device USB device + * @return configuration, or empty if the USB device is not a test device + */ + public static Optional getConfig(UsbDevice device) { + return Stream.of(LOOPBACK_DEVICE, COMPOSITE_DEVICE) + .filter(config -> device.getVendorId() == config.vid() && device.getProductId() == config.pid()) + .findFirst(); + } +} diff --git a/java-does-usb/src/test/java/net/codecrete/usb/TimeoutTest.java b/java-does-usb/src/test/java/net/codecrete/usb/TimeoutTest.java new file mode 100644 index 00000000..7f5f1cdd --- /dev/null +++ b/java-does-usb/src/test/java/net/codecrete/usb/TimeoutTest.java @@ -0,0 +1,89 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Unit tests for transfers with timeout +// + +package net.codecrete.usb; + +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class TimeoutTest extends TestDeviceBase { + + @Test + @Timeout(value = 2, unit = TimeUnit.SECONDS) + void bulkTransferIn_timesOut() { + var endpointIn = config.endpointLoopbackIn(); + assertThrows(UsbTimeoutException.class, () -> testDevice.transferIn(endpointIn, 200)); + } + + @Test + @Timeout(value = 1, unit = TimeUnit.SECONDS) + void bulkTransfer_doesNotTimeOut() { + var data = generateRandomBytes(20, 7280277392L); + testDevice.transferOut(config.endpointLoopbackOut(), data); + + var received = testDevice.transferIn(config.endpointLoopbackIn(), 200); + assertArrayEquals(data, received); + } + + @Test + @Timeout(value = 1, unit = TimeUnit.SECONDS) + void bulkTransferOut_timesOut() { + drainData(config.endpointLoopbackIn()); + var endpointOut = config.endpointLoopbackOut(); + + // The test device has an internal buffer of about 2KB for full-speed + // and 16KB for high-speed. The first transfer should not time out. + final var bufferSize = 32 * testDevice + .getEndpoint(UsbDirection.OUT, endpointOut).getPacketSize(); + + var data = generateRandomBytes(100, 9383073929L); + testDevice.transferOut(endpointOut, data, 200); + + assertThrows(UsbTimeoutException.class, () -> { + for (var i = 0; i < bufferSize / data.length; i++) { + testDevice.transferOut(endpointOut, data, 200); + } + }); + + drainData(config.endpointLoopbackIn()); + } + + @Test + @Timeout(value = 1, unit = TimeUnit.SECONDS) + void interruptTransferIn_timesOut() { + Assumptions.assumeTrue(isLoopbackDevice(), + "Interrupt transfer only supported by loopback test device"); + + var endpointIn = config.endpointEchoIn(); + assertThrows(UsbTimeoutException.class, () -> testDevice.transferIn(endpointIn, 200)); + } + + @Test + void interruptTransfer_doesNotTimeOut() { + Assumptions.assumeTrue(isLoopbackDevice(), + "Interrupt transfer only supported by loopback test device"); + + var sampleData = generateRandomBytes(12, 293872394); + testDevice.transferOut(config.endpointEchoOut(), sampleData, 200); + + // receive first echo + var echo = testDevice.transferIn(config.endpointEchoIn(), 200); + assertArrayEquals(sampleData, echo); + + // receive second echo + echo = testDevice.transferIn(config.endpointEchoIn(), 200); + assertArrayEquals(sampleData, echo); + } +} diff --git a/java-does-usb/src/test/java/net/codecrete/usb/common/ConfigurationDescriptors.java b/java-does-usb/src/test/java/net/codecrete/usb/common/ConfigurationDescriptors.java new file mode 100644 index 00000000..9d98afc9 --- /dev/null +++ b/java-does-usb/src/test/java/net/codecrete/usb/common/ConfigurationDescriptors.java @@ -0,0 +1,509 @@ +package net.codecrete.usb.common; + +class ConfigurationDescriptors { + + // For convenience, the configuration descriptor are maintained as int arrays. + private static final int[] SIMPLE_INT_ARRAY = new int[] { + // configuration descriptor + 0x09, // bLength + 0x02, // bDescriptorType = configuration + 0x12, 0x00, // wTotalLength + 0x01, // bNumInterfaces + 0x01, // bConfigurationValue + 0x00, // iConfiguration + 0x34, // bmAttributes + 0x64, // bMaxPower + + // interface descriptor + 0x09, // bLength + 0x04, // bDescriptorType = interface + 0x00, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x00, // bNumEndpoints + 0xff, // bInterfaceClass + 0xdd, // bInterfaceSubClass + 0xcc, // bInterfaceProtocol + 0x00, // iInterface + }; + + private static final int[] LARGE_COMPOSITE_INT_ARRAY = new int[] { + // configuration descriptor + 0x09, // bLength = 9 + 0x02, // bDescriptorType = configuration + 0x5A, 0x04, // wTotalLength = 1114 + 0x04, // bNumInterfaces = 4 + 0x01, // bConfigurationValue + 0x00, // iConfiguration (string index) + 0xA0, // bmAttributes (remote wakeup) + 0x70, // bMaxPower = 224mA + + // interface association descriptor (IAD) + 0x08, // bLength = 9 + 0x0B, // bDescriptorType = iad + 0x00, // bFirstInterface = 0 + 0x03, // bInterfaceCount = 3 + 0x0E, // bFunctionClass = 0x0E (Video) + 0x03, // bFunctionSubClass = 0x03 (Video Interface Collection) + 0x00, // bFunctionProtocol = 0x00 (Undefined) + 0x00, // iFunction (string index) + + // interface descriptor + 0x09, // bLength + 0x04, // bDescriptorType = interface + 0x00, // bInterfaceNumber = 0 + 0x00, // bAlternateSetting = 0 + 0x01, // bNumEndpoints = 1 + 0x0E, // bInterfaceClass = 0x0E (Video) + 0x01, // bInterfaceSubClass = 0x01 (Video Control) + 0x00, // bInterfaceProtocol = 0x00 (Undefined) + 0x00, // iInterface (string index) + + 0x0E, // bLength = 14 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x01, 0x00, 0x01, 0xA9, 0x00, 0x80, 0xC3, 0xC9, 0x01, 0x02, 0x01, 0x02, + + 0x09, // bLength = 9 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x03, 0x04, 0x01, 0x01, 0x00, 0x02, 0x00, + + 0x09, // bLength = 9 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x03, 0x05, 0x01, 0x01, 0x00, 0x02, 0x00, + + 0x1B, // bLength = 27 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x06, 0x02, 0x6A, 0xD1, 0x49, 0x2C, 0xB8, 0x32, 0x85, 0x44, 0x3E, 0xA8, 0x64, 0x3A, 0x15, 0x23, 0x62, 0xF2, 0x06, 0x01, 0x06, 0x02, 0x3F, 0x00, 0x00, + + 0x1B, // bLength = 27 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x06, 0x06, 0xD0, 0x9E, 0xE4, 0x23, 0x78, 0x11, 0x31, 0x4F, 0xAE, 0x52, 0xD2, 0xFB, 0x8A, 0x8D, 0x3B, 0x48, 0x05, 0x01, 0x03, 0x02, 0xFF, 0x7F, 0x00, + + 0x1B, // bLength = 27 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x06, 0x0F, 0xDC, 0x95, 0x3F, 0x0F, 0x32, 0x26, 0x4E, 0x4C, 0x92, 0xC9, 0xA0, 0x47, 0x82, 0xF4, 0x3B, 0xC8, 0x02, 0x01, 0x03, 0x02, 0x20, 0x01, 0x00, + + 0x1D, // bLength = 29 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x06, 0x0E, 0xF2, 0x5D, 0xBD, 0xA8, 0x98, 0x1A, 0x4E, 0x47, 0x8D, 0xD0, 0xD9, 0x26, 0x72, 0xD1, 0x94, 0xFA, 0x02, 0x01, 0x03, 0x04, 0xFF, 0xF3, 0xF3, 0xFF, 0x00, + + 0x12, // bLength = 18 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x02, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x2E, 0x0A, 0x02, + + 0x0B, // bLength = 11 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x05, 0x03, 0x01, 0x00, 0x00, 0x02, 0x5B, 0x17, 0x00, + + // endpoint descriptor + 0x07, // bLength = 7 + 0x05, // bDescriptorType = endpoint + 0x85, // bEndpointAddress (IN) + 0x03, // bmAttributes (interrupt) + 0x40, 0x00, // wMaxPacketSize = 64 + 0x08, // bInterval = 8 + + 0x06, // bLength = 6 + 0x30, // bDescriptorType = 0x30 (video) + 0x00, 0x00, 0x08, 0x00, + + 0x05, // bLength = 5 + 0x25, // bDescriptorType = 0x25 (CS_ENDPOINT) + 0x03, 0x40, 0x00, + + // interface descriptor + 0x09, // bLength = 9 + 0x04, // bDescriptorType = interface + 0x01, // bInterfaceNumber = 1 + 0x00, // bAlternateSetting = 0 + 0x00, // bNumEndpoints = 0 + 0x0E, // bInterfaceClass = 0x0E (Video) + 0x02, // bInterfaceSubClass = 0x02 (Video Streaming) + 0x00, // bInterfaceProtocol = 0x00 (Undefined) + 0x00, // iInterface (string index) + + 0x10, // bLength = 16 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x01, 0x03, 0xD9, 0x02, 0x81, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + + 0x1B, // bLength = 27 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x04, 0x01, 0x06, 0x59, 0x55, 0x59, 0x32, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, + + 0x1E, // bLength = 30 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x05, 0x01, 0x00, 0x80, 0x02, 0xE0, 0x01, 0x00, 0x00, 0xCA, 0x08, 0x00, 0x00, 0xCA, 0x08, 0x00, 0x60, 0x09, 0x00, 0x15, 0x16, 0x05, 0x00, 0x01, 0x15, 0x16, 0x05, 0x00, + + 0x1E, // bLength = 30 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x05, 0x02, 0x00, 0x80, 0x02, 0x68, 0x01, 0x00, 0x80, 0x97, 0x06, 0x00, 0x80, 0x97, 0x06, 0x00, 0x08, 0x07, 0x00, 0x15, 0x16, 0x05, 0x00, 0x01, 0x15, 0x16, 0x05, 0x00, + + 0x1E, // bLength = 30 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x05, 0x03, 0x00, 0x00, 0x05, 0xD0, 0x02, 0x00, 0x00, 0x18, 0x15, 0x00, 0x00, 0x18, 0x15, 0x00, 0x20, 0x1C, 0x00, 0x9A, 0x5B, 0x06, 0x00, 0x01, 0x9A, 0x5B, 0x06, 0x00, + + 0x1E, // bLength = 30 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x05, 0x04, 0x00, 0x00, 0x05, 0xD0, 0x02, 0x00, 0x00, 0x5E, 0x1A, 0x00, 0x00, 0x5E, 0x1A, 0x00, 0x20, 0x1C, 0x00, 0x15, 0x16, 0x05, 0x00, 0x01, 0x15, 0x16, 0x05, 0x00, + + 0x1E, // bLength = 30 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x05, 0x05, 0x00, 0x80, 0x07, 0x38, 0x04, 0x00, 0x00, 0x76, 0x2F, 0x00, 0x00, 0x76, 0x2F, 0x00, 0x48, 0x3F, 0x00, 0x9A, 0x5B, 0x06, 0x00, 0x01, 0x9A, 0x5B, 0x06, 0x00, + + 0x1E, // bLength = 30 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x05, 0x06, 0x00, 0x80, 0x07, 0x38, 0x04, 0x00, 0x80, 0x53, 0x3B, 0x00, 0x80, 0x53, 0x3B, 0x00, 0x48, 0x3F, 0x00, 0x15, 0x16, 0x05, 0x00, 0x01, 0x15, 0x16, 0x05, 0x00, + + 0x06, // bLength = 6 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x0D, 0x01, 0x01, 0x04, + + 0x0B, // bLength = 11 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x06, 0x02, 0x0B, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + + 0x1E, // bLength = 30 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x07, 0x01, 0x00, 0x80, 0x02, 0xE0, 0x01, 0x00, 0x40, 0x38, 0x00, 0x00, 0x00, 0xA0, 0x05, 0x00, 0x00, 0x10, 0x00, 0x15, 0x16, 0x05, 0x00, 0x01, 0x15, 0x16, 0x05, 0x00, + + 0x1E, // bLength = 30 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x07, 0x02, 0x00, 0x00, 0x05, 0xD0, 0x02, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x10, 0x00, 0x9A, 0x5B, 0x06, 0x00, 0x01, 0x9A, 0x5B, 0x06, 0x00, + + 0x1E, // bLength = 30 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x07, 0x03, 0x00, 0x00, 0x05, 0xD0, 0x02, 0x00, 0x40, 0x38, 0x00, 0x00, 0x00, 0xA0, 0x05, 0x00, 0x00, 0x10, 0x00, 0x15, 0x16, 0x05, 0x00, 0x01, 0x15, 0x16, 0x05, 0x00, + + 0x1E, // bLength = 30 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x07, 0x04, 0x00, 0x00, 0x05, 0xD0, 0x02, 0x00, 0x80, 0x70, 0x00, 0x00, 0x00, 0x40, 0x0B, 0x00, 0x00, 0x10, 0x00, 0x0A, 0x8B, 0x02, 0x00, 0x01, 0x0A, 0x8B, 0x02, 0x00, + + 0x1E, // bLength = 30 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x07, 0x05, 0x00, 0x80, 0x07, 0x38, 0x04, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x10, 0x00, 0x9A, 0x5B, 0x06, 0x00, 0x01, 0x9A, 0x5B, 0x06, 0x00, + + 0x1E, // bLength = 30 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x07, 0x06, 0x00, 0x80, 0x07, 0x38, 0x04, 0x00, 0x40, 0x38, 0x00, 0x00, 0x00, 0xA0, 0x05, 0x00, 0x00, 0x10, 0x00, 0x15, 0x16, 0x05, 0x00, 0x01, 0x15, 0x16, 0x05, 0x00, + + 0x1E, // bLength = 30 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x07, 0x07, 0x00, 0x80, 0x07, 0x38, 0x04, 0x00, 0x80, 0x70, 0x00, 0x00, 0x00, 0x40, 0x0B, 0x00, 0x00, 0x10, 0x00, 0x0A, 0x8B, 0x02, 0x00, 0x01, 0x0A, 0x8B, 0x02, 0x00, + + 0x1E, // bLength = 30 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x07, 0x08, 0x00, 0x00, 0x0A, 0xA0, 0x05, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x10, 0x00, 0x9A, 0x5B, 0x06, 0x00, 0x01, 0x9A, 0x5B, 0x06, 0x00, + + 0x1E, // bLength = 30 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x07, 0x09, 0x00, 0x00, 0x0A, 0xA0, 0x05, 0x00, 0x40, 0x38, 0x00, 0x00, 0x00, 0xA0, 0x05, 0x00, 0x00, 0x10, 0x00, 0x15, 0x16, 0x05, 0x00, 0x01, 0x15, 0x16, 0x05, 0x00, + + 0x1E, // bLength = 30 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x07, 0x0A, 0x00, 0x00, 0x0F, 0x70, 0x08, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x10, 0x00, 0x9A, 0x5B, 0x06, 0x00, 0x01, 0x9A, 0x5B, 0x06, 0x00, + + 0x1E, // bLength = 30 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x07, 0x0B, 0x00, 0x00, 0x0F, 0x70, 0x08, 0x00, 0x40, 0x38, 0x00, 0x00, 0x00, 0xA0, 0x05, 0x00, 0x00, 0x10, 0x00, 0x15, 0x16, 0x05, 0x00, 0x01, 0x15, 0x16, 0x05, 0x00, + + 0x06, // bLength = 6 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x0D, 0x01, 0x01, 0x04, + + 0x1B, // bLength = 27 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x04, 0x03, 0x04, 0x4E, 0x56, 0x31, 0x32, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71, 0x0C, 0x01, 0x00, 0x00, 0x00, 0x00, + + 0x1E, // bLength = 30 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x05, 0x01, 0x00, 0x80, 0x02, 0xE0, 0x01, 0x00, 0x80, 0x97, 0x06, 0x00, 0x80, 0x97, 0x06, 0x00, 0x08, 0x07, 0x00, 0x15, 0x16, 0x05, 0x00, 0x01, 0x15, 0x16, 0x05, 0x00, + + 0x1E, // bLength = 30 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x05, 0x02, 0x00, 0x80, 0x02, 0x68, 0x01, 0x00, 0xA0, 0xF1, 0x04, 0x00, 0xA0, 0xF1, 0x04, 0x00, 0x46, 0x05, 0x00, 0x15, 0x16, 0x05, 0x00, 0x01, 0x15, 0x16, 0x05, 0x00, + + 0x1E, // bLength = 30 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x05, 0x03, 0x00, 0x00, 0x05, 0xD0, 0x02, 0x00, 0x80, 0xC6, 0x13, 0x00, 0x80, 0xC6, 0x13, 0x00, 0x18, 0x15, 0x00, 0x15, 0x16, 0x05, 0x00, 0x01, 0x15, 0x16, 0x05, 0x00, + + 0x1E, // bLength = 30 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x05, 0x04, 0x00, 0x80, 0x07, 0x38, 0x04, 0x00, 0xA0, 0x7E, 0x2C, 0x00, 0xA0, 0x7E, 0x2C, 0x00, 0x76, 0x2F, 0x00, 0x15, 0x16, 0x05, 0x00, 0x01, 0x15, 0x16, 0x05, 0x00, + + 0x06, // bLength = 6 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x0D, 0x01, 0x01, 0x04, + + // interface descriptor + 0x09, // bLength = 9 + 0x04, // bDescriptorType = interface + 0x01, // bInterfaceNumber = 1 + 0x01, // bAlternateSetting = 1 + 0x01, // bNumEndpoints = 1 + 0x0E, // bInterfaceClass = 0x0E (Video) + 0x02, // bInterfaceSubClass = 0x02 (Video Streaming) + 0x00, // bInterfaceProtocol = 0x00 (Undefined) + 0x00, // iInterface (string index) + + // endpoint descriptor + 0x07, // bLength = 7 + 0x05, // bDescriptorType = endpoint + 0x81, // bEndpointAddress (IN) + 0x05, // bmAttributes (isochronous, async, data) + 0x00, 0x04, // wMaxPacketSize = 1024 + 0x01, // bInterval = 1 + + 0x06, // bLength = 6 + 0x30, // bDescriptorType = superspeed endpoint companion + 0x05, // bMaxBurst = 5 + 0x02, // bmAttributes.isochronous.mult = 2 + 0x00, 0x48, // wBytesPerInterval = 18432 + + // interface descriptor + 0x09, // bLength = 9 + 0x04, // bDescriptorType = interface + 0x02, // bInterfaceNumber = 2 + 0x00, // bAlternateSetting = 0 + 0x00, // bNumEndpoints = 0 + 0x0E, // bInterfaceClass = 0x0E (Video) + 0x02, // bInterfaceSubClass = 0x02 (Video Streaming) + 0x00, // bInterfaceProtocol = 0x00 (Undefined) + 0x00, // iInterface (string index) + + 0x0E, // bLength = 14 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x01, 0x01, 0x4D, 0x00, 0x82, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, + + 0x1B, // bLength = 27 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x04, 0x03, 0x01, 0x4E, 0x56, 0x31, 0x32, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71, 0x0C, 0x01, 0x00, 0x00, 0x00, 0x00, + + 0x1E, // bLength = 30 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x05, 0x01, 0x00, 0x80, 0x02, 0xE0, 0x01, 0x00, 0x80, 0x97, 0x06, 0x00, 0x80, 0x97, 0x06, 0x00, 0x08, 0x07, 0x00, 0x15, 0x16, 0x05, 0x00, 0x01, 0x15, 0x16, 0x05, 0x00, + + 0x06, // bLength = 6 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x0D, 0x01, 0x01, 0x04, + + // interface descriptor + 0x09, // bLength = 9 + 0x04, // bDescriptorType = interface + 0x02, // bInterfaceNumber = 2 + 0x01, // bAlternateSetting = 1 + 0x01, // bNumEndpoints = 1 + 0x0E, // bInterfaceClass = 0x0E (Video) + 0x02, // bInterfaceSubClass = 0x02 (Video Streaming) + 0x00, // bInterfaceProtocol = 0x00 (Undefined) + 0x00, // iInterface (string index) + + // endpoint descriptor + 0x07, // bLength = 7 + 0x05, // bDescriptorType = endpoint + 0x82, // bEndpointAddress (IN) + 0x05, // bmAttributes (isochronous, async, data) + 0x00, 0x04, // wMaxPacketSize = 1024 + 0x01, // bInterval = 1 + + 0x06, // bLength = 6 + 0x30, // bDescriptorType = superspeed endpoint companion + 0x00, // bMaxBurst = 0 + 0x02, // bmAttributes.isochronous.mult = 2 + 0x00, 0x0C, // wBytesPerInterval = 3072 + + // interface descriptor + 0x09, // bLength = 9 + 0x04, // bDescriptorType = interface + 0x03, // bInterfaceNumber = 3 + 0x00, // bAlternateSetting = 0 + 0x01, // bNumEndpoints = 1 + 0x03, // bInterfaceClass = 0x03 (HID) + 0x00, // bInterfaceSubClass = 0x00 (No Subclass) + 0x00, // bInterfaceProtocol = 0x00 (None) + 0x00, // iInterface (string index) + + // HID descriptor + 0x09, // bLength = 9 + 0x21, // bDescriptorType = 0x21 (HID) + 0x10, 0x01, // bcdHID = 1.10 + 0x00, // bCountryCode = 0 (not localized) + 0x01, // bNumDescriptors = 1 + 0x22, // bDescriptorType = 0x22 (report) + 0x46, 0x02, // wDescriptorLength = 582 + + // endpoint descriptor + 0x07, // bLength = 7 + 0x05, // bDescriptorType = endpoint + 0x84, // bEndpointAddress (IN) + 0x03, // bmAttributes (interrupt) + 0x40, 0x00, // wMaxPacketSize = 64 + 0x0A, // bInterval = 10 + + 0x06, // bLength = 6 + 0x30, // bDescriptorType = superspeed endpoint companion + 0x00, // bMaxBurst = 0 + 0x00, // bmAttributes = 0 + 0x40, 0x00 // wBytesPerInterval = 64 + }; + + private static final int[] COMPOSITE_TEST_DEVICE_INT_ARRAY = new int[] { + // configuration descriptor + 0x09, // bLength = 9 + 0x02, // bDescriptorType = configuration + 0x73, 0x00, // wTotalLength = 115 + 0x04, // bNumInterfaces = 4 + 0x01, // bConfigurationValue = 1 + 0x00, // iConfiguration (string index) + 0x80, // bmAttributes = bus powered + 0xFA, // bMaxPower = 500mA + + // interface association descriptor (IAD) + 0x08, // bLength = 8 + 0x0B, // bDescriptorType = iad + 0x00, // bFirstInterface = 0 + 0x02, // bInterfaceCount = 2 + 0x02, // bFunctionClass = 2 (Communications) + 0x02, // bFunctionSubClass = 2 (Abstract) + 0x00, // bFunctionProtocol = 0 (None) + 0x00, // iFunction (string index) + + // interface descriptor + 0x09, // bLength = 9 + 0x04, // bDescriptorType = interface + 0x00, // bInterfaceNumber = 0 + 0x00, // bAlternateSetting = 0 + 0x01, // bNumEndpoints = 1 + 0x02, // bInterfaceClass = 2 (Communications) + 0x02, // bInterfaceSubClass = 2 (Abstract) + 0x00, // bInterfaceProtocol = 0 (None) + 0x00, // iInterface (string index) + + // CDC header functional descriptor + 0x05, // bLength = 5 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x00, // bDescriptorSubtype = 0 (Header) + 0x20, 0x01, // bcdCDC = 1.20 + + // CDC call management functional descriptor + 0x05, // bLength = 5 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x01, // bDescriptorSubtype = 1 (Call Management) + 0x00, // bmCapabilities = 0 (None) + 0x01, // bDataInterface = 1 (Data Class Interface 1) + + // CDC abstract control management functional descriptor + 0x04, // bLength = 4 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x02, // bDescriptorSubtype = 2 (Abstract Control Management) + 0x02, // bmCapabilities = 2 (Line Coding and Serial State) + + // CDC union functional descriptor + 0x05, // bLength = 5 + 0x24, // bDescriptorType = 0x24 (CS_INTERFACE) + 0x06, // bDescriptorSubtype = 6 (Union) + 0x00, // bMasterInterface = 0 (Communications Class Interface 0) + 0x01, // bSlaveInterface0 = 1 (Data Class Interface 1) + + // endpoint descriptor + 0x07, // bLength = 7 + 0x05, // bDescriptorType = endpoint + 0x83, // bEndpointAddress (IN) + 0x03, // bmAttributes (interrupt) + 0x08, 0x00, // wMaxPacketSize = 8 + 0x10, // bInterval = 16 + + // interface descriptor + 0x09, // bLength = 9 + 0x04, // bDescriptorType = interface + 0x01, // bInterfaceNumber = 1 + 0x00, // bAlternateSetting = 0 + 0x02, // bNumEndpoints = 2 + 0x0A, // bInterfaceClass = 0x0A (CDC Data) + 0x00, // bInterfaceSubClass = 0 (None) + 0x00, // bInterfaceProtocol = 0 (None) + 0x00, // iInterface (string index) + + // endpoint descriptor + 0x07, // bLength = 7 + 0x05, // bDescriptorType = endpoint + 0x02, // bEndpointAddress (OUT) + 0x02, // bmAttributes (bulk) + 0x40, 0x00, // wMaxPacketSize = 64 + 0x00, // bInterval = 0 + + // endpoint descriptor + 0x07, // bLength = 7 + 0x05, // bDescriptorType = endpoint + 0x81, // bEndpointAddress (IN) + 0x02, // bmAttributes (Bulk) + 0x40, 0x00, // wMaxPacketSize = 64 + 0x00, // bInterval = 0 + + // interface association descriptor (IAD) + 0x08, // bLength = 8 + 0x0B, // bDescriptorType = iad + 0x02, // bFirstInterface = 2 + 0x02, // bInterfaceCount = 2 + 0xFF, // bFunctionClass = 0xFF (Vendor Specific) + 0x00, // bFunctionSubClass = 0 (None) + 0x00, // bFunctionProtocol = 0 (None) + 0x04, // iFunction (string index) + + // interface descriptor + 0x09, // bLength = 9 + 0x04, // bDescriptorType = interface + 0x02, // bInterfaceNumber = 2 + 0x00, // bAlternateSetting = 0 + 0x00, // bNumEndpoints = 0 + 0xFF, // bInterfaceClass = 0xFF (Vendor Specific) + 0x00, // bInterfaceSubClass = 0 (None) + 0x00, // bInterfaceProtocol = 0 (None) + 0x00, // iInterface (string index) + + // interface descriptor + 0x09, // bLength = 9 + 0x04, // bDescriptorType = interface + 0x03, // bInterfaceNumber = 3 + 0x00, // bAlternateSetting = 0 + 0x02, // bNumEndpoints = 2 + 0xFF, // bInterfaceClass = 0xFF (Vendor Specific) + 0x00, // bInterfaceSubClass = 0 (None) + 0x00, // bInterfaceProtocol = 0 (None) + 0x00, // iInterface (string index) + + // endpoint descriptor + 0x07, // bLength = 7 + 0x05, // bDescriptorType = endpoint + 0x01, // bEndpointAddress (OUT) + 0x02, // bmAttributes (Bulk) + 0x40, 0x00, // wMaxPacketSize = 64 + 0x00, // bInterval = 0 + + // endpoint descriptor + 0x07, // bLength = 7 + 0x05, // bDescriptorType = endpoint + 0x82, // bEndpointAddress (IN) + 0x02, // bmAttributes (Bulk) + 0x40, 0x00, // wMaxPacketSize = 64 + 0x00, // bInterval = 0 + }; + + static final byte[] SIMPLE; + + static final byte[] COMPOSITE_LARGE; + + static final byte[] COMPOSITE_TEST_DEVICE; + + static { + SIMPLE = new byte[SIMPLE_INT_ARRAY.length]; + for (int i = 0; i < SIMPLE_INT_ARRAY.length; i++) + SIMPLE[i] = (byte) SIMPLE_INT_ARRAY[i]; + + COMPOSITE_LARGE = new byte[LARGE_COMPOSITE_INT_ARRAY.length]; + for (int i = 0; i < LARGE_COMPOSITE_INT_ARRAY.length; i++) + COMPOSITE_LARGE[i] = (byte) LARGE_COMPOSITE_INT_ARRAY[i]; + + COMPOSITE_TEST_DEVICE = new byte[COMPOSITE_TEST_DEVICE_INT_ARRAY.length]; + for (int i = 0; i < COMPOSITE_TEST_DEVICE_INT_ARRAY.length; i++) + COMPOSITE_TEST_DEVICE[i] = (byte) COMPOSITE_TEST_DEVICE_INT_ARRAY[i]; + } +} diff --git a/java-does-usb/src/test/java/net/codecrete/usb/common/ConfigurationParserTest.java b/java-does-usb/src/test/java/net/codecrete/usb/common/ConfigurationParserTest.java new file mode 100644 index 00000000..f9a65e25 --- /dev/null +++ b/java-does-usb/src/test/java/net/codecrete/usb/common/ConfigurationParserTest.java @@ -0,0 +1,252 @@ +package net.codecrete.usb.common; + +import net.codecrete.usb.UsbDirection; +import net.codecrete.usb.UsbException; +import net.codecrete.usb.UsbTransferType; +import org.junit.jupiter.api.Test; + +import java.lang.foreign.MemorySegment; + +import static net.codecrete.usb.common.ConfigurationDescriptors.COMPOSITE_LARGE; +import static net.codecrete.usb.common.ConfigurationDescriptors.COMPOSITE_TEST_DEVICE; +import static net.codecrete.usb.common.ConfigurationDescriptors.SIMPLE; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class ConfigurationParserTest { + + @Test + void simpleDescriptor_canBeParsed() { + + var configuration = ConfigurationParser.parseConfigurationDescriptor(MemorySegment.ofArray(SIMPLE)); + + assertThat(configuration.interfaces()) + .hasSize(1) + .singleElement().satisfies(intf -> { + assertThat(intf.getNumber()).isZero(); + assertThat(intf.getAlternates()) + .hasSize(1) + .singleElement().satisfies(altIntf -> { + assertThat(altIntf).isSameAs(intf.getCurrentAlternate()); + assertThat(altIntf.getNumber()).isZero(); + assertThat(altIntf.getEndpoints()).isEmpty(); + assertThat(altIntf.getClassCode()).isEqualTo(0x0ff); + assertThat(altIntf.getSubclassCode()).isEqualTo(0x0dd); + assertThat(altIntf.getProtocolCode()).isEqualTo(0x0cc); + }); + assertThat(intf.isClaimed()).isFalse(); + }); + assertThat(configuration.functions()).hasSize(1); + assertThat(configuration.configValue()).isEqualTo(1); + assertThat(configuration.attributes()).isEqualTo(0x34); + assertThat(configuration.maxPower()).isEqualTo(0x64); + } + + @Test + @SuppressWarnings("java:S5961") + void largeCompositeDescriptor_canBeParsed() { + var configuration = ConfigurationParser.parseConfigurationDescriptor(MemorySegment.ofArray(COMPOSITE_LARGE)); + + // 2 functions + assertThat(configuration.functions()) + .hasSize(2); + + // function 0: 3 interfaces + assertThat(configuration.functions().get(0)).satisfies(function -> { + assertThat(function.firstInterfaceNumber()).isZero(); + assertThat(function.numInterfaces()).isEqualTo(3); + }); + + // function 1: 1 interface + assertThat(configuration.functions().get(1)).satisfies(function -> { + assertThat(function.firstInterfaceNumber()).isEqualTo(3); + assertThat(function.numInterfaces()).isEqualTo(1); + }); + + assertThat(configuration.interfaces()).hasSize(4); + + // interface 0 + assertThat(configuration.interfaces().get(0)).satisfies(intf -> { + assertThat(intf.getNumber()).isZero(); + assertThat(intf.getAlternates()).hasSize(1); + assertThat(intf.getCurrentAlternate().getEndpoints()).hasSize(1); + assertThat(intf.getCurrentAlternate().getEndpoints().getFirst()).satisfies(endpoint -> { + assertThat(endpoint.getNumber()).isEqualTo(5); + assertThat(endpoint.getDirection()).isEqualTo(UsbDirection.IN); + assertThat(endpoint.getTransferType()).isEqualTo(UsbTransferType.INTERRUPT); + }); + }); + + // interface 1 + assertThat(configuration.interfaces().get(1)).satisfies(intf -> { + assertThat(intf.getNumber()).isEqualTo(1); + assertThat(intf.getAlternates()).hasSize(2); + assertThat(intf.getAlternates().get(0)).satisfies(alternate -> { + assertThat(alternate.getNumber()).isZero(); + assertThat(alternate.getEndpoints()).isEmpty(); + }); + assertThat(intf.getAlternates().get(1)).satisfies(alternate -> { + assertThat(alternate.getNumber()).isEqualTo(1); + assertThat(alternate.getEndpoints()).hasSize(1); + assertThat(alternate.getEndpoints().getFirst()).satisfies(endpoint -> { + assertThat(endpoint.getNumber()).isEqualTo(1); + assertThat(endpoint.getDirection()).isEqualTo(UsbDirection.IN); + assertThat(endpoint.getTransferType()).isEqualTo(UsbTransferType.ISOCHRONOUS); + }); + }); + }); + + // interface 2 + assertThat(configuration.interfaces().get(2)).satisfies(intf -> { + assertThat(intf.getNumber()).isEqualTo(2); + assertThat(intf.getAlternates()).hasSize(2); + assertThat(intf.getAlternates().get(0)).satisfies(alternate -> { + assertThat(alternate.getNumber()).isZero(); + assertThat(alternate.getEndpoints()).isEmpty(); + }); + assertThat(intf.getAlternates().get(1)).satisfies(alternate -> { + assertThat(alternate.getNumber()).isEqualTo(1); + assertThat(alternate.getEndpoints()).hasSize(1); + assertThat(alternate.getEndpoints().getFirst()).satisfies(endpoint -> { + assertThat(endpoint.getNumber()).isEqualTo(2); + assertThat(endpoint.getDirection()).isEqualTo(UsbDirection.IN); + assertThat(endpoint.getTransferType()).isEqualTo(UsbTransferType.ISOCHRONOUS); + }); + }); + }); + + // interface 3 + assertThat(configuration.interfaces().get(3)).satisfies(intf -> { + assertThat(intf.getNumber()).isEqualTo(3); + assertThat(intf.getAlternates()).hasSize(1); + assertThat(intf.getAlternates().getFirst()).satisfies(alternate -> { + assertThat(alternate.getNumber()).isZero(); + assertThat(alternate.getEndpoints()).hasSize(1); + assertThat(alternate.getEndpoints().getFirst()).satisfies(endpoint -> { + assertThat(endpoint.getNumber()).isEqualTo(4); + assertThat(endpoint.getDirection()).isEqualTo(UsbDirection.IN); + assertThat(endpoint.getTransferType()).isEqualTo(UsbTransferType.INTERRUPT); + }); + }); + }); + } + + + @Test + @SuppressWarnings("java:S5961") + void compositeTestDeviceDescriptor_canBeParsed() { + var configuration = ConfigurationParser.parseConfigurationDescriptor(MemorySegment.ofArray(COMPOSITE_TEST_DEVICE)); + + // 2 functions + assertThat(configuration.functions()) + .hasSize(2); + + // function 0: 2 interfaces + assertThat(configuration.functions().get(0)).satisfies(function -> { + assertThat(function.firstInterfaceNumber()).isZero(); + assertThat(function.numInterfaces()).isEqualTo(2); + }); + + // function 1: 2 interfaces + assertThat(configuration.functions().get(1)).satisfies(function -> { + assertThat(function.firstInterfaceNumber()).isEqualTo(2); + assertThat(function.numInterfaces()).isEqualTo(2); + }); + + assertThat(configuration.interfaces()).hasSize(4); + + // interface 0 + assertThat(configuration.interfaces().get(0)).satisfies(intf -> { + assertThat(intf.getNumber()).isZero(); + assertThat(intf.getAlternates()).hasSize(1); + assertThat(intf.getCurrentAlternate().getEndpoints()).hasSize(1); + assertThat(intf.getCurrentAlternate().getEndpoints().getFirst()).satisfies(endpoint -> { + assertThat(endpoint.getNumber()).isEqualTo(3); + assertThat(endpoint.getDirection()).isEqualTo(UsbDirection.IN); + assertThat(endpoint.getTransferType()).isEqualTo(UsbTransferType.INTERRUPT); + }); + }); + + // interface 1 + assertThat(configuration.interfaces().get(1)).satisfies(intf -> { + assertThat(intf.getNumber()).isEqualTo(1); + assertThat(intf.getAlternates()).hasSize(1); + assertThat(intf.getAlternates().getFirst()).satisfies(alternate -> { + assertThat(alternate.getNumber()).isZero(); + assertThat(alternate.getEndpoints()).hasSize(2); + assertThat(alternate.getEndpoints().get(0)).satisfies(endpoint -> { + assertThat(endpoint.getNumber()).isEqualTo(2); + assertThat(endpoint.getDirection()).isEqualTo(UsbDirection.OUT); + assertThat(endpoint.getTransferType()).isEqualTo(UsbTransferType.BULK); + }); + assertThat(alternate.getEndpoints().get(1)).satisfies(endpoint -> { + assertThat(endpoint.getNumber()).isEqualTo(1); + assertThat(endpoint.getDirection()).isEqualTo(UsbDirection.IN); + assertThat(endpoint.getTransferType()).isEqualTo(UsbTransferType.BULK); + }); + }); + }); + + // interface 2 + assertThat(configuration.interfaces().get(2)).satisfies(intf -> { + assertThat(intf.getNumber()).isEqualTo(2); + assertThat(intf.getAlternates()).hasSize(1); + assertThat(intf.getAlternates().getFirst()).satisfies(alternate -> { + assertThat(alternate.getNumber()).isZero(); + assertThat(alternate.getEndpoints()).isEmpty(); + }); + }); + + // interface 3 + assertThat(configuration.interfaces().get(3)).satisfies(intf -> { + assertThat(intf.getNumber()).isEqualTo(3); + assertThat(intf.getAlternates()).hasSize(1); + assertThat(intf.getAlternates().getFirst()).satisfies(alternate -> { + assertThat(alternate.getNumber()).isZero(); + assertThat(alternate.getEndpoints()).hasSize(2); + assertThat(alternate.getEndpoints().get(0)).satisfies(endpoint -> { + assertThat(endpoint.getNumber()).isEqualTo(1); + assertThat(endpoint.getDirection()).isEqualTo(UsbDirection.OUT); + assertThat(endpoint.getTransferType()).isEqualTo(UsbTransferType.BULK); + }); + assertThat(alternate.getEndpoints().get(1)).satisfies(endpoint -> { + assertThat(endpoint.getNumber()).isEqualTo(2); + assertThat(endpoint.getDirection()).isEqualTo(UsbDirection.IN); + assertThat(endpoint.getTransferType()).isEqualTo(UsbTransferType.BULK); + }); + }); + }); + } + + @Test + void tooShortDescriptor_throwsException() { + var desc = new byte[COMPOSITE_LARGE.length - 1]; + System.arraycopy(COMPOSITE_LARGE, 0, desc, 0, desc.length); + var segment = MemorySegment.ofArray(desc); + + assertThatThrownBy(() -> ConfigurationParser.parseConfigurationDescriptor(segment)) + .isInstanceOf(UsbException.class) + .hasMessage("invalid USB configuration descriptor (invalid length)"); + } + + @Test + void tooLongDescriptor_throwsException() { + var desc = new byte[COMPOSITE_LARGE.length + 1]; + System.arraycopy(COMPOSITE_LARGE, 0, desc, 0, COMPOSITE_LARGE.length); + var segment = MemorySegment.ofArray(desc); + + assertThatThrownBy(() -> ConfigurationParser.parseConfigurationDescriptor(segment)) + .isInstanceOf(UsbException.class) + .hasMessage("invalid USB configuration descriptor (invalid length)"); + } + + @Test + void invalidDescriptor_throwsException() { + var desc = new byte[]{0x5a, 0x41, 0x03, 0x07}; + var segment = MemorySegment.ofArray(desc); + + assertThatThrownBy(() -> ConfigurationParser.parseConfigurationDescriptor(segment)) + .isInstanceOf(UsbException.class) + .hasMessage("invalid USB configuration descriptor"); + } +} diff --git a/java-does-usb/src/test/java/net/codecrete/usb/sample/MonitorDevices.java b/java-does-usb/src/test/java/net/codecrete/usb/sample/MonitorDevices.java deleted file mode 100644 index 9f0c0df3..00000000 --- a/java-does-usb/src/test/java/net/codecrete/usb/sample/MonitorDevices.java +++ /dev/null @@ -1,33 +0,0 @@ -// -// Java Does USB -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// - -package net.codecrete.usb.sample; - -import net.codecrete.usb.USB; - -import java.io.IOException; - -/** - * Sample program displaying information when USB devices are connected or disconnected. - *

- * Quit with Ctrl-C or whatever stops a program on your platform. - *

- */ -public class MonitorDevices { - - public static void main(String[] args) throws IOException { - USB.setOnDeviceConnected((device) -> System.out.println("Connected: " + device.toString())); - USB.setOnDeviceDisconnected((device) -> System.out.println("Disconnected: " + device.toString())); - - for (var device : USB.getAllDevices()) - System.out.println("Present: " + device.toString()); - System.out.println("Monitoring..."); - - //noinspection ResultOfMethodCallIgnored - System.in.read(); - } -} diff --git a/java-does-usb/src/test/java/net/codecrete/usb/special/Continuous.java b/java-does-usb/src/test/java/net/codecrete/usb/special/Continuous.java new file mode 100644 index 00000000..c432972b --- /dev/null +++ b/java-does-usb/src/test/java/net/codecrete/usb/special/Continuous.java @@ -0,0 +1,61 @@ +package net.codecrete.usb.special; + +import net.codecrete.usb.Usb; +import net.codecrete.usb.UsbDevice; +import net.codecrete.usb.UsbException; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Random; + +public class Continuous { + + @SuppressWarnings("ResultOfMethodCallIgnored") + public static void main(String[] args) throws IOException { + var device = Usb.findDevice(0xcafe, 0xceaf) + .or(() -> Usb.findDevice(0xcafe, 0xcea0)) + .orElseThrow(() -> new IllegalStateException("No test device connected")); + var interfaceNumber = device.getProductId() == 0xceaf ? 0 : 3; + + device.open(); + device.claimInterface(interfaceNumber); + + new Thread(() -> readData(device)).start(); + new Thread(() -> sendData(device)).start(); + + System.out.println("Press RETURN to exit"); + System.in.read(); + device.close(); + } + + @SuppressWarnings({"java:S2925", "BusyWait"}) + private static void sendData(UsbDevice device) { + var random = new Random(); + var buffer = new byte[40]; + + while (true) { + random.nextBytes(buffer); + try { + device.transferOut(1, buffer); + Thread.sleep(3000); + } catch (UsbException _) { + return; + } catch (InterruptedException _) { + Thread.currentThread().interrupt(); + } + } + } + + private static void readData(UsbDevice device) { + var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + while (true) { + try { + var packet = device.transferIn(2); + System.out.printf("%s packet of %d bytes received%n", LocalDateTime.now().format(formatter), packet.length); + } catch (UsbException _) { + return; + } + } + } +} diff --git a/java-does-usb/src/test/java/net/codecrete/usb/sample/EnumerateDevices.java b/java-does-usb/src/test/java/net/codecrete/usb/special/EnumerateDevices.java similarity index 74% rename from java-does-usb/src/test/java/net/codecrete/usb/sample/EnumerateDevices.java rename to java-does-usb/src/test/java/net/codecrete/usb/special/EnumerateDevices.java index c18a3b45..a88ff90f 100644 --- a/java-does-usb/src/test/java/net/codecrete/usb/sample/EnumerateDevices.java +++ b/java-does-usb/src/test/java/net/codecrete/usb/special/EnumerateDevices.java @@ -5,9 +5,9 @@ // https://opensource.org/licenses/MIT // -package net.codecrete.usb.sample; +package net.codecrete.usb.special; -import net.codecrete.usb.USB; +import net.codecrete.usb.Usb; /** * Sample program displaying the connected USB devices @@ -15,7 +15,7 @@ public class EnumerateDevices { public static void main(String[] args) { - for (var device : USB.getAllDevices()) { + for (var device : Usb.getDevices()) { System.out.println(device); } } diff --git a/java-does-usb/src/test/java/net/codecrete/usb/special/LogicAnalyzer.java b/java-does-usb/src/test/java/net/codecrete/usb/special/LogicAnalyzer.java new file mode 100644 index 00000000..006f6693 --- /dev/null +++ b/java-does-usb/src/test/java/net/codecrete/usb/special/LogicAnalyzer.java @@ -0,0 +1,377 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Firmware for logic analyzer is from Sigrok project +// and licensed under GNU GPL (version 2, or later). +// + +package net.codecrete.usb.special; + +import net.codecrete.usb.Usb; +import net.codecrete.usb.UsbControlTransfer; +import net.codecrete.usb.UsbDevice; +import net.codecrete.usb.UsbDirection; +import net.codecrete.usb.UsbException; +import net.codecrete.usb.UsbRecipient; +import net.codecrete.usb.UsbRequestType; + +import java.io.Closeable; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Sample program for sampling data with a logic analyzer. + *

+ * This sample assumes that a Saleae 8 clone with the VID 0x0925 and PID + * 0x3881 is connected. + *

+ *

+ * The USB communication of Saleae 8 clones (and any other logic analyzer + * based on a similar Cypress chip) is very sensitive as the chip only has + * a tiny internal buffer and – for the maximum 24 MHz sample rate – operates + * rather close to the practical limit of USB 2.0 high-speed. If the USB bus + * has other traffic from other devices or if the JVM takes a GC time-out, + * the internal buffer overruns and the logic analyzer stops data acquisition. + *

+ *

+ * The firmware for the Cypress chips needs to be uploaded once after the + * device has been connected. After the firmware upload, the device disconnect + * and reconnect after about 2 to 3 seconds. Vendor and product ID do not + * change but the manufacturer name and serial number do. + *

+ */ +public class LogicAnalyzer implements Closeable { + + /// USB vendor ID + static final int VID = 0x0925; + /// USB product ID + static final int PID = 0x3881; + + // bulk endpoint number + static final int EP = 2; + + /// Effective sample rate (in Hz) + private int effSampleRate; + /// Sample period (in clock ticks) + private int period; + /// Sample duration (in ms) + private int duration; + /// Flag to use 48 MHz clock (instead of 30 MHz) + private boolean use48Mhz; + /// Flag that buffer overrun has been detected + private volatile boolean bufferOverrunDetected; + /// Flag that acquisition should/has stopped + private volatile boolean stopped; + /// Filename for saving sample data + private String filename; + /// Variable is updated while new data is received + private volatile long activityValue; + /// Indicates a dry run (to suppress output) + private boolean isDryRun; + /// Buffer size for input stream (good for approx. 0.2s of data) + private int bufferSize; + + public static void main(String[] args) { + try (var logicAnalyzer = new LogicAnalyzer()) { + logicAnalyzer.sampleData(24000000, 5000, "sample.bin"); + } + } + + private UsbDevice device; + + LogicAnalyzer() { + var optionalDevice = Usb.findDevice(VID, PID); + if (optionalDevice.isEmpty()) + throw new IllegalStateException("no logic analyzer connected"); + + device = optionalDevice.get(); + + checkFirmware(); + + device.open(); + device.claimInterface(0); + + dryRun(12000000, 100); + dryRun(10000000, 200); + } + + @Override + public void close() { + device.close(); + } + + /** + * Sample data and save it to the file + * @param sampleRate sample rate (in Hz) + * @param duration duration (in ms) + * @param filename filename to save to, or {@code null} to not save it + */ + void sampleData(int sampleRate, int duration, String filename) { + if (sampleRate < 20000 || sampleRate > 24000000) { + System.err.println("Sample rate outside the supported range of 10kHz to 24Mhz"); + return; + } + + stopped = false; + bufferOverrunDetected = false; + this.filename = filename; + prepareSampling(sampleRate, duration); + var acquirer = CompletableFuture.runAsync(this::saveSamples); + sleep(50); // give thread time to start + startAcquisition(); + var watchdog = CompletableFuture.runAsync(this::detectBufferOverrun); + CompletableFuture.allOf(acquirer, watchdog).join(); + } + + void dryRun(int sampleRate, int duration) { + isDryRun = true; + sampleData(sampleRate, duration, null); + isDryRun = false; + } + + void prepareSampling(int sampleRate, int duration) { + this.duration = duration; + + // calculate the optimal sample rate and if to use the 48 or 30 MHz clock + int ticks48Mhz = (48000000 + sampleRate / 2) / sampleRate; + int ticks30Mhz = (30000000 + sampleRate / 2) / sampleRate; + double err48Mhz = Math.abs(48000000.0 / ticks48Mhz - sampleRate); + double err30Mhz = Math.abs(30000000.0 / ticks30Mhz - sampleRate); + + if (ticks48Mhz <= 0x0600 && err48Mhz <= err30Mhz) { + use48Mhz = true; + period = ticks48Mhz; + effSampleRate = 48000000 / period; + } else { + use48Mhz = false; + period = ticks30Mhz; + effSampleRate = 30000000 / period; + } + + bufferSize = (int) Math.round(effSampleRate * 0.2); + bufferSize = Math.max(bufferSize, 16 * 4096); + } + + /** + * Start acquisition with the specified sample rate + */ + void startAcquisition() { + // Command structure (3 bytes) + // Bit 5: 0 - 8 bit samples, 1 - 16 bit samples + // Bit 6: 0 - 30 MHz clock, 1 - 48 MHz clock + // Byte 0: flags (not needed here) + // Byte 1-2: clock ticks (-1) between two samples (16 bit, big endian) + + int ticks = period - 1; + byte flags = use48Mhz ? (byte) (1 << 6) : 0; + + var cmd = new byte[3]; + cmd[0] = flags; + cmd[1] = (byte) (ticks >> 8); + cmd[2] = (byte) (ticks & 0xff); + + // send the start command + final int commandCodeStart = 0xb1; + device.controlTransferOut(new UsbControlTransfer(UsbRequestType.VENDOR, UsbRecipient.DEVICE, commandCodeStart, 0, 0), cmd); + } + + void saveSamples() { + + // retrieve the sample data from the bulk endpoint + int expectedSize = (int) (((long) duration * effSampleRate + 500) / 1000); + + byte[] sampleData = new byte[expectedSize]; + + int size = 0; + try (var is = device.openInputStream(EP, bufferSize)) { + + while (size < expectedSize) { + int n = is.read(sampleData, size, sampleData.length - size); + if (n <= 0) + break; + size += n; + activityValue = size; + } + + } catch (UsbException e) { + if (!stopped && !bufferOverrunDetected) + throw e; + + } catch (IOException e) { + System.err.println("Retrieving samples failed"); + e.printStackTrace(System.err); + return; + } + + if (bufferOverrunDetected) { + System.err.println("Buffer overflow, acquisition has stopped"); + } else if (!stopped) { + stopAcquisition(); + } else { + stopped = true; + } + + if (!isDryRun) + System.out.printf("%,d samples retrieved with %,d sample/s%n", size, effSampleRate); + + if (filename != null) { + try { + Files.write(Path.of(filename), sampleData); + } catch (IOException e) { + System.err.printf("Saving samples to %s failed%n", filename); + e.printStackTrace(System.err); + } + } + } + + void stopAcquisition() { + // stop the acquisition by halting the bulk endpoint and clearing the halt + stopped = true; + device.abortTransfers(UsbDirection.IN, EP); + } + + void detectBufferOverrun() { + sleep(10); + // if the 'activityValue' hasn't changed within 20ms, the logic analyzer + // has stopped sending data (likely due to a buffer overrun) + long lastValue = 0; + while (true) { + sleep(5); + + if (stopped) + break; + + if (lastValue == activityValue) { + bufferOverrunDetected = true; + stopAcquisition(); + return; + } + + lastValue = activityValue; + } + } + + void checkFirmware() { + if (device.getManufacturer() != null) { + System.out.println("Device ready"); + return; + } + + System.out.println("Uploading firmware..."); + + byte[] firmware; + // load open-source firmware from Sigrok project (see http://sigrok.org/wiki/Fx2lafw) + try (var is = getClass().getClassLoader().getResourceAsStream("fx2lafw-saleae-logic.fw")) { + firmware = is.readAllBytes(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + device.open(); + device.claimInterface(0); + + byte[] cmd = new byte[]{1}; + device.controlTransferOut(new UsbControlTransfer(UsbRequestType.VENDOR, UsbRecipient.DEVICE, 0xa0, 0xe600, 0x0000), cmd); + + final int len = firmware.length; + int offset = 0; + while (offset < len) { + int n = Math.min(len - offset, 0x1000); + byte[] chunk = Arrays.copyOfRange(firmware, offset, offset + n); + device.controlTransferOut(new UsbControlTransfer(UsbRequestType.VENDOR, UsbRecipient.DEVICE, 0xa0, offset, 0x0000), chunk); + offset += n; + } + + cmd = new byte[]{0}; + device.controlTransferOut(new UsbControlTransfer(UsbRequestType.VENDOR, UsbRecipient.DEVICE, 0xa0, 0xe600, 0x0000), cmd); + + device.close(); + DeviceMonitor.instance().awaitDevice(false); + + System.out.println("Waiting for device to reconnect..."); + + DeviceMonitor.instance().awaitDevice(true); + sleep(200); + + var optionalDevice = Usb.findDevice(VID, PID); + if (optionalDevice.isEmpty()) + throw new IllegalStateException("no logic analyzer connected"); + device = optionalDevice.get(); + if (device.getManufacturer() == null) + throw new IllegalStateException("firmware upload failed"); + + System.out.println("Device is ready"); + } + + void sleep(long milliseconds) { + while (true) { + try { + //noinspection BusyWait + Thread.sleep(milliseconds); // NOSONAR + return; + } catch (InterruptedException _) { + // ignore and try again + } + } + } + + + static class DeviceMonitor { + + private static DeviceMonitor singleInstance; + + private final Lock deviceLock = new ReentrantLock(); + private final Condition deviceConnected = deviceLock.newCondition(); + private boolean isDeviceConnected; + + static synchronized DeviceMonitor instance() { + if (singleInstance == null) { + singleInstance = new DeviceMonitor(); + singleInstance.start(); + } + return singleInstance; + } + + private DeviceMonitor() { + } + + private void start() { + Usb.setOnDeviceConnected(dev -> onDeviceConnected(dev, true)); + Usb.setOnDeviceDisconnected(dev -> onDeviceConnected(dev, false)); + isDeviceConnected = Usb.findDevice(VID, PID).isPresent(); + } + + private void onDeviceConnected(UsbDevice device, boolean connected) { + if (device.getVendorId() == VID && device.getProductId() == PID) { + try { + deviceLock.lock(); + isDeviceConnected = connected; + deviceConnected.signalAll(); + + } finally { + deviceLock.unlock(); + } + } + } + + void awaitDevice(boolean connected) { + try { + deviceLock.lock(); + while (isDeviceConnected != connected) + deviceConnected.awaitUninterruptibly(); + + } finally { + deviceLock.unlock(); + } + } + } +} diff --git a/java-does-usb/src/test/java/net/codecrete/usb/special/MonitorDevices.java b/java-does-usb/src/test/java/net/codecrete/usb/special/MonitorDevices.java new file mode 100644 index 00000000..ebf0df0f --- /dev/null +++ b/java-does-usb/src/test/java/net/codecrete/usb/special/MonitorDevices.java @@ -0,0 +1,62 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.special; + +import net.codecrete.usb.Usb; +import net.codecrete.usb.UsbControlTransfer; +import net.codecrete.usb.UsbDevice; +import net.codecrete.usb.UsbRecipient; +import net.codecrete.usb.UsbRequestType; + +import java.io.IOException; + +/** + * Test program that communicates with the device as soon as it has been plugged in. + *

+ * Quit with Ctrl-C or whatever stops a program on your platform. + *

+ */ +public class MonitorDevices { + + public static void main(String[] args) throws IOException { + Usb.setOnDeviceConnected(device -> { + System.out.println("Connected: " + device.toString()); + talkToTestDevice(device); + }); + Usb.setOnDeviceDisconnected(device -> System.out.println("Disconnected: " + device.toString())); + + for (var device : Usb.getDevices()) { + System.out.println("Present: " + device.toString()); + talkToTestDevice(device); + } + System.out.println("Monitoring..."); + + //noinspection ResultOfMethodCallIgnored + System.in.read(); + } + + private static void talkToTestDevice(UsbDevice device) { + if (device.getVendorId() != 0xcafe) + return; // no test device + + int interfaceNumber = device.getProductId() == 0xcea0 ? 2 : 0; + device.open(); + device.claimInterface(interfaceNumber); + var response = device.controlTransferIn( + new UsbControlTransfer(UsbRequestType.VENDOR, UsbRecipient.INTERFACE, + (byte) 0x05, (short) 0, (short) interfaceNumber), + 1 + ); + + if (response.length == 1 || interfaceNumber == response[0]) { + System.out.println("Device responded"); + } else { + System.err.println("Invalid response from device"); + } + } +} diff --git a/java-does-usb/src/test/java/net/codecrete/usb/special/USBSerial.java b/java-does-usb/src/test/java/net/codecrete/usb/special/USBSerial.java new file mode 100644 index 00000000..ec4e4b67 --- /dev/null +++ b/java-does-usb/src/test/java/net/codecrete/usb/special/USBSerial.java @@ -0,0 +1,98 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.special; + +import net.codecrete.usb.Usb; +import net.codecrete.usb.UsbControlTransfer; +import net.codecrete.usb.UsbDevice; +import net.codecrete.usb.UsbDirection; +import net.codecrete.usb.UsbRecipient; +import net.codecrete.usb.UsbRequestType; + +/** + * Interacts with a USB CDC device (serial device) directly, without using the + * standard USB CDC driver. + * + *

+ * This test is aimed at Linux, which requires to unload and restore the + * kernel driver. It will neither work on macOS (the USB CDC driver claims the + * interface with exclusive access) nor on Windows (only devices with the + * WinUSB driver can be opened). + *

+ *

+ * For the test to work on Linux, the user must have sufficient permission to + * access the device at the USB level. So check the permissions under + * /dev/bus/usb/... The permissions of the serial device (/dev/tty...) do not + * apply for this test. + *

+ */ +public class USBSerial { + public static void main(String[] args) { + + for (var device : Usb.getDevices()) { + int commInterfaceNum = getCDCCommInterfaceNum(device); + if (commInterfaceNum >= 0) { + System.out.printf("USB CDC device: %s%n", device); + interact(device, commInterfaceNum); + } + } + } + + static void interact(UsbDevice device, int commInterfaceNum) { + // communication and data interface must have consecutive numbers + int dataInterfaceNum = commInterfaceNum + 1; + + // open device and interfaces + device.open(); + device.claimInterface(commInterfaceNum); + device.claimInterface(dataInterfaceNum); + + // set line coding (9600bps, 8 bit) + byte[] coding = {(byte) 0x80, 0x25, 0, 0, 0, 0, 8}; + device.controlTransferOut( + new UsbControlTransfer(UsbRequestType.CLASS, UsbRecipient.INTERFACE, 0x20, 0, commInterfaceNum), + coding); + + // send some data + int dataOutEp = getDataOutEndpointNum(device, dataInterfaceNum); + byte[] data = {'H', 'e', 'l', 'l', 'o', '\r', '\n'}; + device.transferOut(dataOutEp, data); + + // close device and interfaces + device.releaseInterface(dataInterfaceNum); + device.releaseInterface(commInterfaceNum); + device.close(); + } + + static int getCDCCommInterfaceNum(UsbDevice device) { + // CDC ACM implementations consist of two consecutive interfaces + // with certain class, subclass and protocol codes + int numInterfaces = device.getInterfaces().size(); + if (numInterfaces < 2) + return -1; + + for (int i = 0; i < numInterfaces - 1; i += 1) { + var commIntf = device.getInterface(i).getCurrentAlternate(); + var dataIntf = device.getInterface(i + 1).getCurrentAlternate(); + + if (commIntf.getClassCode() == 2 && commIntf.getSubclassCode() == 2 && commIntf.getProtocolCode() == 1 && dataIntf.getClassCode() == 10) + return i; + } + + return -1; + } + + static int getDataOutEndpointNum(UsbDevice device, int dataInterfaceNum) { + var altInterface = device.getInterface(dataInterfaceNum).getCurrentAlternate(); + for (var endpoint : altInterface.getEndpoints()) { + if (endpoint.getDirection() == UsbDirection.OUT) + return endpoint.getNumber(); + } + return -1; + } +} diff --git a/java-does-usb/src/test/java/net/codecrete/usb/special/Unplug.java b/java-does-usb/src/test/java/net/codecrete/usb/special/Unplug.java new file mode 100644 index 00000000..abba3d4c --- /dev/null +++ b/java-does-usb/src/test/java/net/codecrete/usb/special/Unplug.java @@ -0,0 +1,328 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.special; + +import net.codecrete.usb.TestDeviceConfig; +import net.codecrete.usb.Usb; +import net.codecrete.usb.UsbDevice; +import net.codecrete.usb.UsbException; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import static java.time.Duration.ofSeconds; + +/** + * Test for robustness when USB devices is unplugged during operation. + * + *

+ * Requires use of test device. + *

+ */ +public class Unplug { + private static final Map activeDevices = new HashMap<>(); + + static void main() throws IOException { + System.out.println("Plug and unplug test device multiple times."); + System.out.println("Hit ENTER to exit."); + + Usb.setOnDeviceConnected(Unplug::onPluggedDevice); + Usb.setOnDeviceDisconnected(Unplug::onUnpluggedDevice); + Usb.getDevices().forEach(Unplug::onPluggedDevice); + + //noinspection ResultOfMethodCallIgnored + System.in.read(); + } + + private static void onPluggedDevice(UsbDevice device) { + var config = TestDeviceConfig.getConfig(device); + if (config.isEmpty()) + return; + + var worker = new DeviceWorker(device, config.get()); + activeDevices.put(device, worker); + worker.start(); + } + + private static void onUnpluggedDevice(UsbDevice device) { + var config = TestDeviceConfig.getConfig(device); + if (config.isEmpty()) + return; + + var worker = activeDevices.remove(device); + worker.setDisconnectTime(System.currentTimeMillis()); + worker.join(); + + // test handling of disconnected devices + new Thread(() -> { + sleep(2000); + try { + device.open(); + System.err.println("Device should not be openable after disconnect"); + } catch (UsbException e) { + if (!e.getMessage().contains("disconnected")) + System.err.println("Unexpected error: " + e.getMessage()); + } + }).start(); + } + + @SuppressWarnings({"SameParameterValue", "java:S2925"}) + private static void sleep(long millis) { + try { + Thread.sleep(millis); + + } catch (InterruptedException _) { + Thread.currentThread().interrupt(); + } + } + + static class DeviceWorker { + + private final UsbDevice device; + private final TestDeviceConfig config; + + private final int seed; + + private long disconnectTime; + + private final Map workTracking = new HashMap<>(); + + DeviceWorker(UsbDevice device, TestDeviceConfig config) { + this.device = device; + this.config = config; + this.seed = (int) System.currentTimeMillis(); + } + + void start() { + System.out.println("Device connected"); + + device.open(); + device.claimInterface(config.interfaceNumber()); + + // start loopback sender and receiver + startThread((seed & 1) != 0 ? this::sendLoopbackDataStream : this::sendLoopbackData); + startThread((seed & 2) != 0 ? this::receiveLoopbackDataStream : this::receiveLoopbackData); + + // start echo sender and receiver + if (config.endpointEchoOut() > 0) { + startThread(this::sendEcho); + startThread(this::receiveEcho); + } + } + + private void startThread(Runnable action) { + var thread = new Thread(() -> runAction(action)); + var work = new Work(); + workTracking.put(thread, work); + thread.start(); + } + + void join() { + // wait for threads to finish + for (var thread : workTracking.keySet()) { + try { + boolean terminated = thread.join(ofSeconds(5)); + if (!terminated) + System.err.printf("Thread \"%s\" failed to join within 5s%n", thread.getName()); + + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + device.close(); + + // check achieved work + for (var e : workTracking.entrySet()) { + var work = e.getValue(); + var expectedWork = work.expectedWorkPerSec * 0.001 * (work.finishTime - work.startTime); + if (work.actualWork < expectedWork) + System.err.printf("Thread \"%s\" achieved insufficient work executed. Expected: %.0f, achieved: %d%n", + e.getKey().getName(), expectedWork, work.actualWork); + } + + // check that the threads haven't terminated early + for (var e : workTracking.entrySet()) { + long duration = Math.abs(e.getValue().finishTime - disconnectTime); + if (duration > 500) + System.err.printf("Thread \"%s\" has likely crashed early%n", e.getKey().getName()); + } + + System.out.println("Device disconnected"); + } + + void setDisconnectTime(long time) { + disconnectTime = time; + } + + private synchronized void logFinish() { + var work = workTracking.get(Thread.currentThread()); + work.finishTime = System.currentTimeMillis(); + } + + private void logStart(String operation, long expectedWorkPerSec) { + Thread.currentThread().setName(operation); + var work = workTracking.get(Thread.currentThread()); + work.startTime = System.currentTimeMillis(); + work.expectedWorkPerSec = expectedWorkPerSec; + } + + private void logWork(long amount) { + var work = workTracking.get(Thread.currentThread()); + work.actualWork += amount; + } + + private void runAction(Runnable action) { + try { + action.run(); + } catch (UsbException _) { + logFinish(); + } + } + + private void sendLoopbackData() { + logStart("sending loopback data", 300_000); + var prng = new PRNG(); + var data = new byte[5000]; + //noinspection InfiniteLoopStatement + while (true) { + prng.fill(data); + device.transferOut(config.endpointLoopbackOut(), data, 1000); + logWork(data.length); + } + } + + private void receiveLoopbackData() { + logStart("receiving loopback data", 300_000); + var prng = new PRNG(); + //noinspection InfiniteLoopStatement + while (true) { + byte[] data = device.transferIn(config.endpointLoopbackIn()); + int index = prng.verify(data); + if (index >= 0) + throw new RuntimeException("invalid data received"); + logWork(data.length); + } + } + + private void sendLoopbackDataStream() { + logStart("sending loopback data with output stream", 300_000); + var prng = new PRNG(); + var data = new byte[5000]; + try (var os = device.openOutputStream(config.endpointLoopbackOut())) { + //noinspection InfiniteLoopStatement + while (true) { + prng.fill(data); + os.write(data); + logWork(data.length); + } + } catch (IOException e) { + if (e.getCause() instanceof UsbException usbException) + throw usbException; + throw new RuntimeException(e); + } + } + + private void receiveLoopbackDataStream() { + logStart("receiving loopback data with input stream", 300_000); + var prng = new PRNG(); + try (var is = device.openInputStream(config.endpointLoopbackIn())) { + //noinspection InfiniteLoopStatement + while (true) { + var data = new byte[2000]; + int n = is.read(data); + int index = prng.verify(data, n); + if (index >= 0) + throw new RuntimeException("invalid data received"); + logWork(n); + } + } catch (IOException e) { + if (e.getCause() instanceof UsbException usbException) + throw usbException; + throw new RuntimeException(e); + } + } + + private void sendEcho() { + logStart("sending echo", 7); + var data = new byte[]{0x03, 0x45, 0x73, (byte) 0xb3, (byte) 0x9f, 0x3f, 0x00, 0x6a}; + //noinspection InfiniteLoopStatement + while (true) { + device.transferOut(config.endpointEchoOut(), data); + logWork(1); + sleep(100); + } + } + + private void receiveEcho() { + logStart("receiving echo", 14); + //noinspection InfiniteLoopStatement + while (true) { + device.transferIn(config.endpointEchoIn()); + logWork(1); + } + } + } + + static class Work { + long startTime; + long expectedWorkPerSec; + long actualWork; + long finishTime; + } + + /** + * Pseudo random number generator + */ + static class PRNG { + private int state; + private int nBytes; + private int bits; + + int next() { + int x = state; + x ^= x << 13; + x ^= x >>> 17; + x ^= x << 5; + state = x; + return x; + } + + void fill(byte[] data) { + int len = data.length; + for (int i = 0; i < len; i++) { + if (nBytes == 0) { + bits = next(); + nBytes = 4; + } + data[i] = (byte) bits; + bits >>>= 8; + nBytes--; + } + } + + int verify(byte[] data) { + return verify(data, data.length); + } + + int verify(byte[] data, int len) { + for (int i = 0; i < len; i++) { + if (nBytes == 0) { + bits = next(); + nBytes = 4; + } + if (data[i] != (byte) bits) + return i; + bits >>>= 8; + nBytes--; + } + return -1; + } + } +} diff --git a/java-does-usb/src/test/java/net/codecrete/usb/usbstandard/StringDescriptorTest.java b/java-does-usb/src/test/java/net/codecrete/usb/usbstandard/StringDescriptorTest.java new file mode 100644 index 00000000..d2b638be --- /dev/null +++ b/java-does-usb/src/test/java/net/codecrete/usb/usbstandard/StringDescriptorTest.java @@ -0,0 +1,107 @@ +package net.codecrete.usb.usbstandard; + +import net.codecrete.usb.UsbException; +import org.junit.jupiter.api.Test; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.util.function.Consumer; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class StringDescriptorTest { + + @Test + void validStringDescriptor() { + testDescriptor( + new byte[]{0x0c, 0x03, 'H', 0, 'e', 0, 'l', 0, 'l', 0, 'o', 0}, + stringDescriptor -> { + assertThat(stringDescriptor.isValid()).isTrue(); + assertThat(stringDescriptor.string()).isEqualTo("Hello"); + } + ); + } + + @Test + void truncateTrailingZeros() { + testDescriptor( + new byte[]{0x0e, 0x03, 'H', 0, 'e', 0, 'l', 0, 'l', 0, 'o', 0, 0, 0}, + stringDescriptor -> { + assertThat(stringDescriptor.isValid()).isTrue(); + assertThat(stringDescriptor.string()).isEqualTo("Hello"); + } + ); + } + + @Test + void invalidDescriptorType() { + testDescriptor( + new byte[]{0x0c, 0x04, 'H', 0, 'e', 0, 'l', 0, 'l', 0, 'o', 0}, + stringDescriptor -> assertThat(stringDescriptor.isValid()).isFalse() + ); + } + + @Test + void inconsistentLength() { + testDescriptor( + new byte[]{0x0c, 0x03, 'H', 0, 'e', 0, 'l', 0, 'l', 0, 'o', 0, 0, 0}, + stringDescriptor -> assertThat(stringDescriptor.isValid()).isFalse() + ); + } + + @Test + void inconsistentLengthThrowsException() { + testDescriptor( + new byte[]{0x0c, 0x03, 'H', 0, 'e', 0, 'l', 0, 'l', 0, 'o', 0, 0, 0}, + stringDescriptor -> { + assertThat(stringDescriptor.isValid()).isFalse(); + assertThatThrownBy(stringDescriptor::string) + .isInstanceOf(UsbException.class) + .hasMessage("String descriptor is invalid"); + } + ); + } + + @Test + void oddLength() { + testDescriptor( + new byte[]{0x0d, 0x03, 'H', 0, 'e', 0, 'l', 0, 'l', 0, 'o', 0, 0}, + stringDescriptor -> assertThat(stringDescriptor.isValid()).isFalse() + ); + } + + @Test + void unicodeSurrogates() { + // In theory, USB is stuck with an old Unicode standard and the below is invalid. + // In practice, it will work anyway. + testDescriptor( + new byte[]{0x06, 0x03, 0x3D, (byte) 0xD8, 0x1B, (byte) 0xDE}, + stringDescriptor -> { + assertThat(stringDescriptor.isValid()).isTrue(); + assertThat(stringDescriptor.string()).isEqualTo("\uD83D\uDE1B"); + } + ); + } + + @Test + void invalidUnicodeCharactersAreReplaced() { + testDescriptor( + new byte[]{0x06, 0x03, 'H', 0, 0x1B, (byte) 0xDE}, + stringDescriptor -> { + assertThat(stringDescriptor.isValid()).isTrue(); + assertThat(stringDescriptor.string()).isEqualTo("H�"); + } + ); + } + + private void testDescriptor(byte[] descriptorBytes, Consumer validator) { + try (var arena = Arena.ofConfined()) { + var memorySegment = arena.allocate(descriptorBytes.length); + memorySegment.copyFrom(MemorySegment.ofArray(descriptorBytes)); + + var stringDescriptor = new StringDescriptor(memorySegment); + validator.accept(stringDescriptor); + } + } +} diff --git a/java-does-usb/src/test/resources/fx2lafw-saleae-logic.fw b/java-does-usb/src/test/resources/fx2lafw-saleae-logic.fw new file mode 100644 index 00000000..96167c13 Binary files /dev/null and b/java-does-usb/src/test/resources/fx2lafw-saleae-logic.fw differ diff --git a/java-does-usb/src/test/resources/tinylog.properties b/java-does-usb/src/test/resources/tinylog.properties new file mode 100644 index 00000000..9cf0c142 --- /dev/null +++ b/java-does-usb/src/test/resources/tinylog.properties @@ -0,0 +1,3 @@ +writer = console +writer.format = {date: HH:mm:ss.SSS} {level|size=5} [{class|size=40}] {message} +writer.level = info diff --git a/reference/.vscode/settings.json b/reference/.vscode/settings.json new file mode 100644 index 00000000..de1cf8d1 --- /dev/null +++ b/reference/.vscode/settings.json @@ -0,0 +1,63 @@ +{ + "files.associations": { + "vector": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "*.tcc": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "chrono": "cpp", + "cinttypes": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "compare": "cpp", + "concepts": "cpp", + "condition_variable": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "map": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "random": "cpp", + "ratio": "cpp", + "regex": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "fstream": "cpp", + "initializer_list": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "mutex": "cpp", + "new": "cpp", + "numbers": "cpp", + "ostream": "cpp", + "semaphore": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "thread": "cpp", + "typeinfo": "cpp" + } +} \ No newline at end of file diff --git a/reference/README.md b/reference/README.md index 265d80e6..a8d4b960 100644 --- a/reference/README.md +++ b/reference/README.md @@ -6,3 +6,4 @@ The C++ code contains less error checking, less test cases and does not cover al - [MacOS](macos) (for use with Xcode) - [Windows](windows) (for use with Visual Studio) +- [Linux](linux) (for use with CMake) diff --git a/reference/linux/.vscode/settings.json b/reference/linux/.vscode/settings.json index 9eac4d83..753dfa42 100644 --- a/reference/linux/.vscode/settings.json +++ b/reference/linux/.vscode/settings.json @@ -53,6 +53,10 @@ "stop_token": "cpp", "streambuf": "cpp", "thread": "cpp", - "typeinfo": "cpp" + "typeinfo": "cpp", + "set": "cpp", + "fstream": "cpp", + "iomanip": "cpp", + "sstream": "cpp" } } \ No newline at end of file diff --git a/reference/linux/CMakeLists.txt b/reference/linux/CMakeLists.txt index 925a2428..8cad5236 100644 --- a/reference/linux/CMakeLists.txt +++ b/reference/linux/CMakeLists.txt @@ -9,11 +9,17 @@ find_package(Threads REQUIRED) set(SOURCES assertion.cpp assertion.hpp + blocking_queue.hpp + config_parser.cpp config_parser.hpp + configuration.cpp configuration.hpp main.cpp + prng.cpp prng.hpp scope.hpp + speed_test.cpp speed_test.hpp tests.cpp tests.hpp usb_device.cpp usb_device.hpp usb_error.cpp usb_error.hpp + usb_iostream.cpp usb_iostream.hpp usb_registry.cpp usb_registry.hpp ) diff --git a/reference/linux/assertion.cpp b/reference/linux/assertion.cpp index 4e3111b5..eed1265f 100644 --- a/reference/linux/assertion.cpp +++ b/reference/linux/assertion.cpp @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code common for Linux / macOS / Windows // #include "assertion.hpp" @@ -16,7 +16,7 @@ static void failed(const char* check, const char* message) { if (message == nullptr) message = "Check failed"; std::cerr << message << ": " << check << std::endl; - throw new check_failed_error(); + throw check_failed_error(); } void assert_equals(int expected, int actual, const char* message) { diff --git a/reference/linux/assertion.hpp b/reference/linux/assertion.hpp index 1d763e13..75d089fd 100644 --- a/reference/linux/assertion.hpp +++ b/reference/linux/assertion.hpp @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code common for Linux / macOS / Windows // #pragma once diff --git a/reference/linux/blocking_queue.hpp b/reference/linux/blocking_queue.hpp new file mode 100644 index 00000000..4b2a8b49 --- /dev/null +++ b/reference/linux/blocking_queue.hpp @@ -0,0 +1,70 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#pragma once + +#include +#include +#include + + +/** + * Blocking FIFO queue for passing work from one thread to another. + * + * The queue is unbounded. + */ +template +class blocking_queue { +public: + /** + * Indicates if the queue is empty. + * + * @return 'true' if the queue is empty, 'false' if the queue contains elements + */ + bool empty() const { + std::lock_guard lock(guard); + return queue.empty(); + } + + /** + * Adds the item to the end of the queue. + * + * @param item item to add + */ + void put(const T& item) { + { + std::lock_guard lock(guard); + queue.push(item); + } + + signal.notify_one(); + } + + /** + * Takes the oldest item from the queue and removes it. + * + * Waits until an item is available. + * + * @return item removed from queue + */ + T take() { + std::unique_lock lock(guard); + while (queue.empty()) + signal.wait(lock); + + T item = queue.front(); + queue.pop(); + return item; + } + +private: + std::queue queue; + mutable std::mutex guard; + std::condition_variable signal; +}; diff --git a/reference/linux/config_parser.cpp b/reference/linux/config_parser.cpp new file mode 100644 index 00000000..66b13ec2 --- /dev/null +++ b/reference/linux/config_parser.cpp @@ -0,0 +1,189 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#include "config_parser.hpp" +#include "usb_error.hpp" +#include + + +// --- USB descriptor types + +enum class usb_descriptor_type : uint8_t { + configuration = 0x02, + string = 0x03, + interface = 0x04, + endpoint = 0x05, + interface_association = 0x0b +}; + + +// --- USB configuration descriptor + +#pragma pack(push, 1) + +struct usb_config_desc { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t bMaxPower; +}; + +#pragma pack(pop) + + +// --- USB interface descriptor + +#pragma pack(push, 1) + +struct usb_interface_desc { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; +}; + +#pragma pack(pop) + + +// --- USB interface association descriptor + +#pragma pack(push, 1) + +struct usb_interface_association_desc { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bFirstInterface; + uint8_t bInterfaceCount; + uint8_t bFunctionClass; + uint8_t bFunctionSubClass; + uint8_t bFunctionProtocol; + uint8_t iFunction; +}; + +#pragma pack(pop) + + +// --- USB endpoint descriptor + +#pragma pack(push, 1) + + struct usb_endpoint_desc { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketSize; + uint8_t bInterval; + }; + +#pragma pack(pop) + + +// --- Configuration parser + + static int peek_desc_length(const uint8_t* desc, int offset) { return desc[offset]; } + static usb_descriptor_type peek_desc_type(const uint8_t* desc, int offset) { return static_cast(desc[offset + 1]); } + + + config_parser::config_parser() + : configuration_value(0) { } + +void config_parser::parse(const uint8_t* config_desc, int desc_len) +{ + const usb_config_desc* header = reinterpret_cast(config_desc); + if (desc_len <= sizeof(usb_config_desc) + || header->bDescriptorType != static_cast(usb_descriptor_type::configuration) + || header->wTotalLength != desc_len) + throw usb_error("Invalid configuration descriptor"); + + configuration_value = header->bConfigurationValue; + + int offset = peek_desc_length(config_desc, 0); + usb_alternate_interface* last_alternate = nullptr; + + while (offset + 2 < desc_len) { + + int len = peek_desc_length(config_desc, offset); + usb_descriptor_type type = peek_desc_type(config_desc, offset); + + if (offset + len > desc_len) + break; + + if (type == usb_descriptor_type::interface_association) { + + const usb_interface_association_desc* ia_desc = reinterpret_cast(config_desc + offset); + functions.push_back(usb_composite_function(ia_desc->bFirstInterface, ia_desc->bInterfaceCount, + ia_desc->bFunctionClass, ia_desc->bFunctionSubClass, ia_desc->bFunctionProtocol)); + last_alternate = nullptr; + + } else if (type == usb_descriptor_type::interface) { + + const usb_interface_desc* intf_desc = reinterpret_cast(config_desc + offset); + int number = intf_desc->bInterfaceNumber; + + // If there is no interface with this number yet, it's a new interface + // and not just an additional alternate interface. + auto intf = get_interface(number); + if (intf == nullptr) { + interfaces.push_back(usb_interface(number)); + intf = &interfaces.back(); + } + + // add alternate interface + last_alternate = intf->add_alternate(usb_alternate_interface(intf_desc->bAlternateSetting, + intf_desc->bInterfaceClass, intf_desc->bInterfaceSubClass, intf_desc->bInterfaceProtocol)); + + // If there is no function for this interface, there was not preceeding IAD. + // So create a new function with a single interface. + if (get_function(intf->number()) == nullptr) + functions.push_back(usb_composite_function(intf->number(), 1, last_alternate->class_code(), last_alternate->subclass_code(), last_alternate->protocol_code())); + + } else if (type == usb_descriptor_type::endpoint) { + + if (last_alternate == nullptr) + throw usb_error("invalid configuration descriptor"); + + const usb_endpoint_desc* ep_desc = reinterpret_cast(config_desc + offset); + last_alternate->add_endpoint(usb_endpoint(ep_desc->bEndpointAddress & 0x7f, static_cast(ep_desc->bEndpointAddress & 0x80), + static_cast(ep_desc->bmAttributes & 0x03), ep_desc->wMaxPacketSize)); + } + + offset += len; + } + + if (offset != desc_len) + throw usb_error("invalid configuration descriptor"); +} + +usb_interface* config_parser::get_interface(int number) { + auto iter = std::find_if(interfaces.begin(), interfaces.end(), [number](const usb_interface& itf) { return itf.number() == number; }); + if (iter == interfaces.end()) + return nullptr; + + return &*iter; +} + +usb_composite_function* config_parser::get_function(int intf_number) { + auto iter = std::find_if(functions.begin(), functions.end(), [intf_number](const usb_composite_function& f) { + return intf_number >= f.first_interface() && intf_number < f.first_interface() + f.num_interfaces(); + }); + if (iter == functions.end()) + return nullptr; + + return &*iter; +} diff --git a/reference/linux/config_parser.hpp b/reference/linux/config_parser.hpp new file mode 100644 index 00000000..04f6615c --- /dev/null +++ b/reference/linux/config_parser.hpp @@ -0,0 +1,31 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#pragma once + +#include +#include "configuration.hpp" + +/// +/// Parses a USB configuration descriptor. +/// +class config_parser { +public: + config_parser(); + void parse(const uint8_t* config_desc, int desc_len); + + uint8_t configuration_value; + + std::vector interfaces; + std::vector functions; + +private: + usb_interface* get_interface(int number); + usb_composite_function* get_function(int intf_number); +}; diff --git a/reference/linux/configuration.cpp b/reference/linux/configuration.cpp new file mode 100644 index 00000000..3db7235c --- /dev/null +++ b/reference/linux/configuration.cpp @@ -0,0 +1,64 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#include "configuration.hpp" + +// --- usb_endpoint + +usb_endpoint::usb_endpoint(int number, usb_direction direction, usb_transfer_type transfer_type, int packet_size) + : number_(number), direction_(direction), transfer_type_(transfer_type), packet_size_(packet_size) { } + +usb_endpoint usb_endpoint::invalid(-1, usb_direction::out, usb_transfer_type::bulk, 0); + + +// --- usb_alternate_interface + +usb_alternate_interface::usb_alternate_interface(int number, int class_code, int subclass_code, int protocol_code) + : number_(number), class_code_(class_code), subclass_code_(subclass_code), protocol_code_(protocol_code) { } + +void usb_alternate_interface::add_endpoint(usb_endpoint&& endpoint) { + endpoints_.push_back(std::move(endpoint)); +} + + +usb_alternate_interface usb_alternate_interface::invalid(-1, 0, 0, 0); + + +// --- usb_interface + +usb_interface::usb_interface(int number) + : number_(number), is_claimed_(false), alternate_index_(0) { } + +const usb_alternate_interface& usb_interface::alternate() const { + if (alternate_index_ < 0 || alternate_index_ >= alternates_.size()) + return usb_alternate_interface::invalid; + return alternates_[alternate_index_]; +} + +void usb_interface::set_claimed(bool claimed) { + is_claimed_ = claimed; +} + + +usb_alternate_interface* usb_interface::add_alternate(usb_alternate_interface&& alternate) { + alternates_.push_back(std::move(alternate)); + return &alternates_.back(); +} + +void usb_interface::set_alternate(int index) { + alternate_index_ = index; +} + +usb_interface usb_interface::invalid(-1); + + +// --- usb_composite_function + +usb_composite_function::usb_composite_function(int first_interface, int num_interfaces, int class_code, int subclass_code, int protocol_code) + : first_interface_(first_interface), num_interfaces_(num_interfaces), class_code_(class_code), subclass_code_(subclass_code), protocol_code_(protocol_code) { } diff --git a/reference/linux/configuration.hpp b/reference/linux/configuration.hpp new file mode 100644 index 00000000..5f3dedc9 --- /dev/null +++ b/reference/linux/configuration.hpp @@ -0,0 +1,162 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#pragma once + +#include +#include + +/// USB endpoint direction +enum class usb_direction : uint8_t { + /// Direction OUT: host to device + out = 0x00, + /// Direction IN: device to host + in = 0x80 +}; + +/// USB endpoint transfer type +enum class usb_transfer_type : uint8_t { + /// Control transfer + control = 0x00, + /// Isochronous transfer + isochronous = 0x01, + /// Bulk transfer + bulk = 0x02, + /// Interrupt transfer + interrupt = 0x03 +}; + +/// +/// USB endpoint +/// +struct usb_endpoint { +public: + /// Endpoint number + int number() const { return number_; } + /// Endpoint direction + usb_direction direction() const { return direction_; } + /// Endpoint transfer type + usb_transfer_type transfer_type() const { return transfer_type_; } + /// Maximum packet size + int packet_size() const { return packet_size_; } + /// Indicates if this endpoint is valid (or a return value indicating an error) + bool is_valid() const { return number_ >= 0; } + +private: + usb_endpoint(int number, usb_direction direction, usb_transfer_type transfer_type, int packet_size); + + int number_; + usb_direction direction_; + usb_transfer_type transfer_type_; + int packet_size_; + + static usb_endpoint invalid; + + friend class config_parser; + friend class usb_device; +}; + +/// +/// USB alternate interface +/// +struct usb_alternate_interface { +public: + /// Alternate number + int number() const { return number_; } + /// Interface class code + int class_code() const { return class_code_; } + /// Interface subclass code + int subclass_code() const { return subclass_code_; } + /// Interface protocol code + int protocol_code() const { return protocol_code_; } + /// List of endpoints + const std::vector& endpoints() const { return endpoints_; } + /// Indicates if this alternate interface is valid (or a return value indicating an error) + bool is_valid() const { return number_ >= 0; } + +private: + usb_alternate_interface(int number, int class_code, int subclass_code, int protocol_code); + void add_endpoint(usb_endpoint&& endpoint); + + int number_; + int class_code_; + int subclass_code_; + int protocol_code_; + std::vector endpoints_; + + static usb_alternate_interface invalid; + + friend class config_parser; + friend struct usb_interface; +}; + +/// +/// USB interface +/// +struct usb_interface { +public: + /// Interface number + int number() const { return number_; } + /// Indicates if interface has been claimed + bool is_claimed() const { return is_claimed_; } + /// Currently selected alternate interface + const usb_alternate_interface& alternate() const; + /// Indicates if this interface is valid (or a return value indicating an error) + bool is_valid() const { return number_ >= 0; } + /// List of all alternate interfaces of this interfaces + const std::vector& alternates() const { return alternates_; } + +private: + usb_interface(int number); + void set_claimed(bool claimed); + usb_alternate_interface* add_alternate(usb_alternate_interface&& alternate); + void set_alternate(int index); + + int number_; + bool is_claimed_; + int alternate_index_; + std::vector alternates_; + + static usb_interface invalid; + + friend class config_parser; + friend class usb_device; +}; + +/// +/// USB composite function +/// +/// For a composite USB device, the composite function describes a single function. +/// A compsite function consists of a single or multiple consecutive interfaces. +/// +/// +struct usb_composite_function { +public: + /// Number of first interface + int first_interface() const { return first_interface_; } + /// Number of interfaces + int num_interfaces() const { return num_interfaces_; } + /// Function class code + int class_code() const { return class_code_; } + /// Function subclass code + int subclass_code() const { return subclass_code_; } + /// Function protocol code + int protocol_code() const { return protocol_code_; } + +private: + usb_composite_function(int first_interface, int num_interfaces, int class_code, int subclass_code, int protocol_code); + + int first_interface_; + int num_interfaces_; + int class_code_; + int subclass_code_; + int protocol_code_; + + friend class config_parser; +}; diff --git a/reference/linux/main.cpp b/reference/linux/main.cpp index 64e85de5..cbb3747c 100644 --- a/reference/linux/main.cpp +++ b/reference/linux/main.cpp @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code common for Linux / macOS / Windows // #include "tests.hpp" diff --git a/reference/linux/prng.cpp b/reference/linux/prng.cpp new file mode 100644 index 00000000..c9cd0b1a --- /dev/null +++ b/reference/linux/prng.cpp @@ -0,0 +1,80 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#include "prng.hpp" + +prng::prng(uint32_t init) : state(init), nbytes(0), bits(0) {} + +void prng::reset(uint32_t init) +{ + state = init; + nbytes = 0; + bits = 0; +} + +uint32_t prng::next() +{ + uint32_t x = state; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + state = x; + return x; +} + +void prng::fill(uint8_t *buf, int len) +{ + for (int i = 0; i < len; i++) + { + if (nbytes == 0) + { + bits = next(); + nbytes = 4; + } + buf[i] = bits; + bits >>= 8; + nbytes--; + } +} + +void prng::fill(std::vector& buf, int len) +{ + if (len == -1 || len > buf.size()) + len = static_cast(buf.size()); + + fill(buf.data(), len); +} + +int prng::verify(const uint8_t *buf, int len) +{ + for (int i = 0; i < len; i++) + { + if (nbytes == 0) + { + bits = next(); + nbytes = 4; + } + + if (buf[i] != (uint8_t)bits) + return i; + + bits >>= 8; + nbytes--; + } + + return -1; +} + +int prng::verify(const std::vector &buf, int len) +{ + if (len == -1 || len > buf.size()) + len = static_cast(buf.size()); + + return verify(buf.data(), len); +} diff --git a/reference/linux/prng.hpp b/reference/linux/prng.hpp new file mode 100644 index 00000000..5214dfa6 --- /dev/null +++ b/reference/linux/prng.hpp @@ -0,0 +1,72 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#pragma once + +#include +#include + +/** + * Pseudo Random Number Generator + */ +struct prng +{ + /** + * Constructs a new instance + * @param init initial value + */ + prng(uint32_t init); + + /** + * Returns the next pseudo random value + * @return pseudo random value + */ + uint32_t next(); + + /** + * Fills the buffer with pseudo random data + * @param buf buffer receiving the random data + * @param len length of the buffer (in bytes) + */ + void fill(uint8_t *buf, int len); + + /** + * Fills the buffer with pseudo random data + * @param buf buffer receiving the random data + * @param len number of bytes to fill (-1 for entire buffer) + */ + void fill(std::vector& buf, int len = -1); + + /** + * Verifies that the passed data matches the next bytes of the sequence. + * @param buf buffer with data to verify + * @param len length of the buffer (in bytes) + * @return -1 if they match, otherwise the position of the difference + */ + int verify(const uint8_t *buf, int len); + + /** + * Verifies that the passed data matches the next bytes of the sequence. + * @param buf buffer with data to verify + * @param len number of bytes to verify (-1 for entire buffer) + * @return -1 if they match, otherwise the position of the difference + */ + int verify(const std::vector& buf, int len = -1); + + /** + * Resets the generator to its initial state. + * @param init initial value + */ + void reset(uint32_t init); + +private: + uint32_t state; + int nbytes; + uint32_t bits; +}; diff --git a/reference/linux/scope.hpp b/reference/linux/scope.hpp index 53e3e39f..85998dba 100644 --- a/reference/linux/scope.hpp +++ b/reference/linux/scope.hpp @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code common for Linux / macOS / Windows // #pragma once diff --git a/reference/linux/speed_test.cpp b/reference/linux/speed_test.cpp new file mode 100644 index 00000000..ab60607b --- /dev/null +++ b/reference/linux/speed_test.cpp @@ -0,0 +1,142 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#include "speed_test.hpp" + +#include "prng.hpp" +#include "usb_error.hpp" + +#include +#include +#include +#include + + +static constexpr uint32_t PRNG_INIT = 0x7b; + + +speed_test::speed_test(usb_device_ptr device, int ep_out, int ep_in) +: device_(device), ep_out_(ep_out), ep_in_(ep_in) { } + +void speed_test::run(int num_bytes) { + reset_buffers(); + start_measurement(); + + std::thread sender(&speed_test::transmit, this, num_bytes / 2); + bool successful = receive(num_bytes / 2); + sender.join(); + + if (successful) + stop_measurement(); +} + +bool speed_test::transmit(int num_bytes) { + + prng seq(PRNG_INIT); + + std::vector buf(2000); + int pos = 0; + + auto is_ptr = device_->open_output_stream(ep_out_); + std::ostream& os = *is_ptr; + + while (num_bytes > 0) { + int n = std::min(num_bytes, static_cast(buf.size())); + try { + seq.fill(buf, n); + os.write(reinterpret_cast(buf.data()), n); + + } catch (usb_error& error) { + std::cerr << std::endl << "ERROR: " << error.what() << " (writing at pos " << pos << ")" << std::endl; + return false; + } + + num_bytes -= n; + pos += n; + update_progress(n); + } + + return true; +} + +bool speed_test::receive(int num_bytes) { + + prng seq(PRNG_INIT); + int pos = 0; + std::vector data; + + auto is_ptr = device_->open_input_stream(ep_in_); + std::istream& is = *is_ptr; + + while (num_bytes > 0) { + int n; + try { + data.resize(2000); + is.read(reinterpret_cast(data.data()), data.capacity()); + n = static_cast(is.gcount()); + if (n <= 0) + break; + data.resize(n); + + int p = seq.verify(data, n); + if (p != -1) { + std::cerr << std::endl << "Invalid data received at pos " << (pos + p) << std::endl; + return false; + } + + } catch (usb_error& error) { + std::cerr << std::endl << "ERROR: " << error.what() << " (reading at pos " << pos << ")" << std::endl; + return false; + } + + num_bytes -= n; + pos += n; + + update_progress(n); + } + + if (num_bytes != 0) { + std::cerr << std::endl << "ERROR: EOF encountered after " << pos << " bytes" << std::endl; + } + + return true; +} + +void speed_test::reset_buffers() { + usb_control_request request_set_value_no_data = { 0 }; + request_set_value_no_data.bmRequestType = usb_control_request::request_type(usb_request_type::direction_out, + usb_request_type::type_vendor, usb_request_type::recipient_interface); + request_set_value_no_data.bRequest = 0x04; + request_set_value_no_data.wIndex = 0; // interface number + device_->control_transfer(request_set_value_no_data); + +} + + +// --- throughput measurement --- + +void speed_test::start_measurement() { + start_time = std::chrono::high_resolution_clock::now(); + processed_bytes = 0; +} + +void speed_test::update_progress(int n) { + std::lock_guard lock(progress_mutex); + processed_bytes += n; +} + +int speed_test::stop_measurement() { + auto end_time = std::chrono::high_resolution_clock::now(); + std::chrono::duration dur = end_time - start_time; + double thr = processed_bytes / dur.count(); + + std::cout << "Throughput: " << std::fixed << std::setprecision(1) << thr << " kByte/s" << std::endl; + + return (int)(thr * 1000); +} diff --git a/reference/linux/speed_test.hpp b/reference/linux/speed_test.hpp new file mode 100644 index 00000000..99f19277 --- /dev/null +++ b/reference/linux/speed_test.hpp @@ -0,0 +1,38 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#pragma once + +#include "usb_device.hpp" + +#include +#include + +class speed_test { +public: + speed_test(usb_device_ptr device, int ep_out, int ep_in); + void run(int num_bytes); + +private: + void reset_buffers(); + bool transmit(int num_bytes); + bool receive(int num_bytes); + + void start_measurement(); + void update_progress(int n); + int stop_measurement(); + + usb_device_ptr device_; + int ep_out_; + int ep_in_; + + std::chrono::time_point start_time; + int processed_bytes; + std::mutex progress_mutex; +}; diff --git a/reference/linux/tests.cpp b/reference/linux/tests.cpp index 6b53c83f..9113dc53 100644 --- a/reference/linux/tests.cpp +++ b/reference/linux/tests.cpp @@ -4,11 +4,12 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code common for Linux / macOS / Windows // #include "tests.hpp" #include "assertion.hpp" +#include "speed_test.hpp" #include #include @@ -20,14 +21,14 @@ using random_ushort_engine = std::independent_bits_engine< void tests::run() { registry.start(); - for (auto device : registry.get_devices()) { + for (auto& device : registry.get_devices()) { std::cout << "Present: " << device->description() << std::endl; } registry.set_on_device_connected([this](auto device) { on_device_connected(device); }); registry.set_on_device_disconnected([this](auto device) { on_device_disconnected(device); }); - for (auto device : registry.get_devices()) + for (auto& device : registry.get_devices()) on_device(device); std::cout << "Press RETURN to quit" << std::endl; @@ -36,53 +37,53 @@ void tests::run() { } void tests::test_current_device() { - std::cout << "Found test device" << std::endl; - test_device->open(); - test_device->claim_interface(0); - - test_control_transfers(); - test_bulk_transfers(); - - test_device->release_interface(); - test_device->close(); - std::cout << "Test completed" << std::endl; + try { + std::cout << "Found test device" << std::endl; + test_device->open(); + test_device->claim_interface(0); + + test_control_transfers(); + test_bulk_transfers(); + test_speed(); + + test_device->release_interface(0); + test_device->close(); + std::cout << "Test completed" << std::endl; + } + catch (const std::exception& e) { + std::cout << "Test failed: " << e.what() << std::endl; + } } void tests::test_control_transfers() { - usb_control_request request_set_value_no_data = { - .bmRequestType = usb_control_request::request_type(usb_request_type::direction_out, - usb_request_type::type_vendor, - usb_request_type::recipient_interface), - .bRequest = 0x01, - .wValue = 0x9a41, - .wIndex = 0, // interface number - .wLength = 0 - }; + usb_control_request request_set_value_no_data = { 0 }; + request_set_value_no_data.bmRequestType = usb_control_request::request_type(usb_request_type::direction_out, + usb_request_type::type_vendor, usb_request_type::recipient_interface); + request_set_value_no_data.bRequest = 0x01; + request_set_value_no_data.wValue = 0x9a41; + request_set_value_no_data.wIndex = 0; // interface number + request_set_value_no_data.wLength = 0; test_device->control_transfer(request_set_value_no_data); - usb_control_request request_get_data = { - .bmRequestType = usb_control_request::request_type(usb_request_type::direction_in, - usb_request_type::type_vendor, - usb_request_type::recipient_interface), - .bRequest = 0x03, - .wValue = 0, - .wIndex = 0, // interface number - .wLength = 4 - }; + usb_control_request request_get_data = { 0 }; + request_get_data.bmRequestType = usb_control_request::request_type(usb_request_type::direction_in, + usb_request_type::type_vendor, usb_request_type::recipient_interface); + request_get_data.bRequest = 0x03; + request_get_data.wValue = 0; + request_get_data.wIndex = 0; // interface number + request_get_data.wLength = 4; auto data = test_device->control_transfer_in(request_get_data); std::vector expected_data{ 0x41, 0x9a, 0x00, 0x00 }; assert_equals(expected_data, data); std::vector sent_value{ 0x83, 0x03, 0xda, 0x3d }; - usb_control_request request_set_value_data = { - .bmRequestType = usb_control_request::request_type(usb_request_type::direction_out, - usb_request_type::type_vendor, - usb_request_type::recipient_interface), - .bRequest = 0x02, - .wValue = 0, - .wIndex = 0, // interface number - .wLength = static_cast(sent_value.size()) - }; + usb_control_request request_set_value_data = { 0 }; + request_set_value_data.bmRequestType = usb_control_request::request_type(usb_request_type::direction_out, + usb_request_type::type_vendor, usb_request_type::recipient_interface); + request_set_value_data.bRequest = 0x02; + request_set_value_data.wValue = 0; + request_set_value_data.wIndex = 0; // interface number + request_set_value_data.wLength = static_cast(sent_value.size()); test_device->control_transfer_out(request_set_value_data, sent_value); data = test_device->control_transfer_in(request_get_data); @@ -106,9 +107,9 @@ void tests::test_loopback(int num_bytes) { // read in separate thread std::thread reader([this, &rx_data, num_bytes]() { - int bytes_read = 0; + size_t bytes_read = 0; while (bytes_read < num_bytes) { - auto data = test_device->transfer_in(2, 64); + auto data = test_device->transfer_in(2); rx_data.insert(rx_data.end(), data.begin(), data.end()); bytes_read += data.size(); } @@ -131,6 +132,13 @@ void tests::test_loopback(int num_bytes) { assert_equals(random_data, rx_data); } +void tests::test_speed() { + int packet_size = test_device->get_endpoint(usb_direction::out, 1).packet_size(); + + speed_test test(test_device, 1, 2); + test.run(packet_size == 512 ? 20000000 : 2000000); +} + void tests::on_device(usb_device_ptr device) { if (is_test_device(device)) { test_device = device; @@ -156,7 +164,6 @@ std::vector tests::random_bytes(int num) { return std::vector(p, p + num); } - bool tests::is_test_device(usb_device_ptr device) { return device->vendor_id() == 0xcafe && device->product_id() == 0xceaf; } diff --git a/reference/linux/tests.hpp b/reference/linux/tests.hpp index 992de2b9..11cf328f 100644 --- a/reference/linux/tests.hpp +++ b/reference/linux/tests.hpp @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code common for Linux / macOS / Windows // #pragma once @@ -20,6 +20,7 @@ class tests { void test_current_device(); void test_control_transfers(); void test_bulk_transfers(); + void test_speed(); void test_loopback(int num_bytes); diff --git a/reference/linux/usb_device.cpp b/reference/linux/usb_device.cpp index 711ad252..7d70c05d 100644 --- a/reference/linux/usb_device.cpp +++ b/reference/linux/usb_device.cpp @@ -4,12 +4,15 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code for Linux // #include "usb_device.hpp" +#include "usb_iostream.hpp" +#include "usb_registry.hpp" #include "usb_error.hpp" #include "scope.hpp" +#include "config_parser.hpp" #include #include @@ -17,9 +20,12 @@ #include #include +#include -usb_device::usb_device(const char* path, int vendor_id, int product_id) -: path_(path), fd_(-1), claimed_interface_(-1), vendor_id_(vendor_id), product_id_(product_id) { + +usb_device::usb_device(usb_registry* registry, const char* path, int vendor_id, int product_id) +: registry_(registry), path_(path), fd_(-1), uses_urbs_(false), vendor_id_(vendor_id), product_id_(product_id) { + read_descriptor(); } usb_device::~usb_device() { @@ -27,6 +33,32 @@ usb_device::~usb_device() { close(); } +void usb_device::set_product_strings(const char* manufacturer, const char* product, const char* serial_number) { + manufacturer_ = manufacturer != nullptr ? manufacturer : ""; + product_ = product != nullptr ? product : ""; + serial_number_ = serial_number != nullptr ? serial_number : ""; +} + +void usb_device::read_descriptor() { + // read device and configuration descriptor + std::vector descriptors{}; + { + std::ifstream file(path(), std::ios::binary); + uint8_t buf[256]; + while (!file.eof()) { + file.read(reinterpret_cast(buf), sizeof(buf)); + if (file.bad()) + throw usb_error("failed to read device and configuration descriptor"); + descriptors.insert(descriptors.end(), buf, buf + file.gcount()); + } + } + + int config_desc_offset = descriptors[0]; + config_parser parser{}; + parser.parse(descriptors.data() + config_desc_offset, descriptors.size() - config_desc_offset); + interfaces_ = std::move(parser.interfaces); +} + std::string usb_device::description() const { const char* fmt = "VID: 0x%04x, PID: 0x%04x, manufacturer: %s, product: %s, serial: %s"; @@ -41,6 +73,31 @@ std::string usb_device::description() const { return desc; } +const std::vector& usb_device::interfaces() const { + return interfaces_; +} + +const usb_interface& usb_device::get_interface(int interface_number) const { + for (const usb_interface& intf : interfaces_) { + if (intf.number() == interface_number) + return intf; + } + + return usb_interface::invalid; +} + +const usb_endpoint& usb_device::get_endpoint(usb_direction direction, int endpoint_number) const { + for (const usb_interface& intf : interfaces_) { + for (const usb_endpoint& ep : intf.alternate().endpoints()) { + if (ep.direction() == direction && ep.number() == endpoint_number) + return ep; + } + } + + return usb_endpoint::invalid; +} + + bool usb_device::is_open() const { return fd_ >= 0; } @@ -58,9 +115,16 @@ void usb_device::close() { if (!is_open()) return; - if (claimed_interface_ >= 0) - release_interface(); + if (uses_urbs_) { + registry_->remove_async_fd(fd_); + uses_urbs_ = false; + } + + for (auto& intf : interfaces_) + intf.set_claimed(false); + claimed_interfaces_.clear(); + int ret = ::close(fd_); fd_ = -1; if (ret != 0) @@ -68,37 +132,67 @@ void usb_device::close() { } void usb_device::claim_interface(int interface_number) { + + if (!is_open()) + throw usb_error("device is not open"); + + usb_interface* intf = get_intf_ptr(interface_number); + if (intf == nullptr) + throw usb_error("no such interface"); + + if (intf->is_claimed()) + throw usb_error("interface has already been claimed"); + + usbdevfs_disconnect_claim dc = { + .interface = static_cast(interface_number), + .flags = USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER, + .driver = "usbfs" + }; - if (claimed_interface_ >= 0) - throw usb_error("an interface has already been claimed"); - - int result = ioctl(fd_, USBDEVFS_CLAIMINTERFACE, &interface_number); + int result = ioctl(fd_, USBDEVFS_DISCONNECT_CLAIM, &dc); if (result < 0) - usb_error::throw_error("Failed to claim interface 0"); + usb_error::throw_error("Failed to claim interface"); - claimed_interface_ = interface_number; + claimed_interfaces_.insert(interface_number); + intf->set_claimed(true); } -void usb_device::release_interface() { - if (claimed_interface_ < 0) - throw usb_error("no interface has been claimed"); - - int result = ioctl(fd_, USBDEVFS_RELEASEINTERFACE, &claimed_interface_); +void usb_device::release_interface(int interface_number) { + + if (!is_open()) + throw usb_error("device is not open"); + + usb_interface* intf = get_intf_ptr(interface_number); + if (intf == nullptr) + throw usb_error("no such interface"); + + if (!intf->is_claimed()) + throw usb_error("interface has not been claimed"); + + int result = ioctl(fd_, USBDEVFS_RELEASEINTERFACE, &interface_number); if (result < 0) usb_error::throw_error("Failed to release interface"); - claimed_interface_ = -1; + intf->set_claimed(false); + claimed_interfaces_.erase(interface_number); + + usbdevfs_ioctl cmd = { + .ifno = interface_number, + .ioctl_code = USBDEVFS_CONNECT, + .data = nullptr + }; + ioctl(fd_, USBDEVFS_IOCTL, &cmd); } -std::vector usb_device::transfer_in(int endpoint_number, int data_len, int timeout) { - if (claimed_interface_ < 0) - throw usb_error("no interface has been claimed"); +std::vector usb_device::transfer_in(int endpoint_number, int timeout) { + + auto ep = check_endpoint(usb_direction::in, endpoint_number); - std::vector data(data_len); + std::vector data(ep->packet_size()); - struct usbdevfs_bulktransfer transfer = {0}; + usbdevfs_bulktransfer transfer = {0}; transfer.ep = endpoint_number + 128; - transfer.len = data_len; + transfer.len = ep->packet_size(); transfer.timeout = timeout; transfer.data = data.data(); @@ -110,11 +204,13 @@ std::vector usb_device::transfer_in(int endpoint_number, int data_len, return data; } -void usb_device::transfer_out(int endpoint_number, const std::vector& data, int timeout) { - if (claimed_interface_ < 0) - throw usb_error("no interface has been claimed"); +void usb_device::transfer_out(int endpoint_number, const std::vector& data, int len, int timeout) { + if (len < 0 || len > data.size()) + len = static_cast(data.size()); + + check_endpoint(usb_direction::out, endpoint_number); - struct usbdevfs_bulktransfer transfer = {0}; + usbdevfs_bulktransfer transfer = {0}; transfer.ep = endpoint_number; transfer.len = data.size(); transfer.timeout = timeout; @@ -130,7 +226,7 @@ int usb_device::control_transfer_core(const usb_control_request &request, uint8_ if (!is_open()) throw usb_error("USB device is not open"); - struct usbdevfs_ctrltransfer ctrl_request = {0}; + usbdevfs_ctrltransfer ctrl_request = {0}; ctrl_request.bRequestType = request.bmRequestType; ctrl_request.bRequest = request.bRequest; ctrl_request.wValue = request.wValue; @@ -169,3 +265,55 @@ std::vector usb_device::control_transfer_in(const usb_control_request& data.resize(request.wLength); return data; } + +usb_interface* usb_device::get_intf_ptr(int number) { + for (auto& intf : interfaces_) + if (intf.number() == number) + return &intf; + return nullptr; +} + +const usb_endpoint* usb_device::check_endpoint(usb_direction direction, int endpoint_number) { + + if (!is_open()) + throw usb_error("device is not open"); + + for (auto& intf : interfaces_) { + for (auto& ep : intf.alternate().endpoints()) { + if (ep.direction() == direction && ep.number() == endpoint_number) { + if (!intf.is_claimed()) + throw usb_error("interface has not been claimed"); + if (ep.transfer_type() != usb_transfer_type::bulk && ep.transfer_type() != usb_transfer_type::interrupt) + throw usb_error("invalid endpoint transfer type for operation"); + return &ep; + } + } + } + + throw usb_error("no such endpoint"); +} + +std::unique_ptr usb_device::open_input_stream(int endpoint_number) { + return std::unique_ptr(new usb_istream(registry_->get_shared_ptr(this), endpoint_number)); +} + +std::unique_ptr usb_device::open_output_stream(int endpoint_number) { + return std::unique_ptr(new usb_ostream(registry_->get_shared_ptr(this), endpoint_number)); +} + +void usb_device::submit_urb(usbdevfs_urb* urb) { + if (!uses_urbs_) { + uses_urbs_ = true; + registry_->add_async_fd(fd_); + } + + int result = ioctl(fd_, USBDEVFS_SUBMITURB, urb); + if (result < 0) + usb_error::throw_error("Failed to submit URB"); +} + +void usb_device::cancel_urb(usbdevfs_urb* urb) { + int result = ioctl(fd_, USBDEVFS_DISCARDURB, urb); + if (result < 0 && errno != EINVAL) + usb_error::throw_error("Failed to cancel URB"); +} diff --git a/reference/linux/usb_device.hpp b/reference/linux/usb_device.hpp index 30a27445..625186ba 100644 --- a/reference/linux/usb_device.hpp +++ b/reference/linux/usb_device.hpp @@ -4,16 +4,23 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code for Linux // #pragma once +#include #include +#include #include #include #include +#include "configuration.hpp" + + +typedef std::function usb_io_callback; + /** * USB control request type. @@ -65,6 +72,8 @@ struct usb_control_request { } }; +class usb_registry; +struct usbdevfs_urb; /** * USB device. @@ -87,7 +96,26 @@ class usb_device { std::string serial_number() const { return serial_number_; } /// Descriptive string including VID, PID, manufacturer, product name and serial number std::string description() const; + /// List of interfaces + const std::vector& interfaces() const; + + /** + * Get the USB interface. + * + * @param interface_number interface number + * @return interface or `nullptr` if no such interface exists + */ + const usb_interface& get_interface(int interface_number) const; + /** + * Get a USB endpoint. + * + * @param direction endpoint direction + * @param endpoint_number endpoint number (between 1 and 127) + * @return endpoint or `nullptr` if endpoint does not exist + */ + const usb_endpoint& get_endpoint(usb_direction direction, int endpoint_number) const; + /// Opens the device for communication void open(); @@ -100,25 +128,22 @@ class usb_device { /** * Claims an interface * - * A single interface can be claimed. - * * @param interface_number interface number */ void claim_interface(int interface_number); /** - * Releases the claimed interface. + * Releases a claimed interface. + * + * @param interface_number interface number */ - void release_interface(); + void release_interface(int interface_number); /** * Receives data from a bulk or interrupt endpoint. * * The amount of bytes read will be influced by the underlying USB packets. - * If a short packet is sent, the function will return after having read fewer bytes - * than specified. The function will fail if a bigger packet has been received than - * will fit into the given buffer. So the specified data length should be big enough for - * the maximum packet size (64 bytes for full-speed USB). + * It can be 0 (if the device sends a ZLP) up to the maximum packet size. * * The timeout specifies the maximum time it may take to complete the operation. * If the operation does not complete within that time, the function returns after reading @@ -127,11 +152,10 @@ class usb_device { * Interrupt endpoints do not support timeouts. Thus, 0 has to be specified. * * @param endpoint_number endpoint number (between 1 and 127) - * @param data_len maximum length to read (in bytes) * @param timeout timeout (in ms, 0 for no timeout) * @return received data */ - std::vector transfer_in(int endpoint_number, int data_len, int timeout = 0); + std::vector transfer_in(int endpoint_number, int timeout = 0); /** * Transmits data to a bulk or interrupt endpoint. @@ -144,9 +168,10 @@ class usb_device { * * @param endpoint_number endpoint number (between 1 and 127) * @param data data to transmit + * @param len data length, in bytes (-1 for entire data vector) * @param timeout timeout (in ms, 0 for no timeout) */ - void transfer_out(int endpoint_number, const std::vector& data, int timeout = 0); + void transfer_out(int endpoint_number, const std::vector& data, int len = -1, int timeout = 0); /** * Send a control request with no Data phase. @@ -184,14 +209,46 @@ class usb_device { */ std::vector control_transfer_in(const usb_control_request& request, int timeout = 0); + /** + * Open a new input stream for a bulk endpoint. + * + * The input stream is optimized for maximum throughput. + * + * Do not use the input stream concurrently with other transfer operations on the same endpoint. The input stream + * buffers data for high throughput. When the stream is closed, any data in the buffers will be lost. + * + * @param endpoint_number endpoint number (between 1 and 127) + */ + std::unique_ptr open_input_stream(int endpoint_number); + + /** + * Open a new output stream for a bulk endpoint. + * + * The output stream is optimized for maximum throughput. + * + * Do not use the output stream concurrently with other transfer operations on the same endpoint. + * + * @param endpoint_number endpoint number (between 1 and 127) + */ + std::unique_ptr open_output_stream(int endpoint_number); + private: - usb_device(const char* path, int vendor_id, int product_id); + usb_device(usb_registry* registry, const char* path, int vendor_id, int product_id); + void set_product_strings(const char* manufacturer, const char* product, const char* serial_number); const char* path() const { return path_.c_str(); } int control_transfer_core(const usb_control_request& request, uint8_t* data, int timeout); + void read_descriptor(); + usb_interface* get_intf_ptr(int number); + const usb_endpoint* check_endpoint(usb_direction direction, int endpoint_number); + void submit_urb(usbdevfs_urb* urb); + void cancel_urb(usbdevfs_urb* urb); + usb_registry* registry_; std::string path_; int fd_; - int claimed_interface_; + bool uses_urbs_; + std::set claimed_interfaces_; + std::vector interfaces_; int product_id_; int vendor_id_; @@ -200,6 +257,8 @@ class usb_device { std::string serial_number_; friend class usb_registry; + friend class usb_istreambuf; + friend class usb_ostreambuf; }; typedef std::shared_ptr usb_device_ptr; diff --git a/reference/linux/usb_error.cpp b/reference/linux/usb_error.cpp index 8228ba2d..1844bd54 100644 --- a/reference/linux/usb_error.cpp +++ b/reference/linux/usb_error.cpp @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code for Linux // #include "usb_error.hpp" @@ -30,6 +30,11 @@ void usb_error::throw_error(const char* message) { throw usb_error(message, errno); } +void usb_error::check(int code, const char* message) { + if (code != 0) + throw usb_error(message, code); +} + std::string usb_error::full_message(const char* message, int code) { if (code == 0) return message; diff --git a/reference/linux/usb_error.hpp b/reference/linux/usb_error.hpp index f1a846db..f5218417 100644 --- a/reference/linux/usb_error.hpp +++ b/reference/linux/usb_error.hpp @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code for Linux // #pragma once @@ -18,8 +18,8 @@ class usb_error : public std::exception { /** * Creates a new instance. * - *@param message error message - *@param code a Mach error code, or 0 if no Mach code is available + * @param message error message + * @param code a Mach error code, or 0 if no Mach code is available */ usb_error(const char* message, int code = 0) noexcept; @@ -36,6 +36,14 @@ class usb_error : public std::exception { */ static void throw_error(const char* message); + /** + * Throws a USB error exception if the code indicates an error. + * + * @param code error code + * @param message additional information for the error message + */ + static void check(int code, const char* message); + private: static std::string full_message(const char* message, int code); diff --git a/reference/linux/usb_iostream.cpp b/reference/linux/usb_iostream.cpp new file mode 100644 index 00000000..727e82be --- /dev/null +++ b/reference/linux/usb_iostream.cpp @@ -0,0 +1,226 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code for Linux +// + +#include "usb_iostream.hpp" +#include "usb_error.hpp" +#include + +// --- usb_istreambuf --- + +usb_istreambuf::usb_istreambuf(usb_device_ptr device, int endpoint_number) +: device(device), endpoint_number(endpoint_number), is_closed(false), num_outstanding_requests(0) { + + setg(nullptr, nullptr, nullptr); + + buffer_size = 8 * device->get_endpoint(usb_direction::in, endpoint_number).packet_size(); + + // allocate the buffers and submit requests + memset(requests, 0, sizeof(requests)); + for (int i = 0; i < max_outstanding_requests; i++) { + transfer_request* request = &requests[i]; + request->io_completion = [this, request]() { on_completed(request); }; + request->urb.type = USBDEVFS_URB_TYPE_BULK; + request->urb.endpoint = 128 + endpoint_number; + request->urb.buffer = new uint8_t[buffer_size]; + request->urb.buffer_length = buffer_size; + request->urb.usercontext = &request->io_completion; + + if (i == 0) + current_request = request; + else + submit_transfer(request); + } +} + +usb_istreambuf::~usb_istreambuf() { + close(); + + // free buffers + for (int i = 0; i < max_outstanding_requests; i++) + delete[] reinterpret_cast(requests[i].urb.buffer); +} + +void usb_istreambuf::close() { + is_closed = true; + setg(nullptr, nullptr, nullptr); + + // cancel outstanding requests + for (int i = 0; i < max_outstanding_requests; i++) { + if (!requests[i].is_completed) + device->cancel_urb(&requests[i].urb); + } + + // wait until completion handlers have been called + while (num_outstanding_requests > 0) + wait_for_request_completion(); +} + +void usb_istreambuf::submit_transfer(transfer_request* request) { + request->is_completed = false; + device->submit_urb(&request->urb); + num_outstanding_requests += 1; +} + +void usb_istreambuf::on_completed(transfer_request* request) { + request->is_completed = true; + completed_request_queue.put(request); +} + +usb_istreambuf::transfer_request* usb_istreambuf::wait_for_request_completion() { + transfer_request* request = completed_request_queue.take(); + num_outstanding_requests -= 1; + return request; +} + +usb_istreambuf::int_type usb_istreambuf::underflow() { + if (is_closed) + return traits_type::eof(); + + if (gptr() < egptr()) + return traits_type::to_int_type(*gptr()); + + // loop until non-ZLP has been received + do { + submit_transfer(current_request); + + current_request = wait_for_request_completion(); + usb_error::check(current_request->result_code(), "error reading from USB endpoint"); + + char* buf = reinterpret_cast(current_request->urb.buffer); + int size = current_request->result_size(); + setg(buf, buf, buf + size); + + } while (current_request->result_size() == 0); + + return traits_type::to_int_type(*gptr()); +} + + +// --- usb_ostreambuf --- + +usb_ostreambuf::usb_ostreambuf(usb_device_ptr device, int endpoint_number) +: device(device), endpoint_number(endpoint_number), is_closed(false), needs_zlp(false) { + + packet_size = device->get_endpoint(usb_direction::out, endpoint_number).packet_size(); + buffer_size = 1 * packet_size; + + // create requests + memset(requests, 0, sizeof(requests)); + for (int i = 0; i < max_outstanding_requests; i++) { + transfer_request* request = &requests[i]; + request->io_completion = [this, request](void) { on_completed(request); }; + request->urb.type = USBDEVFS_URB_TYPE_BULK; + request->urb.endpoint = endpoint_number; + request->urb.buffer = new uint8_t[buffer_size]; + request->urb.usercontext = &request->io_completion; + } + + fill_queue(); +} + +usb_ostreambuf::~usb_ostreambuf() { + // flush all data + sync(); + + // free buffers + for (int i = 0; i < max_outstanding_requests; i++) + delete[] reinterpret_cast(requests[i].urb.buffer); +} + +void usb_ostreambuf::fill_queue() { + for (int i = 1; i < max_outstanding_requests; i++) + available_request_queue.put(&requests[i]); + + // configure stream buffer for first request + current_request = &requests[0]; + char* buf = reinterpret_cast(current_request->urb.buffer); + setp(buf, buf + buffer_size); +} + +int usb_ostreambuf::sync() { + // submit request if there is any data in the current buffer + auto size = pptr() - pbase(); + if (size > 0) + submit_transfer((int)size); + + // send a zero-length packet if required + if (needs_zlp) + submit_transfer(0); + + // Wait until all buffers have been transmitted by removing them from the + // queue and reinserting them. One request is the current request. + // So the queue only contains max_outstanding_requests - 1 requests. + for (int i = 0; i < max_outstanding_requests - 1; i++) + wait_for_available_transfer(); + + fill_queue(); + + return 0; +} + +int usb_ostreambuf::overflow (int c) { + // submit request + auto size = pptr() - pbase(); + submit_transfer((int)size); + + // insert char + if (c != traits_type::eof()) { + *pptr() = (char)c; + pbump(1); + } + + return c; +} + +void usb_ostreambuf::submit_transfer(int size) { + current_request->urb.buffer_length = size; + device->submit_urb(¤t_request->urb); + needs_zlp = size == packet_size; + + current_request = wait_for_available_transfer(); + + // configure stream buffer + char* buf = reinterpret_cast(current_request->urb.buffer); + setp(buf, buf + buffer_size); +} + +void usb_ostreambuf::on_completed(transfer_request* request) { + available_request_queue.put(request); +} + +usb_ostreambuf::transfer_request* usb_ostreambuf::wait_for_available_transfer() { + auto request = available_request_queue.take(); + + // check for error + usb_error::check(request->result_code(), "error writing to USB endpoint"); + + return request; +} + + +// --- usb_istream --- + +usb_istream::usb_istream(usb_device_ptr device, int ep_num) + : std::istream(new usb_istreambuf(device, ep_num)) {} + +usb_istream::~usb_istream() { + // delete buffer + delete rdbuf(); +} + + +// --- usb_ostream --- + +usb_ostream::usb_ostream(usb_device_ptr device, int ep_num) + : std::ostream(new usb_ostreambuf(device, ep_num)) {} + +usb_ostream::~usb_ostream() { + // delete buffer + delete rdbuf(); +} diff --git a/reference/linux/usb_iostream.hpp b/reference/linux/usb_iostream.hpp new file mode 100644 index 00000000..e64c6efc --- /dev/null +++ b/reference/linux/usb_iostream.hpp @@ -0,0 +1,166 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code for Linux +// + +#pragma once + +#include +#include +#include +#include +#include "usb_device.hpp" +#include "blocking_queue.hpp" + +/** + * Input stream buffer for USB bulk or interrupt endpoint. + * + * The stream buffer is internally used by an input stream. It submits multiple asynchronous IO requests to + * achieve maximum throughput. + */ +class usb_istreambuf : public std::streambuf { +public: + /// Constructor + usb_istreambuf(usb_device_ptr device, int ep_num); + /// Destructor + virtual ~usb_istreambuf(); + +protected: + /// Called when the internal buffer has no further data to read. + virtual int_type underflow(); + +private: + /// Transfer request + struct transfer_request { + /// USB request buffer + usbdevfs_urb urb; + /// IO completion handler + usb_io_callback io_completion; + /// indicates if the request has completed + bool is_completed; + + int result_code() { + return urb.status; + } + + int result_size() { + return urb.actual_length; + } + }; + + void submit_transfer(transfer_request* request); + void close(); + void on_completed(transfer_request* request); + transfer_request* wait_for_request_completion(); + + /// Maximum number of concurrently outstanding requests + static constexpr int max_outstanding_requests = 4; + + /// USB device + usb_device_ptr device; + /// endpoint number + int endpoint_number; + /// Indicates that this stream buffer is closed + bool is_closed; + /// buffer size + int buffer_size; + /// transfer requests + transfer_request requests[max_outstanding_requests]; + /// queue with completed requests + blocking_queue completed_request_queue; + /// number of outstanding requests (requests pending with OS and requests in queue) + int num_outstanding_requests; + /// current request being read from + transfer_request* current_request;}; + +/** + * Output stream buffer for USB bulk or interrupt endpoint. + * + * The stream buffer is internally used by an output stream. It submits multiple asynchronous IO requests to + * achieve maximum throughput. + */ +class usb_ostreambuf : public std::streambuf { +public: + /// Constructor + usb_ostreambuf(usb_device_ptr device, int ep_num); + /// Destructor + virtual ~usb_ostreambuf(); + + virtual int sync(); + +protected: + /// Called when the internal buffer has no space left to add more data. + virtual int overflow (int c); + +private: + /// Transfer request + struct transfer_request { + /// USB request buffer + usbdevfs_urb urb; + /// IO completion handler + usb_io_callback io_completion; + /// indicates if the request has completed + bool is_completed; + + int result_code() { + return urb.status; + } + + int result_size() { + return urb.actual_length; + } + }; + + void fill_queue(); + void submit_transfer(int size); + transfer_request* wait_for_available_transfer(); + void on_completed(transfer_request* request); + + /// Maximum number of concurrently outstanding requests + static constexpr int max_outstanding_requests = 4; + + /// USB device + usb_device_ptr device; + /// endpoint number + int endpoint_number; + /// Indicates that this stream buffer is closed + bool is_closed; + /// Indicates if a zero-length packet is required + bool needs_zlp; + /// packet size + int packet_size; + /// buffer size + int buffer_size; + /// transfer requests + transfer_request requests[max_outstanding_requests]; + /// queue with available requests + blocking_queue available_request_queue; + /// current request being written to + transfer_request* current_request; +}; + +/** + * Input stream for reading from a USB bulk endpoint + */ +class usb_istream : public std::istream { +public: + /// Constructor + usb_istream(usb_device_ptr device, int ep_num); + /// Destructor + ~usb_istream(); +}; + +/** + * Output stream for writing to a USB bulk endpoint + */ +class usb_ostream : public std::ostream { +public: + /// Constructor + usb_ostream(usb_device_ptr device, int ep_num); + /// Destructor + ~usb_ostream(); +}; diff --git a/reference/linux/usb_registry.cpp b/reference/linux/usb_registry.cpp index 784ea172..6ed42b4f 100644 --- a/reference/linux/usb_registry.cpp +++ b/reference/linux/usb_registry.cpp @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code for Linux // #include "usb_registry.hpp" @@ -14,8 +14,11 @@ #include #include +#include #include #include +#include +#include #include #include @@ -26,15 +29,21 @@ #include usb_registry::usb_registry() -: wake_event_fd(-1), - on_connected_callback(nullptr), on_disconnected_callback(nullptr), - is_device_list_ready(false) { +: monitor_wake_event_fd(-1), + on_connected_callback(nullptr), on_disconnected_callback(nullptr), is_device_list_ready(false), + async_io_epoll_fd(-1), async_io_exit_event_fd(-1) { } usb_registry::~usb_registry() { - eventfd_write(wake_event_fd, 1); + eventfd_write(monitor_wake_event_fd, 1); monitor_thread.join(); - ::close(wake_event_fd); + ::close(monitor_wake_event_fd); + + if (async_io_exit_event_fd != -1) { + eventfd_write(async_io_exit_event_fd, 999999); + async_io_thread.join(); + ::close(async_io_exit_event_fd); + } } std::vector usb_registry::get_devices() { @@ -50,8 +59,8 @@ void usb_registry::set_on_device_disconnected(std::function usb_registry::create_device(udev_device* udev_dev) { if (vendor_id == 0 || product_id == 0) return nullptr; - return std::shared_ptr(new usb_device(path, vendor_id, product_id)); + std::shared_ptr device(new usb_device(this, path, vendor_id, product_id)); + device->set_product_strings( + udev_device_get_sysattr_value(udev_dev, "manufacturer"), + udev_device_get_sysattr_value(udev_dev, "product"), + udev_device_get_sysattr_value(udev_dev, "serial") + ); + return device; +} + +std::shared_ptr usb_registry::get_shared_ptr(usb_device* device) { + auto it = std::find_if(devices.cbegin(), devices.cend(), [device](auto dev) { return dev.get() == device; }); + if (it == devices.cend()) + return nullptr; + + return *it; +} + +void usb_registry::async_io_run() { + + while (true) { + struct epoll_event events[5]; + int ret = epoll_wait(async_io_epoll_fd, &events[0], 5, -1); + if (ret < 0) { + if (errno == EINTR) + continue; + usb_error::throw_error("internal error (epoll)"); + } + + for (int i = 0; i < ret; i++) { + int fd = events[i].data.fd; + if (fd == async_io_exit_event_fd) + return; + reap_urbs(fd); + } + } +} + +void usb_registry::reap_urbs(int fd) { + while (true) { + usbdevfs_urb* urb = nullptr; + int ret = ioctl(fd, USBDEVFS_REAPURB, &urb); + if (ret < 0) { + if (errno == EAGAIN) + return; // no more pending URBs + if (errno == ENODEV) + return; // ignore, device might have been closed + usb_error::throw_error("internal error (reap URB)"); + } + + auto completion = reinterpret_cast(urb->usercontext); + (*completion)(); + } +} + +void usb_registry::add_async_fd(int fd) { + int expected_request; + + { + std::lock_guard lock(async_io_mutex); + + // start background thread if needed + if (async_io_exit_event_fd == -1) { + async_io_exit_event_fd = eventfd(0, 0); + if (async_io_exit_event_fd < 0) + usb_error::throw_error("internal error(eventfd)"); + + async_io_epoll_fd = epoll_create(4); + if (async_io_epoll_fd < 0) + usb_error::throw_error("internal error(epoll_create)"); + + epoll_event event = {0}; + event.events = EPOLLIN; + event.data.fd = async_io_exit_event_fd; + int ret = epoll_ctl(async_io_epoll_fd, EPOLL_CTL_ADD, async_io_exit_event_fd, &event); + if (ret < 0) + usb_error::throw_error("internal error(epoll_ctl)"); + + async_io_thread = std::thread(&usb_registry::async_io_run, this); + } + } + + epoll_event event = {0}; + event.events = EPOLLOUT; + event.data.fd = fd; + int ret = epoll_ctl(async_io_epoll_fd, EPOLL_CTL_ADD, fd, &event); + if (ret < 0) + usb_error::throw_error("internal error(epoll_ctl)"); +} + + +void usb_registry::remove_async_fd(int fd) { + epoll_event event = {0}; + int ret = epoll_ctl(async_io_epoll_fd, EPOLL_CTL_DEL, fd, &event); + if (ret < 0) + usb_error::throw_error("internal error(epoll_ctl)"); } diff --git a/reference/linux/usb_registry.hpp b/reference/linux/usb_registry.hpp index c17384d2..ff251eaf 100644 --- a/reference/linux/usb_registry.hpp +++ b/reference/linux/usb_registry.hpp @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code for Linux // #pragma once @@ -21,7 +21,6 @@ struct udev; struct udev_device; - /** * Registry of connected USB devices. */ @@ -50,7 +49,13 @@ class usb_registry { void on_device_connected(udev_device* udev_dev); void on_device_disconnected(udev_device* udev_dev); + void async_io_run(); + void add_async_fd(int fd); + void remove_async_fd(int fd); + void reap_urbs(int fd); + std::shared_ptr create_device(udev_device* udev_dev); + std::shared_ptr get_shared_ptr(usb_device* device); std::vector devices; @@ -58,10 +63,15 @@ class usb_registry { std::function on_disconnected_callback; std::thread monitor_thread; - - int wake_event_fd; - + int monitor_wake_event_fd; bool is_device_list_ready; std::mutex monitor_mutex; std::condition_variable monitor_condition; + + std::thread async_io_thread; + std::mutex async_io_mutex; + int async_io_epoll_fd; + int async_io_exit_event_fd; + + friend usb_device; }; diff --git a/reference/macos/USB/assertion.cpp b/reference/macos/USB/assertion.cpp index 4e3111b5..c978c8cb 100644 --- a/reference/macos/USB/assertion.cpp +++ b/reference/macos/USB/assertion.cpp @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code common for Linux / macOS / Windows // #include "assertion.hpp" @@ -16,7 +16,7 @@ static void failed(const char* check, const char* message) { if (message == nullptr) message = "Check failed"; std::cerr << message << ": " << check << std::endl; - throw new check_failed_error(); + throw check_failed_error(); } void assert_equals(int expected, int actual, const char* message) { @@ -40,7 +40,7 @@ void assert_equals(unsigned long expected, unsigned long actual, const char* mes char buf[50]; snprintf(buf, sizeof(buf), "expected: %lud, actual: %lud", expected, actual); failed(buf, message); - } + } } void assert_equals(const std::vector& expected, const std::vector& actual, const char* message) { diff --git a/reference/macos/USB/assertion.hpp b/reference/macos/USB/assertion.hpp index 09968407..75d089fd 100644 --- a/reference/macos/USB/assertion.hpp +++ b/reference/macos/USB/assertion.hpp @@ -4,11 +4,12 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code common for Linux / macOS / Windows // #pragma once +#include #include #include diff --git a/reference/macos/USB/blocking_queue.hpp b/reference/macos/USB/blocking_queue.hpp new file mode 100644 index 00000000..154b0230 --- /dev/null +++ b/reference/macos/USB/blocking_queue.hpp @@ -0,0 +1,69 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#pragma once + +#include +#include + + +/** + * Blocking FIFO queue for passing work from one thread to another. + * + * The queue is unbounded. + */ +template +class blocking_queue { +public: + /** + * Indicates if the queue is empty. + * + * @return 'true' if the queue is empty, 'false' if the queue contains elements + */ + bool empty() const { + std::lock_guard lock(guard); + return queue.empty(); + } + + /** + * Adds the item to the end of the queue. + * + * @param item item to add + */ + void put(const T& item) { + { + std::lock_guard lock(guard); + queue.push(item); + } + + signal.notify_one(); + } + + /** + * Takes the oldest item from the queue and removes it. + * + * Waits until an item is available. + * + * @return item removed from queue + */ + T take() { + std::unique_lock lock(guard); + while (queue.empty()) + signal.wait(lock); + + T item = queue.front(); + queue.pop(); + return item; + } + +private: + std::queue queue; + mutable std::mutex guard; + std::condition_variable signal; +}; diff --git a/reference/macos/USB/config_parser.cpp b/reference/macos/USB/config_parser.cpp new file mode 100644 index 00000000..2d75f475 --- /dev/null +++ b/reference/macos/USB/config_parser.cpp @@ -0,0 +1,188 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#include "config_parser.hpp" +#include "usb_error.hpp" + + +// --- USB descriptor types + +enum class usb_descriptor_type : uint8_t { + configuration = 0x02, + string = 0x03, + interface = 0x04, + endpoint = 0x05, + interface_association = 0x0b +}; + + +// --- USB configuration descriptor + +#pragma pack(push, 1) + +struct usb_config_desc { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t bMaxPower; +}; + +#pragma pack(pop) + + +// --- USB interface descriptor + +#pragma pack(push, 1) + +struct usb_interface_desc { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; +}; + +#pragma pack(pop) + + +// --- USB interface association descriptor + +#pragma pack(push, 1) + +struct usb_interface_association_desc { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bFirstInterface; + uint8_t bInterfaceCount; + uint8_t bFunctionClass; + uint8_t bFunctionSubClass; + uint8_t bFunctionProtocol; + uint8_t iFunction; +}; + +#pragma pack(pop) + + +// --- USB endpoint descriptor + +#pragma pack(push, 1) + + struct usb_endpoint_desc { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketSize; + uint8_t bInterval; + }; + +#pragma pack(pop) + + +// --- Configuration parser + + static int peek_desc_length(const uint8_t* desc, int offset) { return desc[offset]; } + static usb_descriptor_type peek_desc_type(const uint8_t* desc, int offset) { return static_cast(desc[offset + 1]); } + + + config_parser::config_parser() + : configuration_value(0) { } + +void config_parser::parse(const uint8_t* config_desc, int desc_len) +{ + const usb_config_desc* header = reinterpret_cast(config_desc); + if (desc_len <= sizeof(usb_config_desc) + || header->bDescriptorType != static_cast(usb_descriptor_type::configuration) + || header->wTotalLength != desc_len) + throw usb_error("Invalid configuration descriptor"); + + configuration_value = header->bConfigurationValue; + + int offset = peek_desc_length(config_desc, 0); + usb_alternate_interface* last_alternate = nullptr; + + while (offset + 2 < desc_len) { + + int len = peek_desc_length(config_desc, offset); + usb_descriptor_type type = peek_desc_type(config_desc, offset); + + if (offset + len > desc_len) + break; + + if (type == usb_descriptor_type::interface_association) { + + const usb_interface_association_desc* ia_desc = reinterpret_cast(config_desc + offset); + functions.push_back(usb_composite_function(ia_desc->bFirstInterface, ia_desc->bInterfaceCount, + ia_desc->bFunctionClass, ia_desc->bFunctionSubClass, ia_desc->bFunctionProtocol)); + last_alternate = nullptr; + + } else if (type == usb_descriptor_type::interface) { + + const usb_interface_desc* intf_desc = reinterpret_cast(config_desc + offset); + int number = intf_desc->bInterfaceNumber; + + // If there is no interface with this number yet, it's a new interface + // and not just an additional alternate interface. + auto intf = get_interface(number); + if (intf == nullptr) { + interfaces.push_back(usb_interface(number)); + intf = &interfaces.back(); + } + + // add alternate interface + last_alternate = intf->add_alternate(usb_alternate_interface(intf_desc->bAlternateSetting, + intf_desc->bInterfaceClass, intf_desc->bInterfaceSubClass, intf_desc->bInterfaceProtocol)); + + // If there is no function for this interface, there was not preceeding IAD. + // So create a new function with a single interface. + if (get_function(intf->number()) == nullptr) + functions.push_back(usb_composite_function(intf->number(), 1, last_alternate->class_code(), last_alternate->subclass_code(), last_alternate->protocol_code())); + + } else if (type == usb_descriptor_type::endpoint) { + + if (last_alternate == nullptr) + throw usb_error("invalid configuration descriptor"); + + const usb_endpoint_desc* ep_desc = reinterpret_cast(config_desc + offset); + last_alternate->add_endpoint(usb_endpoint(ep_desc->bEndpointAddress & 0x7f, static_cast(ep_desc->bEndpointAddress & 0x80), + static_cast(ep_desc->bmAttributes & 0x03), ep_desc->wMaxPacketSize)); + } + + offset += len; + } + + if (offset != desc_len) + throw usb_error("invalid configuration descriptor"); +} + +usb_interface* config_parser::get_interface(int number) { + auto iter = std::find_if(interfaces.begin(), interfaces.end(), [number](const usb_interface& itf) { return itf.number() == number; }); + if (iter == interfaces.end()) + return nullptr; + + return &*iter; +} + +usb_composite_function* config_parser::get_function(int intf_number) { + auto iter = std::find_if(functions.begin(), functions.end(), [intf_number](const usb_composite_function& f) { + return intf_number >= f.first_interface() && intf_number < f.first_interface() + f.num_interfaces(); + }); + if (iter == functions.end()) + return nullptr; + + return &*iter; +} diff --git a/reference/macos/USB/config_parser.hpp b/reference/macos/USB/config_parser.hpp new file mode 100644 index 00000000..04f6615c --- /dev/null +++ b/reference/macos/USB/config_parser.hpp @@ -0,0 +1,31 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#pragma once + +#include +#include "configuration.hpp" + +/// +/// Parses a USB configuration descriptor. +/// +class config_parser { +public: + config_parser(); + void parse(const uint8_t* config_desc, int desc_len); + + uint8_t configuration_value; + + std::vector interfaces; + std::vector functions; + +private: + usb_interface* get_interface(int number); + usb_composite_function* get_function(int intf_number); +}; diff --git a/reference/macos/USB/configuration.cpp b/reference/macos/USB/configuration.cpp new file mode 100644 index 00000000..3db7235c --- /dev/null +++ b/reference/macos/USB/configuration.cpp @@ -0,0 +1,64 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#include "configuration.hpp" + +// --- usb_endpoint + +usb_endpoint::usb_endpoint(int number, usb_direction direction, usb_transfer_type transfer_type, int packet_size) + : number_(number), direction_(direction), transfer_type_(transfer_type), packet_size_(packet_size) { } + +usb_endpoint usb_endpoint::invalid(-1, usb_direction::out, usb_transfer_type::bulk, 0); + + +// --- usb_alternate_interface + +usb_alternate_interface::usb_alternate_interface(int number, int class_code, int subclass_code, int protocol_code) + : number_(number), class_code_(class_code), subclass_code_(subclass_code), protocol_code_(protocol_code) { } + +void usb_alternate_interface::add_endpoint(usb_endpoint&& endpoint) { + endpoints_.push_back(std::move(endpoint)); +} + + +usb_alternate_interface usb_alternate_interface::invalid(-1, 0, 0, 0); + + +// --- usb_interface + +usb_interface::usb_interface(int number) + : number_(number), is_claimed_(false), alternate_index_(0) { } + +const usb_alternate_interface& usb_interface::alternate() const { + if (alternate_index_ < 0 || alternate_index_ >= alternates_.size()) + return usb_alternate_interface::invalid; + return alternates_[alternate_index_]; +} + +void usb_interface::set_claimed(bool claimed) { + is_claimed_ = claimed; +} + + +usb_alternate_interface* usb_interface::add_alternate(usb_alternate_interface&& alternate) { + alternates_.push_back(std::move(alternate)); + return &alternates_.back(); +} + +void usb_interface::set_alternate(int index) { + alternate_index_ = index; +} + +usb_interface usb_interface::invalid(-1); + + +// --- usb_composite_function + +usb_composite_function::usb_composite_function(int first_interface, int num_interfaces, int class_code, int subclass_code, int protocol_code) + : first_interface_(first_interface), num_interfaces_(num_interfaces), class_code_(class_code), subclass_code_(subclass_code), protocol_code_(protocol_code) { } diff --git a/reference/macos/USB/configuration.hpp b/reference/macos/USB/configuration.hpp new file mode 100644 index 00000000..5f3dedc9 --- /dev/null +++ b/reference/macos/USB/configuration.hpp @@ -0,0 +1,162 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#pragma once + +#include +#include + +/// USB endpoint direction +enum class usb_direction : uint8_t { + /// Direction OUT: host to device + out = 0x00, + /// Direction IN: device to host + in = 0x80 +}; + +/// USB endpoint transfer type +enum class usb_transfer_type : uint8_t { + /// Control transfer + control = 0x00, + /// Isochronous transfer + isochronous = 0x01, + /// Bulk transfer + bulk = 0x02, + /// Interrupt transfer + interrupt = 0x03 +}; + +/// +/// USB endpoint +/// +struct usb_endpoint { +public: + /// Endpoint number + int number() const { return number_; } + /// Endpoint direction + usb_direction direction() const { return direction_; } + /// Endpoint transfer type + usb_transfer_type transfer_type() const { return transfer_type_; } + /// Maximum packet size + int packet_size() const { return packet_size_; } + /// Indicates if this endpoint is valid (or a return value indicating an error) + bool is_valid() const { return number_ >= 0; } + +private: + usb_endpoint(int number, usb_direction direction, usb_transfer_type transfer_type, int packet_size); + + int number_; + usb_direction direction_; + usb_transfer_type transfer_type_; + int packet_size_; + + static usb_endpoint invalid; + + friend class config_parser; + friend class usb_device; +}; + +/// +/// USB alternate interface +/// +struct usb_alternate_interface { +public: + /// Alternate number + int number() const { return number_; } + /// Interface class code + int class_code() const { return class_code_; } + /// Interface subclass code + int subclass_code() const { return subclass_code_; } + /// Interface protocol code + int protocol_code() const { return protocol_code_; } + /// List of endpoints + const std::vector& endpoints() const { return endpoints_; } + /// Indicates if this alternate interface is valid (or a return value indicating an error) + bool is_valid() const { return number_ >= 0; } + +private: + usb_alternate_interface(int number, int class_code, int subclass_code, int protocol_code); + void add_endpoint(usb_endpoint&& endpoint); + + int number_; + int class_code_; + int subclass_code_; + int protocol_code_; + std::vector endpoints_; + + static usb_alternate_interface invalid; + + friend class config_parser; + friend struct usb_interface; +}; + +/// +/// USB interface +/// +struct usb_interface { +public: + /// Interface number + int number() const { return number_; } + /// Indicates if interface has been claimed + bool is_claimed() const { return is_claimed_; } + /// Currently selected alternate interface + const usb_alternate_interface& alternate() const; + /// Indicates if this interface is valid (or a return value indicating an error) + bool is_valid() const { return number_ >= 0; } + /// List of all alternate interfaces of this interfaces + const std::vector& alternates() const { return alternates_; } + +private: + usb_interface(int number); + void set_claimed(bool claimed); + usb_alternate_interface* add_alternate(usb_alternate_interface&& alternate); + void set_alternate(int index); + + int number_; + bool is_claimed_; + int alternate_index_; + std::vector alternates_; + + static usb_interface invalid; + + friend class config_parser; + friend class usb_device; +}; + +/// +/// USB composite function +/// +/// For a composite USB device, the composite function describes a single function. +/// A compsite function consists of a single or multiple consecutive interfaces. +/// +/// +struct usb_composite_function { +public: + /// Number of first interface + int first_interface() const { return first_interface_; } + /// Number of interfaces + int num_interfaces() const { return num_interfaces_; } + /// Function class code + int class_code() const { return class_code_; } + /// Function subclass code + int subclass_code() const { return subclass_code_; } + /// Function protocol code + int protocol_code() const { return protocol_code_; } + +private: + usb_composite_function(int first_interface, int num_interfaces, int class_code, int subclass_code, int protocol_code); + + int first_interface_; + int num_interfaces_; + int class_code_; + int subclass_code_; + int protocol_code_; + + friend class config_parser; +}; diff --git a/reference/macos/USB/iokit_helper.cpp b/reference/macos/USB/iokit_helper.cpp index 49512179..8b089e68 100644 --- a/reference/macos/USB/iokit_helper.cpp +++ b/reference/macos/USB/iokit_helper.cpp @@ -55,3 +55,8 @@ int iokit_helper::ioreg_get_property_as_int(io_service_t service, CFStringRef pr CFNumberGetValue(static_cast(property), kCFNumberSInt32Type, &value); return value; } + +int iokit_helper::get_ref_count(void* obj) { + int* data = reinterpret_cast(obj)[1]; + return data[2]; +} diff --git a/reference/macos/USB/iokit_helper.hpp b/reference/macos/USB/iokit_helper.hpp index f69a4c56..56f3fa07 100644 --- a/reference/macos/USB/iokit_helper.hpp +++ b/reference/macos/USB/iokit_helper.hpp @@ -20,6 +20,8 @@ class iokit_helper { static std::string string_from_cfstring(CFStringRef str); static std::string ioreg_get_property_as_string(io_service_t service, CFStringRef property_name); static int ioreg_get_property_as_int(io_service_t service, CFStringRef property_name); + + static int get_ref_count(void* obj); }; template T** iokit_helper::get_interface(io_service_t service, CFUUIDRef plugin_type, CFUUIDRef interface_id) { diff --git a/reference/macos/USB/main.cpp b/reference/macos/USB/main.cpp index 64e85de5..cbb3747c 100644 --- a/reference/macos/USB/main.cpp +++ b/reference/macos/USB/main.cpp @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code common for Linux / macOS / Windows // #include "tests.hpp" diff --git a/reference/macos/USB/prng.cpp b/reference/macos/USB/prng.cpp new file mode 100644 index 00000000..c9cd0b1a --- /dev/null +++ b/reference/macos/USB/prng.cpp @@ -0,0 +1,80 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#include "prng.hpp" + +prng::prng(uint32_t init) : state(init), nbytes(0), bits(0) {} + +void prng::reset(uint32_t init) +{ + state = init; + nbytes = 0; + bits = 0; +} + +uint32_t prng::next() +{ + uint32_t x = state; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + state = x; + return x; +} + +void prng::fill(uint8_t *buf, int len) +{ + for (int i = 0; i < len; i++) + { + if (nbytes == 0) + { + bits = next(); + nbytes = 4; + } + buf[i] = bits; + bits >>= 8; + nbytes--; + } +} + +void prng::fill(std::vector& buf, int len) +{ + if (len == -1 || len > buf.size()) + len = static_cast(buf.size()); + + fill(buf.data(), len); +} + +int prng::verify(const uint8_t *buf, int len) +{ + for (int i = 0; i < len; i++) + { + if (nbytes == 0) + { + bits = next(); + nbytes = 4; + } + + if (buf[i] != (uint8_t)bits) + return i; + + bits >>= 8; + nbytes--; + } + + return -1; +} + +int prng::verify(const std::vector &buf, int len) +{ + if (len == -1 || len > buf.size()) + len = static_cast(buf.size()); + + return verify(buf.data(), len); +} diff --git a/reference/macos/USB/prng.hpp b/reference/macos/USB/prng.hpp new file mode 100644 index 00000000..5214dfa6 --- /dev/null +++ b/reference/macos/USB/prng.hpp @@ -0,0 +1,72 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#pragma once + +#include +#include + +/** + * Pseudo Random Number Generator + */ +struct prng +{ + /** + * Constructs a new instance + * @param init initial value + */ + prng(uint32_t init); + + /** + * Returns the next pseudo random value + * @return pseudo random value + */ + uint32_t next(); + + /** + * Fills the buffer with pseudo random data + * @param buf buffer receiving the random data + * @param len length of the buffer (in bytes) + */ + void fill(uint8_t *buf, int len); + + /** + * Fills the buffer with pseudo random data + * @param buf buffer receiving the random data + * @param len number of bytes to fill (-1 for entire buffer) + */ + void fill(std::vector& buf, int len = -1); + + /** + * Verifies that the passed data matches the next bytes of the sequence. + * @param buf buffer with data to verify + * @param len length of the buffer (in bytes) + * @return -1 if they match, otherwise the position of the difference + */ + int verify(const uint8_t *buf, int len); + + /** + * Verifies that the passed data matches the next bytes of the sequence. + * @param buf buffer with data to verify + * @param len number of bytes to verify (-1 for entire buffer) + * @return -1 if they match, otherwise the position of the difference + */ + int verify(const std::vector& buf, int len = -1); + + /** + * Resets the generator to its initial state. + * @param init initial value + */ + void reset(uint32_t init); + +private: + uint32_t state; + int nbytes; + uint32_t bits; +}; diff --git a/reference/macos/USB/scope.hpp b/reference/macos/USB/scope.hpp index 53e3e39f..85998dba 100644 --- a/reference/macos/USB/scope.hpp +++ b/reference/macos/USB/scope.hpp @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code common for Linux / macOS / Windows // #pragma once diff --git a/reference/macos/USB/speed_test.cpp b/reference/macos/USB/speed_test.cpp new file mode 100644 index 00000000..ab60607b --- /dev/null +++ b/reference/macos/USB/speed_test.cpp @@ -0,0 +1,142 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#include "speed_test.hpp" + +#include "prng.hpp" +#include "usb_error.hpp" + +#include +#include +#include +#include + + +static constexpr uint32_t PRNG_INIT = 0x7b; + + +speed_test::speed_test(usb_device_ptr device, int ep_out, int ep_in) +: device_(device), ep_out_(ep_out), ep_in_(ep_in) { } + +void speed_test::run(int num_bytes) { + reset_buffers(); + start_measurement(); + + std::thread sender(&speed_test::transmit, this, num_bytes / 2); + bool successful = receive(num_bytes / 2); + sender.join(); + + if (successful) + stop_measurement(); +} + +bool speed_test::transmit(int num_bytes) { + + prng seq(PRNG_INIT); + + std::vector buf(2000); + int pos = 0; + + auto is_ptr = device_->open_output_stream(ep_out_); + std::ostream& os = *is_ptr; + + while (num_bytes > 0) { + int n = std::min(num_bytes, static_cast(buf.size())); + try { + seq.fill(buf, n); + os.write(reinterpret_cast(buf.data()), n); + + } catch (usb_error& error) { + std::cerr << std::endl << "ERROR: " << error.what() << " (writing at pos " << pos << ")" << std::endl; + return false; + } + + num_bytes -= n; + pos += n; + update_progress(n); + } + + return true; +} + +bool speed_test::receive(int num_bytes) { + + prng seq(PRNG_INIT); + int pos = 0; + std::vector data; + + auto is_ptr = device_->open_input_stream(ep_in_); + std::istream& is = *is_ptr; + + while (num_bytes > 0) { + int n; + try { + data.resize(2000); + is.read(reinterpret_cast(data.data()), data.capacity()); + n = static_cast(is.gcount()); + if (n <= 0) + break; + data.resize(n); + + int p = seq.verify(data, n); + if (p != -1) { + std::cerr << std::endl << "Invalid data received at pos " << (pos + p) << std::endl; + return false; + } + + } catch (usb_error& error) { + std::cerr << std::endl << "ERROR: " << error.what() << " (reading at pos " << pos << ")" << std::endl; + return false; + } + + num_bytes -= n; + pos += n; + + update_progress(n); + } + + if (num_bytes != 0) { + std::cerr << std::endl << "ERROR: EOF encountered after " << pos << " bytes" << std::endl; + } + + return true; +} + +void speed_test::reset_buffers() { + usb_control_request request_set_value_no_data = { 0 }; + request_set_value_no_data.bmRequestType = usb_control_request::request_type(usb_request_type::direction_out, + usb_request_type::type_vendor, usb_request_type::recipient_interface); + request_set_value_no_data.bRequest = 0x04; + request_set_value_no_data.wIndex = 0; // interface number + device_->control_transfer(request_set_value_no_data); + +} + + +// --- throughput measurement --- + +void speed_test::start_measurement() { + start_time = std::chrono::high_resolution_clock::now(); + processed_bytes = 0; +} + +void speed_test::update_progress(int n) { + std::lock_guard lock(progress_mutex); + processed_bytes += n; +} + +int speed_test::stop_measurement() { + auto end_time = std::chrono::high_resolution_clock::now(); + std::chrono::duration dur = end_time - start_time; + double thr = processed_bytes / dur.count(); + + std::cout << "Throughput: " << std::fixed << std::setprecision(1) << thr << " kByte/s" << std::endl; + + return (int)(thr * 1000); +} diff --git a/reference/macos/USB/speed_test.hpp b/reference/macos/USB/speed_test.hpp new file mode 100644 index 00000000..99f19277 --- /dev/null +++ b/reference/macos/USB/speed_test.hpp @@ -0,0 +1,38 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#pragma once + +#include "usb_device.hpp" + +#include +#include + +class speed_test { +public: + speed_test(usb_device_ptr device, int ep_out, int ep_in); + void run(int num_bytes); + +private: + void reset_buffers(); + bool transmit(int num_bytes); + bool receive(int num_bytes); + + void start_measurement(); + void update_progress(int n); + int stop_measurement(); + + usb_device_ptr device_; + int ep_out_; + int ep_in_; + + std::chrono::time_point start_time; + int processed_bytes; + std::mutex progress_mutex; +}; diff --git a/reference/macos/USB/tests.cpp b/reference/macos/USB/tests.cpp index 6b53c83f..894da2e1 100644 --- a/reference/macos/USB/tests.cpp +++ b/reference/macos/USB/tests.cpp @@ -4,15 +4,20 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code common for Linux / macOS / Windows // #include "tests.hpp" #include "assertion.hpp" +#include "speed_test.hpp" #include #include +#undef min +#undef max + + using random_ushort_engine = std::independent_bits_engine< std::default_random_engine, 16, unsigned short>; @@ -20,14 +25,14 @@ using random_ushort_engine = std::independent_bits_engine< void tests::run() { registry.start(); - for (auto device : registry.get_devices()) { + for (auto& device : registry.get_devices()) { std::cout << "Present: " << device->description() << std::endl; } registry.set_on_device_connected([this](auto device) { on_device_connected(device); }); registry.set_on_device_disconnected([this](auto device) { on_device_disconnected(device); }); - for (auto device : registry.get_devices()) + for (auto& device : registry.get_devices()) on_device(device); std::cout << "Press RETURN to quit" << std::endl; @@ -36,53 +41,53 @@ void tests::run() { } void tests::test_current_device() { - std::cout << "Found test device" << std::endl; - test_device->open(); - test_device->claim_interface(0); - - test_control_transfers(); - test_bulk_transfers(); - - test_device->release_interface(); - test_device->close(); - std::cout << "Test completed" << std::endl; + try { + std::cout << "Found test device" << std::endl; + test_device->open(); + test_device->claim_interface(0); + + test_control_transfers(); + test_bulk_transfers(); + test_speed(); + + test_device->release_interface(0); + test_device->close(); + std::cout << "Test completed" << std::endl; + } + catch (const std::exception& e) { + std::cout << "Test failed: " << e.what() << std::endl; + } } void tests::test_control_transfers() { - usb_control_request request_set_value_no_data = { - .bmRequestType = usb_control_request::request_type(usb_request_type::direction_out, - usb_request_type::type_vendor, - usb_request_type::recipient_interface), - .bRequest = 0x01, - .wValue = 0x9a41, - .wIndex = 0, // interface number - .wLength = 0 - }; + usb_control_request request_set_value_no_data = { 0 }; + request_set_value_no_data.bmRequestType = usb_control_request::request_type(usb_request_type::direction_out, + usb_request_type::type_vendor, usb_request_type::recipient_interface); + request_set_value_no_data.bRequest = 0x01; + request_set_value_no_data.wValue = 0x9a41; + request_set_value_no_data.wIndex = 0; // interface number + request_set_value_no_data.wLength = 0; test_device->control_transfer(request_set_value_no_data); - usb_control_request request_get_data = { - .bmRequestType = usb_control_request::request_type(usb_request_type::direction_in, - usb_request_type::type_vendor, - usb_request_type::recipient_interface), - .bRequest = 0x03, - .wValue = 0, - .wIndex = 0, // interface number - .wLength = 4 - }; + usb_control_request request_get_data = { 0 }; + request_get_data.bmRequestType = usb_control_request::request_type(usb_request_type::direction_in, + usb_request_type::type_vendor, usb_request_type::recipient_interface); + request_get_data.bRequest = 0x03; + request_get_data.wValue = 0; + request_get_data.wIndex = 0; // interface number + request_get_data.wLength = 4; auto data = test_device->control_transfer_in(request_get_data); std::vector expected_data{ 0x41, 0x9a, 0x00, 0x00 }; assert_equals(expected_data, data); std::vector sent_value{ 0x83, 0x03, 0xda, 0x3d }; - usb_control_request request_set_value_data = { - .bmRequestType = usb_control_request::request_type(usb_request_type::direction_out, - usb_request_type::type_vendor, - usb_request_type::recipient_interface), - .bRequest = 0x02, - .wValue = 0, - .wIndex = 0, // interface number - .wLength = static_cast(sent_value.size()) - }; + usb_control_request request_set_value_data = { 0 }; + request_set_value_data.bmRequestType = usb_control_request::request_type(usb_request_type::direction_out, + usb_request_type::type_vendor, usb_request_type::recipient_interface); + request_set_value_data.bRequest = 0x02; + request_set_value_data.wValue = 0; + request_set_value_data.wIndex = 0; // interface number + request_set_value_data.wLength = static_cast(sent_value.size()); test_device->control_transfer_out(request_set_value_data, sent_value); data = test_device->control_transfer_in(request_get_data); @@ -106,9 +111,9 @@ void tests::test_loopback(int num_bytes) { // read in separate thread std::thread reader([this, &rx_data, num_bytes]() { - int bytes_read = 0; + size_t bytes_read = 0; while (bytes_read < num_bytes) { - auto data = test_device->transfer_in(2, 64); + auto data = test_device->transfer_in(2); rx_data.insert(rx_data.end(), data.begin(), data.end()); bytes_read += data.size(); } @@ -131,6 +136,13 @@ void tests::test_loopback(int num_bytes) { assert_equals(random_data, rx_data); } +void tests::test_speed() { + int packet_size = test_device->get_endpoint(usb_direction::out, 1).packet_size(); + + speed_test test(test_device, 1, 2); + test.run(packet_size == 512 ? 20000000 : 2000000); +} + void tests::on_device(usb_device_ptr device) { if (is_test_device(device)) { test_device = device; @@ -156,7 +168,6 @@ std::vector tests::random_bytes(int num) { return std::vector(p, p + num); } - bool tests::is_test_device(usb_device_ptr device) { return device->vendor_id() == 0xcafe && device->product_id() == 0xceaf; } diff --git a/reference/macos/USB/tests.hpp b/reference/macos/USB/tests.hpp index 992de2b9..11cf328f 100644 --- a/reference/macos/USB/tests.hpp +++ b/reference/macos/USB/tests.hpp @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code common for Linux / macOS / Windows // #pragma once @@ -20,6 +20,7 @@ class tests { void test_current_device(); void test_control_transfers(); void test_bulk_transfers(); + void test_speed(); void test_loopback(int num_bytes); diff --git a/reference/macos/USB/usb_device.cpp b/reference/macos/USB/usb_device.cpp index 1c6c7f83..d2e80d33 100644 --- a/reference/macos/USB/usb_device.cpp +++ b/reference/macos/USB/usb_device.cpp @@ -9,27 +9,44 @@ #include "usb_device.hpp" #include "usb_error.hpp" +#include "usb_iostream.hpp" +#include "usb_registry.hpp" #include "scope.hpp" #include "iokit_helper.hpp" +#include "config_parser.hpp" #include +#include #include +#include +#include -usb_device::usb_device(io_service_t service, IOUSBDeviceInterface** device, uint64_t entry_id, int vendor_id, int product_id) -: entry_id_(entry_id), device_(device), interface_(nullptr), vendor_id_(vendor_id), product_id_(product_id), is_open_(false) { - - (*device)->AddRef(device); +usb_device::usb_device(usb_registry* registry, io_service_t service, IOUSBDeviceInterface** device, uint64_t entry_id, int vendor_id, int product_id) +: registry_(registry), entry_id_(entry_id), device_(device), vendor_id_(vendor_id), product_id_(product_id), is_open_(false) { manufacturer_ = iokit_helper::ioreg_get_property_as_string(service, CFSTR(kUSBVendorString)); product_ = iokit_helper::ioreg_get_property_as_string(service, CFSTR(kUSBProductString)); serial_number_ = iokit_helper::ioreg_get_property_as_string(service, CFSTR(kUSBSerialNumberString)); + + load_configuration(device); + + (*device)->AddRef(device); } usb_device::~usb_device() { close(); } +void usb_device::load_configuration(IOUSBDeviceInterface** device) { + IOUSBConfigurationDescriptorPtr desc = nullptr; + (*device)->GetConfigurationDescriptorPtr(device, 0, &desc); + + config_parser parser{}; + parser.parse(reinterpret_cast(desc), desc->wTotalLength); + interfaces_ = std::move(parser.interfaces); +} + std::string usb_device::description() const { const char* fmt = "VID: 0x%04x, PID: 0x%04x, manufacturer: %s, product: %s, serial: %s"; @@ -44,15 +61,67 @@ std::string usb_device::description() const { return desc; } +const std::vector& usb_device::interfaces() const { + return interfaces_; +} + +const usb_interface& usb_device::get_interface(int interface_number) const { + for (const usb_interface& intf : interfaces_) { + if (intf.number() == interface_number) + return intf; + } + + return usb_interface::invalid; +} + +const usb_endpoint& usb_device::get_endpoint(usb_direction direction, int endpoint_number) const { + for (const usb_interface& intf : interfaces_) { + for (const usb_endpoint& ep : intf.alternate().endpoints()) { + if (ep.direction() == direction && ep.number() == endpoint_number) + return ep; + } + } + + return usb_endpoint::invalid; +} + +void usb_device::detach_standard_drivers() { + if (is_open()) + throw usb_error("detach_standard_drivers() must not be called when the device is open", 0); + + IOReturn ret = (*device_)->USBDeviceReEnumerate(device_, kUSBReEnumerateCaptureDeviceMask); + usb_error::check(ret, "failed to detach standard drivers"); +} + +void usb_device::attach_standard_drivers() { + if (is_open()) + throw usb_error("attach_standard_drivers() must not be called when the device is open", 0); + + IOReturn ret = (*device_)->USBDeviceReEnumerate(device_, kUSBReEnumerateReleaseDeviceMask); + usb_error::check(ret, "failed to attach standard drivers"); +} + + bool usb_device::is_open() const { return is_open_; } void usb_device::open() { if (is_open()) - throw new usb_error("USB device is already open", 0); + throw usb_error("USB device is already open", 0); - IOReturn ret = (*device_)->USBDeviceOpen(device_); + // try multiple times to fight race conditions + int tries = 0; + IOReturn ret = 0; + while (tries < 3) { + ret = (*device_)->USBDeviceOpenSeize(device_); + if (ret != kIOReturnExclusiveAccess) + break; + + // sleep and try again + tries += 1; + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } usb_error::check(ret, "unable to open USB device"); ret = (*device_)->SetConfiguration(device_, 1); @@ -74,8 +143,12 @@ void usb_device::close() { void usb_device::claim_interface(int interface_number) { - if (interface_ != nullptr) - throw usb_error("an interface has already been claimed"); + if (claimed_interfaces_.find(interface_number) != claimed_interfaces_.end()) + throw usb_error("interface has already been claimed"); + + usb_interface* uintf = get_intf_ptr(interface_number); + if (uintf == nullptr) + throw usb_error("no such interface"); // find interface IOUSBFindInterfaceRequest request; @@ -95,7 +168,7 @@ void usb_device::claim_interface(int interface_number) { auto service_guard = make_scope_exit([service]() { IOObjectRelease(service); }); - IOUSBInterfaceInterface** intf = iokit_helper::get_interface(service, kIOUSBInterfaceUserClientTypeID, kIOUSBInterfaceInterfaceID); + IOUSBInterfaceInterface** intf = iokit_helper::get_interface(service, kIOUSBInterfaceUserClientTypeID, kIOUSBInterfaceInterfaceID190); if (intf == nullptr) throw usb_error("internal error (failed to create interface interface)"); @@ -113,53 +186,104 @@ void usb_device::claim_interface(int interface_number) { } if (interface == nullptr) - throw usb_error("no USB interface found for given number"); + throw usb_error("internal error"); auto interface_guard = make_scope_exit([interface]() { (*interface)->Release(interface); }); ret = (*interface)->USBInterfaceOpen(interface); usb_error::check(ret, "failed to open USB interface"); - UInt8 num_pipes = 0; - ret = (*interface)->GetNumEndpoints(interface, &num_pipes); - usb_error::check(ret, "internal error (GetNumEndpoints)"); - - endpoint_addresses_.clear(); - for (int i = 1; i <= num_pipes; i++) { - UInt8 direction = 0; - UInt8 number = 0; - UInt8 ignore = 0; - UInt16 ignore2 = 0; - ret = (*interface)->GetPipeProperties(interface, i, &direction, &number, &ignore, &ignore2, &ignore); - usb_error::check(ret, "internal error (GetPipeProperties)"); - endpoint_addresses_.push_back((direction << 7) | number); - } + (*interface)->AddRef(interface); + claimed_interfaces_[interface_number] = interface; + uintf->set_claimed(true); + build_pipe_info(); +} - interface_ = interface; - (*interface_)->AddRef(interface_); +void usb_device::release_interface(int interface_number) { + + usb_interface* uintf = get_intf_ptr(interface_number); + if (uintf == nullptr) + throw usb_error("no such interface"); + + auto iter = claimed_interfaces_.find(interface_number); + if (iter == claimed_interfaces_.end()) + throw usb_error("interface has not been claimed"); + + IOUSBInterfaceInterface** interface = (*iter).second; + auto source = (*interface)->GetInterfaceAsyncEventSource(interface); + if (source != nullptr) + registry_->remove_event_source(source); + + claimed_interfaces_.erase(iter); + uintf->set_claimed(false); + + (*interface)->USBInterfaceClose(interface); + (*interface)->Release(interface); + + build_pipe_info(); } -void usb_device::release_interface() { - if (interface_ == nullptr) - throw usb_error("no interface has been claimed"); +void usb_device::select_alternate_interface(int interface_number, int alternate_setting) { + + usb_interface* uintf = get_intf_ptr(interface_number); + if (uintf == nullptr) + throw usb_error("no such interface"); + + int alt_index = get_alternate_index(interface_number, alternate_setting); + if (alt_index == -1) + throw usb_error("no such alternate setting"); + + auto iter = claimed_interfaces_.find(interface_number); + if (iter == claimed_interfaces_.end()) + throw usb_error("interface has not been claimed"); - (*interface_)->USBInterfaceClose(interface_); - (*interface_)->Release(interface_); - interface_ = nullptr; - endpoint_addresses_.clear(); + IOUSBInterfaceInterface** interface = iter->second; + (*interface)->SetAlternateInterface(interface, (UInt8) alternate_setting); + uintf->set_alternate(alt_index); + + build_pipe_info(); } -std::vector usb_device::transfer_in(int endpoint_number, int data_len, int timeout) { - if (interface_ == nullptr) - throw usb_error("no interface has been claimed"); +void usb_device::build_pipe_info() { + pipes_.clear(); - UInt32 size = data_len; - std::vector data(data_len); + for (auto& intf : claimed_interfaces_) { + IOUSBInterfaceInterface** interface = intf.second; + + UInt8 num_pipes = 0; + IOReturn ret = (*interface)->GetNumEndpoints(interface, &num_pipes); + usb_error::check(ret, "internal error (GetNumEndpoints)"); + + for (int i = 1; i <= num_pipes; i++) { + UInt8 direction = 0; + UInt8 number = 0; + UInt8 transfer_type = 0; + UInt16 packet_size = 0; + UInt8 ignore = 0; + ret = (*interface)->GetPipeProperties(interface, i, &direction, &number, &transfer_type, &packet_size, &ignore); + usb_error::check(ret, "internal error (GetPipeProperties)"); + + uint8_t addr = static_cast((direction << 7) | number); + usb_transfer_type type = static_cast(transfer_type); // both enumeration use the same value as the USB standard + pipe_info pipe{static_cast(i), addr, packet_size, type, intf.first}; + pipes_.push_back(std::move(pipe)); + } + } +} + + +std::vector usb_device::transfer_in(int endpoint_number, int timeout) { + auto pipe = ep_in_pipe(endpoint_number); + IOUSBInterfaceInterface** interface = claimed_interfaces_[pipe->interface_number]; + + UInt32 size = pipe->packet_size; + std::vector data(size); IOReturn ret; - if (timeout != 0) - ret = (*interface_)->ReadPipeTO(interface_, ep_in_pipe(endpoint_number), data.data(), &size, timeout, timeout); - else - ret = (*interface_)->ReadPipe(interface_, ep_in_pipe(endpoint_number), data.data(), &size); + if (timeout != 0) { + ret = (*interface)->ReadPipeTO(interface, pipe->pipe_index, data.data(), &size, timeout, timeout); + } else { + ret = (*interface)->ReadPipe(interface, pipe->pipe_index, data.data(), &size); + } if (ret != kIOReturnSuccess) { if (ret == kIOUSBTransactionTimeout) @@ -172,15 +296,18 @@ std::vector usb_device::transfer_in(int endpoint_number, int data_len, return data; } -void usb_device::transfer_out(int endpoint_number, const std::vector& data, int timeout) { - if (interface_ == nullptr) - throw usb_error("no interface has been claimed"); +void usb_device::transfer_out(int endpoint_number, const std::vector& data, int len, int timeout) { + if (len < 0 || len > data.size()) + len = static_cast(data.size()); + + auto pipe = ep_out_pipe(endpoint_number); + IOUSBInterfaceInterface** interface = claimed_interfaces_[pipe->interface_number]; IOReturn ret; if (timeout != 0) - ret = (*interface_)->WritePipeTO(interface_, ep_out_pipe(endpoint_number), const_cast(data.data()), static_cast(data.size()), timeout, timeout); + ret = (*interface)->WritePipeTO(interface, pipe->pipe_index, const_cast(data.data()), len, timeout, timeout); else - ret = (*interface_)->WritePipe(interface_, ep_out_pipe(endpoint_number), const_cast(data.data()), static_cast(data.size())); + ret = (*interface)->WritePipe(interface, pipe->pipe_index, const_cast(data.data()), len); if (ret != kIOReturnSuccess) { if (ret == kIOUSBTransactionTimeout) @@ -247,18 +374,109 @@ std::vector usb_device::control_transfer_in(const usb_control_request& return data; } -UInt8 usb_device::ep_to_pipe(int endpoint_address) { - auto it = std::find (endpoint_addresses_.begin(), endpoint_addresses_.end(), endpoint_address); - if (it != endpoint_addresses_.end()) - return std::distance(endpoint_addresses_.begin(), it) + 1; +std::unique_ptr usb_device::open_input_stream(int endpoint_number) { + return std::unique_ptr(new usb_istream(registry_->get_shared_ptr(this), endpoint_number)); +} + +std::unique_ptr usb_device::open_output_stream(int endpoint_number) { + return std::unique_ptr(new usb_ostream(registry_->get_shared_ptr(this), endpoint_number)); +} + +void usb_device::abort_transfer(usb_direction direction, int endpoint_number) { + auto pipe = get_pipe(direction == usb_direction::in ? endpoint_number + 128 : endpoint_number); + IOUSBInterfaceInterface** interface = claimed_interfaces_[pipe->interface_number]; + + IOReturn ret = (*interface)->AbortPipe(interface, pipe->pipe_index); + usb_error::check(ret, "failed to abort transfer"); +} + +const usb_device::pipe_info* usb_device::get_pipe(int endpoint_address) { + auto it = std::find_if(pipes_.begin(), pipes_.end(), + [endpoint_address](const pipe_info& pipe){ return pipe.endpoint_address == endpoint_address; }); + if (it != pipes_.end()) { + const pipe_info* pi = &*it; + if (pi->transfer_type != usb_transfer_type::bulk && pi->transfer_type != usb_transfer_type::interrupt) + throw usb_error("invalid transfer type for endpoint"); + return pi; + } + + // good error message + for (usb_interface& intf : interfaces_) { + for (const usb_endpoint& ep : intf.alternate().endpoints()) { + int addr = ep.number(); + if (ep.direction() == usb_direction::in) + addr += 128; + if (addr == endpoint_address) + throw usb_error("endpoint's interface has not been claimed"); + } + } + + throw usb_error("no such endpoint"); +} + + const usb_device::pipe_info* usb_device::ep_out_pipe(int endpoint_number) { + return get_pipe(endpoint_number); +} + + const usb_device::pipe_info* usb_device::ep_in_pipe(int endpoint_number) { + return get_pipe(endpoint_number + 128); +} + +usb_interface* usb_device::get_intf_ptr(int interface_number) { + for (usb_interface& intf : interfaces_) + if (intf.number() == interface_number) + return &intf; - throw usb_error("invalid endpoint number"); + return nullptr; } -UInt8 usb_device::ep_out_pipe(int endpoint_number) { - return ep_to_pipe(endpoint_number); +int usb_device::get_alternate_index(int interface_number, int alternate_setting) { + for (usb_interface& intf : interfaces_) { + if (intf.number() == interface_number) { + for (int i = 0; i < intf.alternates().size(); i++) { + if (intf.alternates()[i].number() == alternate_setting) + return i; + } + } + } + + return -1; +} + +void usb_device::submit_transfer_in(int endpoint_number, uint8_t* buffer, int buffer_size, const std::function& completion) { + auto pipe = ep_in_pipe(endpoint_number); + IOUSBInterfaceInterface** interface = claimed_interfaces_[pipe->interface_number]; + create_event_source(interface); + + // submit request + IOReturn ret = (*interface)->ReadPipeAsync(interface, pipe->pipe_index, buffer, buffer_size, async_io_completed, + const_cast*>(&completion)); + usb_error::check(ret, "failed to submit async transfer"); +} + +void usb_device::submit_transfer_out(int endpoint_number, const uint8_t* data, int data_size, const std::function& completion) { + auto pipe = ep_out_pipe(endpoint_number); + IOUSBInterfaceInterface** interface = claimed_interfaces_[pipe->interface_number]; + create_event_source(interface); + + // submit request + IOReturn ret = (*interface)->WritePipeAsync(interface, pipe->pipe_index, const_cast(data), data_size, async_io_completed, + const_cast*>(&completion)); + usb_error::check(ret, "failed to submit async transfer"); +} + +void usb_device::create_event_source(IOUSBInterfaceInterface** interface) { + auto source = (*interface)->GetInterfaceAsyncEventSource(interface); + if (source == nullptr) { + IOReturn ret = (*interface)->CreateInterfaceAsyncEventSource(interface, &source); + usb_error::check(ret, "failed to create event source for interface"); + registry_->add_event_source(source); + } } -UInt8 usb_device::ep_in_pipe(int endpoint_number) { - return ep_to_pipe(endpoint_number + 128); +void usb_device::async_io_completed(void* refcon, IOReturn result, void* arg0) { + // 'refcon' is lambda function for completion, 'arg0' is the number of read bytes + int size = static_cast(reinterpret_cast(arg0)); + auto completion = reinterpret_cast*>(refcon); + (*completion)(result, size); } diff --git a/reference/macos/USB/usb_device.hpp b/reference/macos/USB/usb_device.hpp index f6c729f8..788ac588 100644 --- a/reference/macos/USB/usb_device.hpp +++ b/reference/macos/USB/usb_device.hpp @@ -12,11 +12,18 @@ #include #include +#include +#include +#include #include #include #include #include +#include "configuration.hpp" + +class usb_registry; + /** * USB control request type. @@ -90,7 +97,52 @@ class usb_device { std::string serial_number() const { return serial_number_; } /// Descriptive string including VID, PID, manufacturer, product name and serial number std::string description() const; + /// List of interfaces + const std::vector& interfaces() const; + + /** + * Get the USB interface. + * + * @param interface_number interface number + * @return interface or `nullptr` if no such interface exists + */ + const usb_interface& get_interface(int interface_number) const; + /** + * Detaches the standard drivers of the operating system. + * + * By detaching the standard drivers, the operating system releases the exclusive access + * to the device and/or its interfaces. It is relevant for USB devices implementing standard + * USB classes such as HID, CDC or mass storage. + * + * Executing this function requires either root privileges or the _com.apple.vm.device-access_ entitlement. + * + * This method should be called before the device is opened. After the device has been closed, + * `attach_standard_drivers()` should be called to restore the initial state. + */ + void detach_standard_drivers(); + + /** + * Attaches the standard drivers of the operating system. + * + * By attaching the standard drivers, the original state before `detach_standard_drivers()` was called + * is restored. + * + * Executing this function requires either root privileges or the _com.apple.vm.device-access_ entitlement. + * + * This method should be called after the device has been closed. + */ + void attach_standard_drivers(); + + /** + * Get a USB endpoint. + * + * @param direction endpoint direction + * @param endpoint_number endpoint number (between 1 and 127) + * @return endpoint or `nullptr` if endpoint does not exist + */ + const usb_endpoint& get_endpoint(usb_direction direction, int endpoint_number) const; + /// Opens the device for communication void open(); @@ -103,25 +155,32 @@ class usb_device { /** * Claims an interface * - * A single interface can be claimed. - * * @param interface_number interface number */ void claim_interface(int interface_number); /** - * Releases the claimed interface. + * Releases a claimed interface. + * + * @param interface_number interface number + */ + void release_interface(int interface_number); + + /** + * Select an alternate interface setting. + * + * The interface must have been claimed. + * + * @param interface_number interface number + * @param alternate_setting alternate setting number */ - void release_interface(); + void select_alternate_interface(int interface_number, int alternate_setting); /** * Receives data from a bulk or interrupt endpoint. * * The amount of bytes read will be influced by the underlying USB packets. - * If a short packet is sent, the function will return after having read fewer bytes - * than specified. The function will fail if a bigger packet has been received than - * will fit into the given buffer. So the specified data length should be big enough for - * the maximum packet size (64 bytes for full-speed USB). + * It can be 0 (if the device sends a ZLP) up to the maximum packet size. * * The timeout specifies the maximum time it may take to complete the operation. * If the operation does not complete within that time, the function returns after reading @@ -130,11 +189,10 @@ class usb_device { * Interrupt endpoints do not support timeouts. Thus, 0 has to be specified. * * @param endpoint_number endpoint number (between 1 and 127) - * @param data_len maximum length to read (in bytes) * @param timeout timeout (in ms, 0 for no timeout) * @return received data */ - std::vector transfer_in(int endpoint_number, int data_len, int timeout = 0); + std::vector transfer_in(int endpoint_number, int timeout = 0); /** * Transmits data to a bulk or interrupt endpoint. @@ -147,9 +205,10 @@ class usb_device { * * @param endpoint_number endpoint number (between 1 and 127) * @param data data to transmit + * @param len data length, in bytes (-1 for entire data vector) * @param timeout timeout (in ms, 0 for no timeout) */ - void transfer_out(int endpoint_number, const std::vector& data, int timeout = 0); + void transfer_out(int endpoint_number, const std::vector& data, int len = -1, int timeout = 0); /** * Send a control request with no Data phase. @@ -186,21 +245,74 @@ class usb_device { * @return received data */ std::vector control_transfer_in(const usb_control_request& request, int timeout = 0); + + /** + * Open a new input stream for a bulk endpoint. + * + * The input stream is optimized for maximum throughput. + * + * Do not use the input stream concurrently with other transfer operations on the same endpoint. The input stream + * buffers data for high throughput. When the stream is closed, any data in the buffers will be lost. + * + * @param endpoint_number endpoint number (between 1 and 127) + */ + std::unique_ptr open_input_stream(int endpoint_number); + + /** + * Open a new output stream for a bulk endpoint. + * + * The output stream is optimized for maximum throughput. + * + * Do not use the output stream concurrently with other transfer operations on the same endpoint. + * + * @param endpoint_number endpoint number (between 1 and 127) + */ + std::unique_ptr open_output_stream(int endpoint_number); + + /** + * Aborts all transfer on the specified endpoint. + * + * @param direction endpoint direction + * @param endpoint_number endpoint number (between 1 and 127) + */ + void abort_transfer(usb_direction direction, int endpoint_number); private: - usb_device(io_service_t service, IOUSBDeviceInterface** device, uint64_t entry_id, int vendor_id, int product_id); + struct pipe_info { + uint8_t pipe_index; + uint8_t endpoint_address; + uint16_t packet_size; + usb_transfer_type transfer_type; + int interface_number; + }; + + usb_device(usb_registry* registry, io_service_t service, IOUSBDeviceInterface** device, uint64_t entry_id, int vendor_id, int product_id); uint64_t entry_id() const { return entry_id_; } - UInt8 ep_to_pipe(int endpoint_address); - UInt8 ep_out_pipe(int endpoint_number); - UInt8 ep_in_pipe(int endpoint_number); + void build_pipe_info(); + const pipe_info* get_pipe(int endpoint_address); + const pipe_info* ep_in_pipe(int endpoint_address); + const pipe_info* ep_out_pipe(int endpoint_address); int control_transfer_core(const usb_control_request& request, uint8_t* data, int timeout); - + void load_configuration(IOUSBDeviceInterface** device); + usb_interface* get_intf_ptr(int interface_number); + int get_alternate_index(int interface_number, int alternate_setting); + + // Submits async request (completion handler is not copied; lifetime must be managed by caller) + void submit_transfer_in(int endpoint_number, uint8_t* buffer, int buffer_size, const std::function& completion); + // Submits async request (completion handler is not copied; lifetime must be managed by caller) + void submit_transfer_out(int endpoint_number, const uint8_t* data, int data_size, const std::function& completion); + // Create event source for asynchronous communication (if needed) + void create_event_source(IOUSBInterfaceInterface** interface); + static void async_io_completed(void* refcon, IOReturn result, void* arg0); + + usb_registry* registry_; uint64_t entry_id_; IOUSBDeviceInterface** device_; bool is_open_; - IOUSBInterfaceInterface** interface_; - std::vector endpoint_addresses_; - + std::vector pipes_; + std::map claimed_interfaces_; + std::vector interfaces_; + int product_id_; int vendor_id_; std::string manufacturer_; @@ -208,6 +320,8 @@ class usb_device { std::string serial_number_; friend class usb_registry; + friend class usb_istreambuf; + friend class usb_ostreambuf; }; typedef std::shared_ptr usb_device_ptr; diff --git a/reference/macos/USB/usb_error.cpp b/reference/macos/USB/usb_error.cpp index b24aec03..82ec0b1e 100644 --- a/reference/macos/USB/usb_error.cpp +++ b/reference/macos/USB/usb_error.cpp @@ -37,6 +37,8 @@ std::string usb_error::full_message(const char* message, int code) { std::string msg(message); msg += " ("; msg += mach_error_string(code); + msg += " - code "; + msg += std::to_string(code); msg += ")"; return msg; diff --git a/reference/macos/USB/usb_error.hpp b/reference/macos/USB/usb_error.hpp index 83c35739..895ea33c 100644 --- a/reference/macos/USB/usb_error.hpp +++ b/reference/macos/USB/usb_error.hpp @@ -18,8 +18,8 @@ class usb_error : public std::exception { /** * Creates a new instance. * - *@param message error message - *@param code a Mach error code, or 0 if no Mach code is available + * @param message error message + * @param code a Mach error code, or 0 if no Mach code is available */ usb_error(const char* message, int code = 0) noexcept; diff --git a/reference/macos/USB/usb_iostream.cpp b/reference/macos/USB/usb_iostream.cpp new file mode 100644 index 00000000..032387b4 --- /dev/null +++ b/reference/macos/USB/usb_iostream.cpp @@ -0,0 +1,215 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#include "usb_iostream.hpp" +#include "usb_error.hpp" + +// --- usb_istreambuf --- + +usb_istreambuf::usb_istreambuf(usb_device_ptr device, int endpoint_number) +: device(device), endpoint_number(endpoint_number), is_closed(false) { + setg(nullptr, nullptr, nullptr); + + buffer_size = device->get_endpoint(usb_direction::in, endpoint_number).packet_size(); + + // allocate the buffers and submit requests + for (int i = 0; i < max_outstanding_requests; i++) { + transfer_request* request = &requests[i]; + request->buffer = new uint8_t[buffer_size]; + request->io_completion = [this, request](IOReturn result, int size) { on_completed(request, result, size); }; + + if (i == 0) + current_request = request; + else + submit_transfer(request); + } +} + +usb_istreambuf::~usb_istreambuf() { + close(); + + // free buffers + for (int i = 0; i < max_outstanding_requests; i++) + delete [] requests[i].buffer; +} + +void usb_istreambuf::close() { + is_closed = true; + setg(nullptr, nullptr, nullptr); + + device->abort_transfer(usb_direction::in, endpoint_number); + + // wait until completion handlers have been called + while (num_outstanding_requests > 0) + wait_for_request_completion(); +} + +void usb_istreambuf::submit_transfer(transfer_request* request) { + device->submit_transfer_in(endpoint_number, request->buffer, buffer_size, request->io_completion); + num_outstanding_requests += 1; +} + +void usb_istreambuf::on_completed(transfer_request* request, IOReturn result, int size) { + request->result_code = result; + request->result_size = size; + completed_request_queue.put(request); +} + +usb_istreambuf::transfer_request* usb_istreambuf::wait_for_request_completion() { + transfer_request* request = completed_request_queue.take(); + num_outstanding_requests -= 1; + return request; + +} + +usb_istreambuf::int_type usb_istreambuf::underflow() { + if (is_closed) + return traits_type::eof(); + + if (gptr() < egptr()) + return traits_type::to_int_type(*gptr()); + + // loop until non-ZLP has been received + do { + submit_transfer(current_request); + + current_request = wait_for_request_completion(); + usb_error::check(current_request->result_code, "error reading from USB endpoint"); + + char* buf = reinterpret_cast(current_request->buffer); + int size = current_request->result_size; + setg(buf, buf, buf + size); + + } while (current_request->result_size == 0); + + return traits_type::to_int_type(*gptr()); +} + + +// --- usb_ostreambuf --- + +usb_ostreambuf::usb_ostreambuf(usb_device_ptr device, int endpoint_number) +: device(device), endpoint_number(endpoint_number), is_closed(false), needs_zlp(false) { + + packet_size = device->get_endpoint(usb_direction::out, endpoint_number).packet_size(); + buffer_size = packet_size; + + // create requests + for (int i = 0; i < max_outstanding_requests; i++) { + transfer_request* request = &requests[i]; + request->buffer = new uint8_t[buffer_size]; + request->io_completion = [this, request](IOReturn result, int size) { on_completed(request, result); }; + } + + fill_queue(); +} + +void usb_ostreambuf::fill_queue() { + for (int i = 1; i < max_outstanding_requests; i++) + available_request_queue.put(&requests[i]); + + // configure stream buffer for first request + current_request = &requests[0]; + char* buf = reinterpret_cast(current_request->buffer); + setp(buf, buf + buffer_size); +} + +usb_ostreambuf::~usb_ostreambuf() { + sync(); + + // free buffers + for (int i = 0; i < max_outstanding_requests; i++) + delete [] requests[i].buffer; +} + +int usb_ostreambuf::sync() { + // submit request if there is any data in the current buffer + auto size = pptr() - pbase(); + if (size > 0) + submit_transfer((int)size); + + // send a zero-length packet if required + if (needs_zlp) + submit_transfer(0); + + // Wait until all buffers have been transmitted by removing them from the + // queue and reinserting them. One request is the current request. + // So the queue only contains max_outstanding_requests - 1 requests. + for (int i = 0; i < max_outstanding_requests - 1; i++) + wait_for_available_transfer(); + + fill_queue(); + + return 0; +} + +int usb_ostreambuf::overflow (int c) { + // submit request + auto size = pptr() - pbase(); + submit_transfer((int)size); + + // insert char + if (c != traits_type::eof()) { + *pptr() = (char)c; + pbump(1); + } + + return c; +} + +void usb_ostreambuf::submit_transfer(int size) { + device->submit_transfer_out(endpoint_number, current_request->buffer, size, current_request->io_completion); + needs_zlp = size == packet_size; + + current_request = wait_for_available_transfer(); + + // configure stream buffer + char* buf = reinterpret_cast(current_request->buffer); + setp(buf, buf + buffer_size); +} + +void usb_ostreambuf::on_completed(transfer_request* request, IOReturn result) { + request->result_code = result; + available_request_queue.put(request); +} + +usb_ostreambuf::transfer_request* usb_ostreambuf::wait_for_available_transfer() { + auto request = available_request_queue.take(); + + // check for error + int result = request->result_code; + if (result != 0) { + request->result_code = 0; + throw usb_error("error writing to USB endpoint", result); + } + + return request; +} + + +// --- usb_istream --- + +usb_istream::usb_istream(usb_device_ptr device, int ep_num) + : std::istream(new usb_istreambuf(device, ep_num)) {} + +usb_istream::~usb_istream() { + // free stream buffer + delete rdbuf(); +} + + +// --- usb_ostream --- + +usb_ostream::usb_ostream(usb_device_ptr device, int ep_num) + : std::ostream(new usb_ostreambuf(device, ep_num)) {} + +usb_ostream::~usb_ostream() { + // free stream buffer + delete rdbuf(); +} diff --git a/reference/macos/USB/usb_iostream.hpp b/reference/macos/USB/usb_iostream.hpp new file mode 100644 index 00000000..ca2b97a3 --- /dev/null +++ b/reference/macos/USB/usb_iostream.hpp @@ -0,0 +1,155 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#pragma once + +#include +#include +#include "usb_device.hpp" +#include "blocking_queue.hpp" + +/** + * Input stream buffer for USB bulk or interrupt endpoint. + * + * The stream buffer is internally used by an input stream. It submits multiple asynchronous IO requests to + * achieve maximum throughput. + */ +class usb_istreambuf : public std::streambuf { +public: + /// Constructor + usb_istreambuf(usb_device_ptr device, int ep_num); + /// Destructor + virtual ~usb_istreambuf(); + + /// Closes this buffer. This stream buffer will automatically free itself. + void close(); + +protected: + /// Called when the internal buffer has no further data to read. + virtual int_type underflow(); + +private: + /// Transfer request + struct transfer_request { + /// buffer for recevied data + uint8_t* buffer; + /// result coce + int result_code; + /// result size (in bytes) + int result_size; + /// IO completion handler + std::function io_completion; + }; + + void on_completed(transfer_request* request, IOReturn result, int size); + void submit_transfer(transfer_request* request); + transfer_request* wait_for_request_completion(); + + /// Maximum number of concurrently outstanding requests + static constexpr int max_outstanding_requests = 4; + + /// USB device + usb_device_ptr device; + /// endpoint number + int endpoint_number; + /// Indicates that this stream buffer is closed + bool is_closed; + /// buffer size + int buffer_size; + /// transfer requests + transfer_request requests[max_outstanding_requests]; + /// queue with completed requests + blocking_queue completed_request_queue; + /// number of outstanding requests (requests pending with OS and requests in queue) + int num_outstanding_requests; + /// current request being read from + transfer_request* current_request; +}; + +/** + * Output stream buffer for USB bulk or interrupt endpoint. + * + * The stream buffer is internally used by an output stream. It submits multiple asynchronous IO requests to + * achieve maximum throughput. + */ +class usb_ostreambuf : public std::streambuf { +public: + /// Constructor + usb_ostreambuf(usb_device_ptr device, int ep_num); + /// Destructor + virtual ~usb_ostreambuf(); + + virtual int sync(); + +protected: + /// Called when the internal buffer has no space left to add more data. + virtual int overflow (int c); + + +private: + /// Transfer request + struct transfer_request { + /// buffer for recevied data + uint8_t* buffer; + /// result coce + int result_code; + /// IO completion handler + std::function io_completion; + }; + + void fill_queue(); + void submit_transfer(int size); + void on_completed(transfer_request* request, IOReturn result); + transfer_request* wait_for_available_transfer(); + + /// Maximum number of concurrently outstanding requests + static constexpr int max_outstanding_requests = 4; + + /// USB device + usb_device_ptr device; + /// endpoint number + int endpoint_number; + /// Indicates that this stream buffer is closed + bool is_closed; + /// Indicates if a zero-length packet is required + bool needs_zlp; + /// packet size + int packet_size; + /// packet size + int buffer_size; + /// transfer requests + transfer_request requests[max_outstanding_requests]; + /// queue with available requests + blocking_queue available_request_queue; + /// current request being written to + transfer_request* current_request; + +}; + +/** + * Input stream for reading from a USB bulk endpoint + */ +class usb_istream : public std::istream { +public: + /// Constructor + usb_istream(usb_device_ptr device, int ep_num); + /// Destructor + ~usb_istream(); +}; + +/** + * Output stream for writing to a USB bulk endpoint + */ +class usb_ostream : public std::ostream { +public: + /// Constructor + usb_ostream(usb_device_ptr device, int ep_num); + /// Destructor + ~usb_ostream(); +}; diff --git a/reference/macos/USB/usb_registry.cpp b/reference/macos/USB/usb_registry.cpp index bee6b975..478f46f0 100644 --- a/reference/macos/USB/usb_registry.cpp +++ b/reference/macos/USB/usb_registry.cpp @@ -19,10 +19,10 @@ #include usb_registry::usb_registry() -: notify_port(nullptr), run_loop_source(nullptr), +: notify_port(nullptr), monitor_run_loop_source(nullptr), device_connected_iter(0), device_disconnected_iter(0), on_connected_callback(nullptr), on_disconnected_callback(nullptr), - is_device_list_ready(false) { + async_io_run_loop(nullptr), is_device_list_ready(false) { } usb_registry::~usb_registry() { @@ -34,11 +34,14 @@ usb_registry::~usb_registry() { IOObjectRelease(device_disconnected_iter); device_disconnected_iter = 0; } - - CFRunLoopStop(run_loop); - monitor_thread.join(); - + if (async_io_run_loop != nullptr) { + CFRunLoopStop(async_io_run_loop); + async_io_thread.join(); + } if (notify_port != nullptr) { + CFRunLoopStop(monitor_run_loop); + monitor_thread.join(); + IONotificationPortDestroy(notify_port); notify_port = nullptr; } @@ -59,17 +62,17 @@ void usb_registry::set_on_device_disconnected(std::function wait_lock(monitor_mutex); + std::unique_lock wait_lock(monitor_mutex); monitor_condition.wait(wait_lock, [this] { return is_device_list_ready; }); } void usb_registry::monitor() { notify_port = IONotificationPortCreate(kIOMainPortDefault); - run_loop_source = IONotificationPortGetRunLoopSource(notify_port); + monitor_run_loop_source = IONotificationPortGetRunLoopSource(notify_port); - run_loop = CFRunLoopGetCurrent(); - CFRunLoopAddSource(run_loop, run_loop_source, kCFRunLoopDefaultMode); + monitor_run_loop = CFRunLoopGetCurrent(); + CFRunLoopAddSource(monitor_run_loop, monitor_run_loop_source, kCFRunLoopDefaultMode); auto matching_dict = IOServiceMatching(kIOUSBDeviceClassName); // Interested in instances of USB device @@ -103,7 +106,11 @@ void usb_registry::monitor() { // iterate to activate notifications device_disconnected(device_disconnected_iter); - is_device_list_ready = true; + { + std::lock_guard lock(monitor_mutex); + is_device_list_ready = true; + } + monitor_condition.notify_all(); // start run loop @@ -123,7 +130,7 @@ void usb_registry::device_connected(io_iterator_t iterator) { auto service_guard = make_scope_exit([service]() { IOObjectRelease(service); }); // Test if the device has a client interface (otherwise it's likely a controller) - IOUSBDeviceInterface** dev = iokit_helper::get_interface(service, kIOUSBDeviceUserClientTypeID, kIOUSBDeviceInterfaceID); + IOUSBDeviceInterface** dev = iokit_helper::get_interface(service, kIOUSBDeviceUserClientTypeID, kIOUSBDeviceInterfaceID182); if (dev == nullptr) continue; @@ -142,7 +149,7 @@ void usb_registry::device_connected(io_iterator_t iterator) { continue; // ignore // Create new device - std::shared_ptr device(new usb_device(service, dev, entry_id, vendor_id, product_id)); + std::shared_ptr device(new usb_device(this, service, dev, entry_id, vendor_id, product_id)); devices.push_back(device); // Call callback function @@ -183,3 +190,41 @@ void usb_registry::device_disconnected(io_iterator_t iterator) { on_disconnected_callback(device); } } + +std::shared_ptr usb_registry::get_shared_ptr(usb_device* device) { + auto it = std::find_if(devices.cbegin(), devices.cend(), [device](auto dev) { return dev.get() == device; }); + if (it == devices.cend()) + return nullptr; + + return *it; +} + +void usb_registry::add_event_source(CFRunLoopSourceRef source) { + std::unique_lock wait_lock(async_io_mutex); + if (async_io_run_loop == nullptr) { + if (async_io_thread.joinable()) { + async_io_condition.wait(wait_lock, [this] { return async_io_run_loop != nullptr; }); + } else { + async_io_thread = std::thread(&usb_registry::async_io_run, this, source); + async_io_condition.wait(wait_lock, [this] { return async_io_run_loop != nullptr; }); + return; + } + } + + CFRunLoopAddSource(async_io_run_loop, source, kCFRunLoopDefaultMode); +} + +void usb_registry::remove_event_source(CFRunLoopSourceRef source) { + CFRunLoopRemoveSource(async_io_run_loop, source, kCFRunLoopDefaultMode); +} + +void usb_registry::async_io_run(CFRunLoopSourceRef first_source) { + { + std::lock_guard lock(async_io_mutex); + async_io_run_loop = CFRunLoopGetCurrent(); + CFRunLoopAddSource(async_io_run_loop, first_source, kCFRunLoopDefaultMode); + } + + async_io_condition.notify_all(); + CFRunLoopRun(); +} diff --git a/reference/macos/USB/usb_registry.hpp b/reference/macos/USB/usb_registry.hpp index 9fd820fc..86e90e5f 100644 --- a/reference/macos/USB/usb_registry.hpp +++ b/reference/macos/USB/usb_registry.hpp @@ -37,6 +37,9 @@ class usb_registry { /// Starts the registry void start(); + /// Indicates if the registry has been started + bool isStarted() { return notify_port != nullptr; } + /// Gets the currently connected devices. std::vector> get_devices(); @@ -49,20 +52,38 @@ class usb_registry { static void device_connected_f(void *refcon, io_iterator_t iterator); static void device_disconnected_f(void *refcon, io_iterator_t iterator); + + std::shared_ptr get_shared_ptr(usb_device* device); + // Add event source to background thread for handling asynchronous IO completion + void add_event_source(CFRunLoopSourceRef source); + // Remove event source to background thread for handling asynchronous IO completion + void remove_event_source(CFRunLoopSourceRef source); + // Main function for background thread + void async_io_run(CFRunLoopSourceRef first_source); std::vector devices; std::function on_connected_callback; std::function on_disconnected_callback; + // monitoring thread, port, run loop, mutex, condition etc. std::thread monitor_thread; - CFRunLoopRef run_loop; + CFRunLoopRef monitor_run_loop; IONotificationPortRef notify_port; - CFRunLoopSourceRef run_loop_source; + CFRunLoopSourceRef monitor_run_loop_source; + std::mutex monitor_mutex; + std::condition_variable monitor_condition; + + // async_io thread and run loop + std::thread async_io_thread; + volatile CFRunLoopRef async_io_run_loop; + std::mutex async_io_mutex; + std::condition_variable async_io_condition; + io_iterator_t device_connected_iter; io_iterator_t device_disconnected_iter; bool is_device_list_ready; - std::mutex monitor_mutex; - std::condition_variable monitor_condition; + + friend class usb_device; }; diff --git a/reference/macos/usb.xcodeproj/project.pbxproj b/reference/macos/usb.xcodeproj/project.pbxproj index 6028251f..b17cf56e 100644 --- a/reference/macos/usb.xcodeproj/project.pbxproj +++ b/reference/macos/usb.xcodeproj/project.pbxproj @@ -7,6 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 64292E7329144D6C004F6169 /* prng.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 64292E7129144D6C004F6169 /* prng.cpp */; }; + 64292E7629144DE1004F6169 /* speed_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 64292E7429144DE1004F6169 /* speed_test.cpp */; }; + 64835E60290BCA9F00113E4E /* config_parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 64835E5D290BCA9F00113E4E /* config_parser.cpp */; }; + 64835E61290BCA9F00113E4E /* configuration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 64835E5F290BCA9F00113E4E /* configuration.cpp */; }; 64937C6428D5EBF1009BD0C2 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 64937C6328D5EBF1009BD0C2 /* main.cpp */; }; 64937C7228D5ED1F009BD0C2 /* usb_registry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 64937C6B28D5ED1F009BD0C2 /* usb_registry.cpp */; }; 64937C7328D5ED1F009BD0C2 /* iokit_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 64937C6C28D5ED1F009BD0C2 /* iokit_helper.cpp */; }; @@ -16,6 +20,7 @@ 64937C7E28D60C82009BD0C2 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 64937C7D28D60C82009BD0C2 /* CoreFoundation.framework */; }; 64937C8128D6F9DE009BD0C2 /* tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 64937C7F28D6F9DE009BD0C2 /* tests.cpp */; }; 64937C8428D71D31009BD0C2 /* assertion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 64937C8228D71D31009BD0C2 /* assertion.cpp */; }; + 649E7E1A298AEB580043B589 /* usb_iostream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 649E7E18298AEB580043B589 /* usb_iostream.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -31,6 +36,15 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 64292E7129144D6C004F6169 /* prng.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = prng.cpp; sourceTree = ""; }; + 64292E7229144D6C004F6169 /* prng.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = prng.hpp; sourceTree = ""; }; + 64292E7429144DE1004F6169 /* speed_test.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = speed_test.cpp; sourceTree = ""; }; + 64292E7529144DE1004F6169 /* speed_test.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = speed_test.hpp; sourceTree = ""; }; + 64443AF22997C56E009283A9 /* blocking_queue.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = blocking_queue.hpp; sourceTree = ""; }; + 64835E5C290BCA9F00113E4E /* configuration.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = configuration.hpp; sourceTree = ""; }; + 64835E5D290BCA9F00113E4E /* config_parser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = config_parser.cpp; sourceTree = ""; }; + 64835E5E290BCA9F00113E4E /* config_parser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = config_parser.hpp; sourceTree = ""; }; + 64835E5F290BCA9F00113E4E /* configuration.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = configuration.cpp; sourceTree = ""; }; 64937C6028D5EBF1009BD0C2 /* USB */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = USB; sourceTree = BUILT_PRODUCTS_DIR; }; 64937C6328D5EBF1009BD0C2 /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; 64937C6B28D5ED1F009BD0C2 /* usb_registry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = usb_registry.cpp; sourceTree = ""; }; @@ -48,6 +62,8 @@ 64937C8028D6F9DE009BD0C2 /* tests.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = tests.hpp; sourceTree = ""; }; 64937C8228D71D31009BD0C2 /* assertion.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = assertion.cpp; sourceTree = ""; }; 64937C8328D71D31009BD0C2 /* assertion.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = assertion.hpp; sourceTree = ""; }; + 649E7E18298AEB580043B589 /* usb_iostream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = usb_iostream.cpp; sourceTree = ""; }; + 649E7E19298AEB580043B589 /* usb_iostream.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = usb_iostream.hpp; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -85,10 +101,19 @@ children = ( 64937C8328D71D31009BD0C2 /* assertion.hpp */, 64937C8228D71D31009BD0C2 /* assertion.cpp */, + 64443AF22997C56E009283A9 /* blocking_queue.hpp */, + 64835E5E290BCA9F00113E4E /* config_parser.hpp */, + 64835E5D290BCA9F00113E4E /* config_parser.cpp */, + 64835E5C290BCA9F00113E4E /* configuration.hpp */, + 64835E5F290BCA9F00113E4E /* configuration.cpp */, 64937C6D28D5ED1F009BD0C2 /* iokit_helper.hpp */, 64937C6C28D5ED1F009BD0C2 /* iokit_helper.cpp */, 64937C6328D5EBF1009BD0C2 /* main.cpp */, + 64292E7229144D6C004F6169 /* prng.hpp */, + 64292E7129144D6C004F6169 /* prng.cpp */, 64937C6F28D5ED1F009BD0C2 /* scope.hpp */, + 64292E7529144DE1004F6169 /* speed_test.hpp */, + 64292E7429144DE1004F6169 /* speed_test.cpp */, 64937C8028D6F9DE009BD0C2 /* tests.hpp */, 64937C7F28D6F9DE009BD0C2 /* tests.cpp */, 64937C7528D5ED8D009BD0C2 /* usb_error.hpp */, @@ -97,6 +122,8 @@ 64937C7728D5FE6D009BD0C2 /* usb_device.cpp */, 64937C6E28D5ED1F009BD0C2 /* usb_registry.hpp */, 64937C6B28D5ED1F009BD0C2 /* usb_registry.cpp */, + 649E7E19298AEB580043B589 /* usb_iostream.hpp */, + 649E7E18298AEB580043B589 /* usb_iostream.cpp */, ); path = USB; sourceTree = ""; @@ -137,7 +164,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastUpgradeCheck = 1400; + LastUpgradeCheck = 1410; TargetAttributes = { 64937C5F28D5EBF1009BD0C2 = { CreatedOnToolsVersion = 14.0; @@ -169,10 +196,15 @@ files = ( 64937C7328D5ED1F009BD0C2 /* iokit_helper.cpp in Sources */, 64937C8428D71D31009BD0C2 /* assertion.cpp in Sources */, + 64835E61290BCA9F00113E4E /* configuration.cpp in Sources */, 64937C7928D5FE6D009BD0C2 /* usb_device.cpp in Sources */, 64937C7228D5ED1F009BD0C2 /* usb_registry.cpp in Sources */, + 64835E60290BCA9F00113E4E /* config_parser.cpp in Sources */, + 649E7E1A298AEB580043B589 /* usb_iostream.cpp in Sources */, 64937C6428D5EBF1009BD0C2 /* main.cpp in Sources */, 64937C7628D5ED8D009BD0C2 /* usb_error.cpp in Sources */, + 64292E7329144D6C004F6169 /* prng.cpp in Sources */, + 64292E7629144DE1004F6169 /* speed_test.cpp in Sources */, 64937C8128D6F9DE009BD0C2 /* tests.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -213,6 +245,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -271,6 +304,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -293,7 +327,9 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 4H9ZA7X4C4; ENABLE_HARDENED_RUNTIME = YES; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -304,7 +340,9 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 4H9ZA7X4C4; ENABLE_HARDENED_RUNTIME = YES; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/reference/macos/usb.xcodeproj/xcshareddata/xcschemes/USB.xcscheme b/reference/macos/usb.xcodeproj/xcshareddata/xcschemes/USB.xcscheme index 46c6a98b..8630fdad 100644 --- a/reference/macos/usb.xcodeproj/xcshareddata/xcschemes/USB.xcscheme +++ b/reference/macos/usb.xcodeproj/xcshareddata/xcschemes/USB.xcscheme @@ -1,6 +1,6 @@ + + + + + + + + + + + + + @@ -60,6 +73,7 @@ true v143 Unicode + false Application @@ -120,6 +134,8 @@ true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true + EnableFastChecks + stdcpp17 Console @@ -134,6 +150,7 @@ true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true + stdcpp17 Console diff --git a/reference/windows/USB/USB.vcxproj.filters b/reference/windows/USB/USB.vcxproj.filters index ffebc144..473ed8c6 100644 --- a/reference/windows/USB/USB.vcxproj.filters +++ b/reference/windows/USB/USB.vcxproj.filters @@ -33,6 +33,24 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -53,5 +71,26 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + \ No newline at end of file diff --git a/reference/windows/USB/assertion.cpp b/reference/windows/USB/assertion.cpp index 2a60687a..cc8be65b 100644 --- a/reference/windows/USB/assertion.cpp +++ b/reference/windows/USB/assertion.cpp @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code common for Linux / macOS / Windows // #include "assertion.hpp" @@ -16,7 +16,7 @@ static void failed(const char* check, const char* message) { if (message == nullptr) message = "Check failed"; std::cerr << message << ": " << check << std::endl; - throw new check_failed_error(); + throw check_failed_error(); } void assert_equals(int expected, int actual, const char* message) { diff --git a/reference/windows/USB/assertion.hpp b/reference/windows/USB/assertion.hpp index 4c458fd3..a97dfa30 100644 --- a/reference/windows/USB/assertion.hpp +++ b/reference/windows/USB/assertion.hpp @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code common for Linux / macOS / Windows // #pragma once diff --git a/reference/windows/USB/blocking_queue.hpp b/reference/windows/USB/blocking_queue.hpp new file mode 100644 index 00000000..154b0230 --- /dev/null +++ b/reference/windows/USB/blocking_queue.hpp @@ -0,0 +1,69 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#pragma once + +#include +#include + + +/** + * Blocking FIFO queue for passing work from one thread to another. + * + * The queue is unbounded. + */ +template +class blocking_queue { +public: + /** + * Indicates if the queue is empty. + * + * @return 'true' if the queue is empty, 'false' if the queue contains elements + */ + bool empty() const { + std::lock_guard lock(guard); + return queue.empty(); + } + + /** + * Adds the item to the end of the queue. + * + * @param item item to add + */ + void put(const T& item) { + { + std::lock_guard lock(guard); + queue.push(item); + } + + signal.notify_one(); + } + + /** + * Takes the oldest item from the queue and removes it. + * + * Waits until an item is available. + * + * @return item removed from queue + */ + T take() { + std::unique_lock lock(guard); + while (queue.empty()) + signal.wait(lock); + + T item = queue.front(); + queue.pop(); + return item; + } + +private: + std::queue queue; + mutable std::mutex guard; + std::condition_variable signal; +}; diff --git a/reference/windows/USB/config_parser.cpp b/reference/windows/USB/config_parser.cpp new file mode 100644 index 00000000..2d75f475 --- /dev/null +++ b/reference/windows/USB/config_parser.cpp @@ -0,0 +1,188 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#include "config_parser.hpp" +#include "usb_error.hpp" + + +// --- USB descriptor types + +enum class usb_descriptor_type : uint8_t { + configuration = 0x02, + string = 0x03, + interface = 0x04, + endpoint = 0x05, + interface_association = 0x0b +}; + + +// --- USB configuration descriptor + +#pragma pack(push, 1) + +struct usb_config_desc { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t bMaxPower; +}; + +#pragma pack(pop) + + +// --- USB interface descriptor + +#pragma pack(push, 1) + +struct usb_interface_desc { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; +}; + +#pragma pack(pop) + + +// --- USB interface association descriptor + +#pragma pack(push, 1) + +struct usb_interface_association_desc { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bFirstInterface; + uint8_t bInterfaceCount; + uint8_t bFunctionClass; + uint8_t bFunctionSubClass; + uint8_t bFunctionProtocol; + uint8_t iFunction; +}; + +#pragma pack(pop) + + +// --- USB endpoint descriptor + +#pragma pack(push, 1) + + struct usb_endpoint_desc { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketSize; + uint8_t bInterval; + }; + +#pragma pack(pop) + + +// --- Configuration parser + + static int peek_desc_length(const uint8_t* desc, int offset) { return desc[offset]; } + static usb_descriptor_type peek_desc_type(const uint8_t* desc, int offset) { return static_cast(desc[offset + 1]); } + + + config_parser::config_parser() + : configuration_value(0) { } + +void config_parser::parse(const uint8_t* config_desc, int desc_len) +{ + const usb_config_desc* header = reinterpret_cast(config_desc); + if (desc_len <= sizeof(usb_config_desc) + || header->bDescriptorType != static_cast(usb_descriptor_type::configuration) + || header->wTotalLength != desc_len) + throw usb_error("Invalid configuration descriptor"); + + configuration_value = header->bConfigurationValue; + + int offset = peek_desc_length(config_desc, 0); + usb_alternate_interface* last_alternate = nullptr; + + while (offset + 2 < desc_len) { + + int len = peek_desc_length(config_desc, offset); + usb_descriptor_type type = peek_desc_type(config_desc, offset); + + if (offset + len > desc_len) + break; + + if (type == usb_descriptor_type::interface_association) { + + const usb_interface_association_desc* ia_desc = reinterpret_cast(config_desc + offset); + functions.push_back(usb_composite_function(ia_desc->bFirstInterface, ia_desc->bInterfaceCount, + ia_desc->bFunctionClass, ia_desc->bFunctionSubClass, ia_desc->bFunctionProtocol)); + last_alternate = nullptr; + + } else if (type == usb_descriptor_type::interface) { + + const usb_interface_desc* intf_desc = reinterpret_cast(config_desc + offset); + int number = intf_desc->bInterfaceNumber; + + // If there is no interface with this number yet, it's a new interface + // and not just an additional alternate interface. + auto intf = get_interface(number); + if (intf == nullptr) { + interfaces.push_back(usb_interface(number)); + intf = &interfaces.back(); + } + + // add alternate interface + last_alternate = intf->add_alternate(usb_alternate_interface(intf_desc->bAlternateSetting, + intf_desc->bInterfaceClass, intf_desc->bInterfaceSubClass, intf_desc->bInterfaceProtocol)); + + // If there is no function for this interface, there was not preceeding IAD. + // So create a new function with a single interface. + if (get_function(intf->number()) == nullptr) + functions.push_back(usb_composite_function(intf->number(), 1, last_alternate->class_code(), last_alternate->subclass_code(), last_alternate->protocol_code())); + + } else if (type == usb_descriptor_type::endpoint) { + + if (last_alternate == nullptr) + throw usb_error("invalid configuration descriptor"); + + const usb_endpoint_desc* ep_desc = reinterpret_cast(config_desc + offset); + last_alternate->add_endpoint(usb_endpoint(ep_desc->bEndpointAddress & 0x7f, static_cast(ep_desc->bEndpointAddress & 0x80), + static_cast(ep_desc->bmAttributes & 0x03), ep_desc->wMaxPacketSize)); + } + + offset += len; + } + + if (offset != desc_len) + throw usb_error("invalid configuration descriptor"); +} + +usb_interface* config_parser::get_interface(int number) { + auto iter = std::find_if(interfaces.begin(), interfaces.end(), [number](const usb_interface& itf) { return itf.number() == number; }); + if (iter == interfaces.end()) + return nullptr; + + return &*iter; +} + +usb_composite_function* config_parser::get_function(int intf_number) { + auto iter = std::find_if(functions.begin(), functions.end(), [intf_number](const usb_composite_function& f) { + return intf_number >= f.first_interface() && intf_number < f.first_interface() + f.num_interfaces(); + }); + if (iter == functions.end()) + return nullptr; + + return &*iter; +} diff --git a/reference/windows/USB/config_parser.hpp b/reference/windows/USB/config_parser.hpp new file mode 100644 index 00000000..04f6615c --- /dev/null +++ b/reference/windows/USB/config_parser.hpp @@ -0,0 +1,31 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#pragma once + +#include +#include "configuration.hpp" + +/// +/// Parses a USB configuration descriptor. +/// +class config_parser { +public: + config_parser(); + void parse(const uint8_t* config_desc, int desc_len); + + uint8_t configuration_value; + + std::vector interfaces; + std::vector functions; + +private: + usb_interface* get_interface(int number); + usb_composite_function* get_function(int intf_number); +}; diff --git a/reference/windows/USB/configuration.cpp b/reference/windows/USB/configuration.cpp new file mode 100644 index 00000000..3db7235c --- /dev/null +++ b/reference/windows/USB/configuration.cpp @@ -0,0 +1,64 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#include "configuration.hpp" + +// --- usb_endpoint + +usb_endpoint::usb_endpoint(int number, usb_direction direction, usb_transfer_type transfer_type, int packet_size) + : number_(number), direction_(direction), transfer_type_(transfer_type), packet_size_(packet_size) { } + +usb_endpoint usb_endpoint::invalid(-1, usb_direction::out, usb_transfer_type::bulk, 0); + + +// --- usb_alternate_interface + +usb_alternate_interface::usb_alternate_interface(int number, int class_code, int subclass_code, int protocol_code) + : number_(number), class_code_(class_code), subclass_code_(subclass_code), protocol_code_(protocol_code) { } + +void usb_alternate_interface::add_endpoint(usb_endpoint&& endpoint) { + endpoints_.push_back(std::move(endpoint)); +} + + +usb_alternate_interface usb_alternate_interface::invalid(-1, 0, 0, 0); + + +// --- usb_interface + +usb_interface::usb_interface(int number) + : number_(number), is_claimed_(false), alternate_index_(0) { } + +const usb_alternate_interface& usb_interface::alternate() const { + if (alternate_index_ < 0 || alternate_index_ >= alternates_.size()) + return usb_alternate_interface::invalid; + return alternates_[alternate_index_]; +} + +void usb_interface::set_claimed(bool claimed) { + is_claimed_ = claimed; +} + + +usb_alternate_interface* usb_interface::add_alternate(usb_alternate_interface&& alternate) { + alternates_.push_back(std::move(alternate)); + return &alternates_.back(); +} + +void usb_interface::set_alternate(int index) { + alternate_index_ = index; +} + +usb_interface usb_interface::invalid(-1); + + +// --- usb_composite_function + +usb_composite_function::usb_composite_function(int first_interface, int num_interfaces, int class_code, int subclass_code, int protocol_code) + : first_interface_(first_interface), num_interfaces_(num_interfaces), class_code_(class_code), subclass_code_(subclass_code), protocol_code_(protocol_code) { } diff --git a/reference/windows/USB/configuration.hpp b/reference/windows/USB/configuration.hpp new file mode 100644 index 00000000..5f3dedc9 --- /dev/null +++ b/reference/windows/USB/configuration.hpp @@ -0,0 +1,162 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#pragma once + +#include +#include + +/// USB endpoint direction +enum class usb_direction : uint8_t { + /// Direction OUT: host to device + out = 0x00, + /// Direction IN: device to host + in = 0x80 +}; + +/// USB endpoint transfer type +enum class usb_transfer_type : uint8_t { + /// Control transfer + control = 0x00, + /// Isochronous transfer + isochronous = 0x01, + /// Bulk transfer + bulk = 0x02, + /// Interrupt transfer + interrupt = 0x03 +}; + +/// +/// USB endpoint +/// +struct usb_endpoint { +public: + /// Endpoint number + int number() const { return number_; } + /// Endpoint direction + usb_direction direction() const { return direction_; } + /// Endpoint transfer type + usb_transfer_type transfer_type() const { return transfer_type_; } + /// Maximum packet size + int packet_size() const { return packet_size_; } + /// Indicates if this endpoint is valid (or a return value indicating an error) + bool is_valid() const { return number_ >= 0; } + +private: + usb_endpoint(int number, usb_direction direction, usb_transfer_type transfer_type, int packet_size); + + int number_; + usb_direction direction_; + usb_transfer_type transfer_type_; + int packet_size_; + + static usb_endpoint invalid; + + friend class config_parser; + friend class usb_device; +}; + +/// +/// USB alternate interface +/// +struct usb_alternate_interface { +public: + /// Alternate number + int number() const { return number_; } + /// Interface class code + int class_code() const { return class_code_; } + /// Interface subclass code + int subclass_code() const { return subclass_code_; } + /// Interface protocol code + int protocol_code() const { return protocol_code_; } + /// List of endpoints + const std::vector& endpoints() const { return endpoints_; } + /// Indicates if this alternate interface is valid (or a return value indicating an error) + bool is_valid() const { return number_ >= 0; } + +private: + usb_alternate_interface(int number, int class_code, int subclass_code, int protocol_code); + void add_endpoint(usb_endpoint&& endpoint); + + int number_; + int class_code_; + int subclass_code_; + int protocol_code_; + std::vector endpoints_; + + static usb_alternate_interface invalid; + + friend class config_parser; + friend struct usb_interface; +}; + +/// +/// USB interface +/// +struct usb_interface { +public: + /// Interface number + int number() const { return number_; } + /// Indicates if interface has been claimed + bool is_claimed() const { return is_claimed_; } + /// Currently selected alternate interface + const usb_alternate_interface& alternate() const; + /// Indicates if this interface is valid (or a return value indicating an error) + bool is_valid() const { return number_ >= 0; } + /// List of all alternate interfaces of this interfaces + const std::vector& alternates() const { return alternates_; } + +private: + usb_interface(int number); + void set_claimed(bool claimed); + usb_alternate_interface* add_alternate(usb_alternate_interface&& alternate); + void set_alternate(int index); + + int number_; + bool is_claimed_; + int alternate_index_; + std::vector alternates_; + + static usb_interface invalid; + + friend class config_parser; + friend class usb_device; +}; + +/// +/// USB composite function +/// +/// For a composite USB device, the composite function describes a single function. +/// A compsite function consists of a single or multiple consecutive interfaces. +/// +/// +struct usb_composite_function { +public: + /// Number of first interface + int first_interface() const { return first_interface_; } + /// Number of interfaces + int num_interfaces() const { return num_interfaces_; } + /// Function class code + int class_code() const { return class_code_; } + /// Function subclass code + int subclass_code() const { return subclass_code_; } + /// Function protocol code + int protocol_code() const { return protocol_code_; } + +private: + usb_composite_function(int first_interface, int num_interfaces, int class_code, int subclass_code, int protocol_code); + + int first_interface_; + int num_interfaces_; + int class_code_; + int subclass_code_; + int protocol_code_; + + friend class config_parser; +}; diff --git a/reference/windows/USB/device_info_set.cpp b/reference/windows/USB/device_info_set.cpp new file mode 100644 index 00000000..6450ec20 --- /dev/null +++ b/reference/windows/USB/device_info_set.cpp @@ -0,0 +1,245 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code for Windows +// + +#include "device_info_set.h" +#include "usb_error.hpp" +#include "scope.hpp" +#include + +device_info_set::device_info_set(HDEVINFO dev_info_set) + : dev_info_set_(dev_info_set), dev_info_data_({ sizeof(dev_intf_data_) }), + has_dev_intf_data_(false), dev_intf_data_({ sizeof(dev_intf_data_) }), iteration_index(-1) +{ +} + +device_info_set device_info_set::of_present_devices(const GUID& interface_guid, const std::wstring& instance_id) { + auto dev_info_set = SetupDiGetClassDevsW(&interface_guid, !instance_id.empty() ? instance_id.c_str() : nullptr, nullptr, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (dev_info_set == INVALID_HANDLE_VALUE) + usb_error::throw_error("internal error (SetupDiGetClassDevsW)"); + return device_info_set(dev_info_set); +} + +device_info_set device_info_set::of_instance(const std::wstring& instance_id) { + auto instance = of_empty(); + instance.add_instance(instance_id); + return instance; +} + +device_info_set device_info_set::of_path(const std::wstring& device_path) { + auto instance = of_empty(); + instance.add_device_path(device_path); + return instance; +} + +device_info_set device_info_set::of_empty() { + auto dev_info_set = SetupDiCreateDeviceInfoList(nullptr, nullptr); + if (dev_info_set == INVALID_HANDLE_VALUE) + usb_error::throw_error("internal error (SetupDiCreateDeviceInfoList)"); + return device_info_set(dev_info_set); +} + +device_info_set::device_info_set(device_info_set&& info_set) noexcept + : dev_info_set_(info_set.dev_info_set_), dev_info_data_(info_set.dev_info_data_), + has_dev_intf_data_(info_set.has_dev_intf_data_), dev_intf_data_(info_set.dev_intf_data_), + iteration_index(info_set.iteration_index) { + info_set.dev_info_set_ = INVALID_HANDLE_VALUE; + info_set.has_dev_intf_data_ = false; +} + +device_info_set::~device_info_set() { + if (dev_info_set_ == INVALID_HANDLE_VALUE) + return; + + if (has_dev_intf_data_) + SetupDiDeleteDeviceInterfaceData(dev_info_set_, &dev_intf_data_); + SetupDiDestroyDeviceInfoList(dev_info_set_); +} + +void device_info_set::add_instance(const std::wstring& instance_id) { + if (SetupDiOpenDeviceInfoW(dev_info_set_, instance_id.c_str(), nullptr, 0, &dev_info_data_) == 0) + throw usb_error("internal error (SetupDiOpenDeviceInfoW)", GetLastError()); +} + +void device_info_set::add_device_path(const std::wstring& device_path) { + if (has_dev_intf_data_) + throw usb_error("calling add_device_path() multiple times is not implemented"); + + // load device information into dev info set + if (SetupDiOpenDeviceInterfaceW(dev_info_set_, device_path.c_str(), 0, &dev_intf_data_) == 0) + usb_error::throw_error("internal error (SetupDiOpenDeviceInterfaceW)"); + has_dev_intf_data_ = true; + + if (SetupDiGetDeviceInterfaceDetailW(dev_info_set_, &dev_intf_data_, nullptr, 0, nullptr, &dev_info_data_) == 0) { + auto err = GetLastError(); + if (err != ERROR_INSUFFICIENT_BUFFER) + throw usb_error("internal error (SetupDiGetDeviceInterfaceDetailW)", err); + } +} + +bool device_info_set::next() { + iteration_index += 1; + + if (SetupDiEnumDeviceInfo(dev_info_set_, iteration_index, &dev_info_data_) == 0) { + auto err = GetLastError(); + if (err == ERROR_NO_MORE_ITEMS) + return false; + throw usb_error("internal error (SetupDiEnumDeviceInfo)", err); + } + + return true; +} + + +uint32_t device_info_set::get_device_property_int(const DEVPROPKEY& prop_key) { + // query property value + DEVPROPTYPE property_type; + uint32_t property_value = -1; + if (!SetupDiGetDevicePropertyW(dev_info_set_, &dev_info_data_, &prop_key, &property_type, reinterpret_cast(&property_value), sizeof(property_value), nullptr, 0)) + usb_error::throw_error("internal error (SetupDiGetDevicePropertyW)"); + + // check property type + if (property_type != DEVPROP_TYPE_UINT32) + throw usb_error("internal error (SetupDiGetDevicePropertyW)"); + + return property_value; +} + +std::vector device_info_set::get_device_property_variable_length(const DEVPROPKEY& prop_key, DEVPROPTYPE expected_type) { + + // query length + DWORD required_size = 0; + DEVPROPTYPE property_type; + if (!SetupDiGetDevicePropertyW(dev_info_set_, &dev_info_data_, &prop_key, &property_type, nullptr, 0, &required_size, 0)) { + DWORD err = GetLastError(); + if (err == ERROR_NOT_FOUND) + return {}; + if (err != ERROR_INSUFFICIENT_BUFFER) + throw usb_error("internal error (SetupDiGetDevicePropertyW)", err); + } + + // check property type + if (property_type != expected_type) + throw usb_error("internal error (SetupDiGetDevicePropertyW)"); + + // query property value + std::vector property_value; + property_value.resize(required_size); + if (!SetupDiGetDevicePropertyW(dev_info_set_, &dev_info_data_, &prop_key, &property_type, &property_value[0], required_size, nullptr, 0)) + usb_error::throw_error("internal error (SetupDiGetDevicePropertyW)"); + + return property_value; +} + +std::wstring device_info_set::get_device_property_string(const DEVPROPKEY& prop_key) { + + auto property_value = get_device_property_variable_length(prop_key, DEVPROP_TYPE_STRING); + if (property_value.size() == 0) + return L""; + return std::wstring(reinterpret_cast(&property_value[0])); +} + +std::vector device_info_set::get_device_property_string_list(const DEVPROPKEY& prop_key) { + auto property_value = get_device_property_variable_length(prop_key, DEVPROP_TYPE_STRING | DEVPROP_TYPEMOD_LIST); + if (property_value.size() == 0) + return {}; + return split_string_list(reinterpret_cast(&property_value[0])); +} + +std::vector device_info_set::split_string_list(const wchar_t* str_list_raw) { + std::vector str_list; + int offset = 0; + while (str_list_raw[offset] != L'\0') { + str_list.push_back(str_list_raw + offset); + offset += static_cast(str_list.back().length()) + 1; + } + + return str_list; +} + +bool device_info_set::is_composite_device() { + std::wstring device_service = get_device_property_string(DEVPKEY_Device_Service); + return lstrcmpiW(device_service.c_str(), L"usbccgp") == 0; +} + +std::wstring device_info_set::get_device_path(const std::wstring& instance_id, const GUID& interface_guid) { + + // get device info set for instance + HDEVINFO dev_info_set = SetupDiGetClassDevsW(&interface_guid, instance_id.c_str(), nullptr, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (dev_info_set == INVALID_HANDLE_VALUE) + usb_error::throw_error("internal error (SetupDiGetClassDevsW)"); + + // ensure the result is destroyed when the scope is left + auto dev_info_set_guard = make_scope_exit([dev_info_set]() { + SetupDiDestroyDeviceInfoList(dev_info_set); + }); + + // retrieve first element of enumeration + SP_DEVICE_INTERFACE_DATA dev_intf_data = { sizeof(dev_intf_data) }; + if (!SetupDiEnumDeviceInterfaces(dev_info_set, nullptr, &interface_guid, 0, &dev_intf_data)) + usb_error::throw_error("internal error (SetupDiEnumDeviceInterfaces)"); + + // retrieve path + uint8_t dev_path_buf[MAX_PATH * sizeof(WCHAR) + sizeof(DWORD)]; + memset(dev_path_buf, 0, sizeof(dev_path_buf)); + PSP_DEVICE_INTERFACE_DETAIL_DATA_W intf_detail_data = reinterpret_cast(dev_path_buf); + intf_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W); + if (!SetupDiGetDeviceInterfaceDetailW(dev_info_set, &dev_intf_data, intf_detail_data, sizeof(dev_path_buf), nullptr, nullptr)) + throw usb_error("Internal error (SetupDiGetDeviceInterfaceDetailA)", GetLastError()); + + return intf_detail_data->DevicePath; +} + +std::wstring device_info_set::get_device_path_by_guid(const std::wstring& instance_id) { + auto device_guids = find_device_interface_guids(); + + CLSID clsid{}; + // use GUIDs to get device path + for (const std::wstring& guid : device_guids) { + if (CLSIDFromString(guid.c_str(), &clsid) != NOERROR) + continue; + + try { + return get_device_path(instance_id.c_str(), clsid); + } + catch (usb_error&) { + // ignore and try next one + } + } + + return {}; +} + +std::vector device_info_set::find_device_interface_guids() { + HKEY reg_key = SetupDiOpenDevRegKey(dev_info_set_, &dev_info_data_, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); + if (reg_key == INVALID_HANDLE_VALUE) + throw usb_error("Cannot open device registry key", GetLastError()); + + auto reg_key_guard = make_scope_exit([reg_key]() { + RegCloseKey(reg_key); + }); + + // read registry value (without buffer, to query length) + DWORD value_type = 0; + DWORD value_size = 0; + LSTATUS res = RegQueryValueExW(reg_key, L"DeviceInterfaceGUIDs", nullptr, &value_type, nullptr, &value_size); + if (res == ERROR_FILE_NOT_FOUND) + return std::vector(); + if (res != 0 && res != ERROR_MORE_DATA) + throw usb_error("Internal error (RegQueryValueExW)", res); + + std::vector str_list_raw; + str_list_raw.resize(value_size); + + // read registry value (with buffer) + res = RegQueryValueExW(reg_key, L"DeviceInterfaceGUIDs", nullptr, &value_type, &str_list_raw[0], &value_size); + if (res != 0) + throw usb_error("Internal error (RegQueryValueExW)", res); + + return split_string_list(reinterpret_cast(&str_list_raw[0])); +} diff --git a/reference/windows/USB/device_info_set.h b/reference/windows/USB/device_info_set.h new file mode 100644 index 00000000..60929951 --- /dev/null +++ b/reference/windows/USB/device_info_set.h @@ -0,0 +1,150 @@ +// +// Java Does USB +// Copyright (c) 2023 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code for Windows +// + +#pragma once + +#include +#include +#include +#undef min +#undef max +#undef LowSpeed +#include + +/** + * Device information set (of Windows Setup API). + * + * An instance of this class represents a device information set (DEVINFO) + * and a current element within the set. + */ +class device_info_set +{ +public: + /** + * Creates a new device info set containing the present devices of the specified device class and + * optionally device instance ID. + * + * After creation, there is no current element. `next()` should be called to iterate the first + * and all subsequent elements. + * + * @param interface_guid device interface class GUID + * @param instance_id device instance ID + * @return device info set + */ + static device_info_set of_present_devices(const GUID& interface_guid, const std::wstring& instance_id = L""); + + /** + * Creates a new device info set containing a single device with the specified instance ID. + * + * The device becomes the current element. The set cannot be iterated. + * + * @param instance_id instance ID + * @return device info set + */ + static device_info_set of_instance(const std::wstring& instance_id); + + /** + * Creates a new device info set containing a single device with the specified path. + * + * The device becomes the current element. The set cannot be iterated. + * + * @param device_path device path + * @return device info set + */ + static device_info_set of_path(const std::wstring& device_path); + + /** + * Creates a new empty device info set. + * + * @return device info set + */ + static device_info_set of_empty(); + + /** + * Iterates to the next element in this set. + * + * @return `true` if there is a current element, `false` if the iteration moved beyond the last element + */ + bool next(); + + /** + * Gets the integer device property of the current element. + * + * @param prop_key property key (`DEVPKEY_xxx`) + * @return property value + */ + uint32_t get_device_property_int(const DEVPROPKEY& prop_key); + + /** + * Gets the string device property of the current element. + * + * @param prop_key property key (`DEVPKEY_xxx`) + * @return property value + */ + std::wstring get_device_property_string(const DEVPROPKEY& prop_key); + + /** + * Gets the string list device property of the current element. + * + * @param prop_key property key (`DEVPKEY_xxx`) + * @return property value + */ + std::vector get_device_property_string_list(const DEVPROPKEY& prop_key); + + /** + * Checks if the current element is a composite device. + * + * @return `true` if it is a composite device + */ + bool is_composite_device(); + + device_info_set(device_info_set&& info_set) noexcept; + ~device_info_set(); + + /** + * Gets the device path for the device with the given device instance ID and device interface class. + * + * @param instance_id device instance ID + * @param interface_guid device interface class GUID + * @return the device path + */ + static std::wstring get_device_path(const std::wstring& instance_id, const GUID& interface_guid); + + /** + * Gets the device path for the device with the given instance ID. + * + * The device path is looked up by checking the GUIDs associated with the current element. + * + * @param instance_id device instance ID + * @return the device path, `nullptr` if not found + */ + std::wstring get_device_path_by_guid(const std::wstring& instance_id); + +private: + device_info_set(HDEVINFO dev_info_set); + device_info_set() = delete; + device_info_set(const device_info_set& info_set) = delete; + device_info_set& operator=(const device_info_set&) = delete; + device_info_set& operator=(device_info_set&& info_set) = delete; + + void add_instance(const std::wstring& instance_id); + void add_device_path(const std::wstring& device_path); + std::vector find_device_interface_guids(); + + std::vector get_device_property_variable_length(const DEVPROPKEY& prop_key, DEVPROPTYPE expected_type); + static std::vector split_string_list(const wchar_t* str_list_raw); + + + HDEVINFO dev_info_set_; + SP_DEVINFO_DATA dev_info_data_; + bool has_dev_intf_data_; + SP_DEVICE_INTERFACE_DATA dev_intf_data_; + int iteration_index; +}; + diff --git a/reference/windows/USB/main.cpp b/reference/windows/USB/main.cpp index 64e85de5..cbb3747c 100644 --- a/reference/windows/USB/main.cpp +++ b/reference/windows/USB/main.cpp @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code common for Linux / macOS / Windows // #include "tests.hpp" diff --git a/reference/windows/USB/prng.cpp b/reference/windows/USB/prng.cpp new file mode 100644 index 00000000..c9cd0b1a --- /dev/null +++ b/reference/windows/USB/prng.cpp @@ -0,0 +1,80 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#include "prng.hpp" + +prng::prng(uint32_t init) : state(init), nbytes(0), bits(0) {} + +void prng::reset(uint32_t init) +{ + state = init; + nbytes = 0; + bits = 0; +} + +uint32_t prng::next() +{ + uint32_t x = state; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + state = x; + return x; +} + +void prng::fill(uint8_t *buf, int len) +{ + for (int i = 0; i < len; i++) + { + if (nbytes == 0) + { + bits = next(); + nbytes = 4; + } + buf[i] = bits; + bits >>= 8; + nbytes--; + } +} + +void prng::fill(std::vector& buf, int len) +{ + if (len == -1 || len > buf.size()) + len = static_cast(buf.size()); + + fill(buf.data(), len); +} + +int prng::verify(const uint8_t *buf, int len) +{ + for (int i = 0; i < len; i++) + { + if (nbytes == 0) + { + bits = next(); + nbytes = 4; + } + + if (buf[i] != (uint8_t)bits) + return i; + + bits >>= 8; + nbytes--; + } + + return -1; +} + +int prng::verify(const std::vector &buf, int len) +{ + if (len == -1 || len > buf.size()) + len = static_cast(buf.size()); + + return verify(buf.data(), len); +} diff --git a/reference/windows/USB/prng.hpp b/reference/windows/USB/prng.hpp new file mode 100644 index 00000000..5214dfa6 --- /dev/null +++ b/reference/windows/USB/prng.hpp @@ -0,0 +1,72 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#pragma once + +#include +#include + +/** + * Pseudo Random Number Generator + */ +struct prng +{ + /** + * Constructs a new instance + * @param init initial value + */ + prng(uint32_t init); + + /** + * Returns the next pseudo random value + * @return pseudo random value + */ + uint32_t next(); + + /** + * Fills the buffer with pseudo random data + * @param buf buffer receiving the random data + * @param len length of the buffer (in bytes) + */ + void fill(uint8_t *buf, int len); + + /** + * Fills the buffer with pseudo random data + * @param buf buffer receiving the random data + * @param len number of bytes to fill (-1 for entire buffer) + */ + void fill(std::vector& buf, int len = -1); + + /** + * Verifies that the passed data matches the next bytes of the sequence. + * @param buf buffer with data to verify + * @param len length of the buffer (in bytes) + * @return -1 if they match, otherwise the position of the difference + */ + int verify(const uint8_t *buf, int len); + + /** + * Verifies that the passed data matches the next bytes of the sequence. + * @param buf buffer with data to verify + * @param len number of bytes to verify (-1 for entire buffer) + * @return -1 if they match, otherwise the position of the difference + */ + int verify(const std::vector& buf, int len = -1); + + /** + * Resets the generator to its initial state. + * @param init initial value + */ + void reset(uint32_t init); + +private: + uint32_t state; + int nbytes; + uint32_t bits; +}; diff --git a/reference/windows/USB/scope.hpp b/reference/windows/USB/scope.hpp index 53e3e39f..85998dba 100644 --- a/reference/windows/USB/scope.hpp +++ b/reference/windows/USB/scope.hpp @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code common for Linux / macOS / Windows // #pragma once diff --git a/reference/windows/USB/speed_test.cpp b/reference/windows/USB/speed_test.cpp new file mode 100644 index 00000000..234aece6 --- /dev/null +++ b/reference/windows/USB/speed_test.cpp @@ -0,0 +1,142 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#include "speed_test.hpp" + +#include "prng.hpp" +#include "usb_error.hpp" + +#include +#include +#include +#include + + +static constexpr uint32_t PRNG_INIT = 0x7b; + + +speed_test::speed_test(usb_device_ptr device, int ep_out, int ep_in) +: device_(device), ep_out_(ep_out), ep_in_(ep_in), processed_bytes(0) { } + +void speed_test::run(int num_bytes) { + reset_buffers(); + start_measurement(); + + std::thread sender(&speed_test::transmit, this, num_bytes / 2); + bool successful = receive(num_bytes / 2); + sender.join(); + + if (successful) + stop_measurement(); +} + +bool speed_test::transmit(int num_bytes) { + + prng seq(PRNG_INIT); + + std::vector buf(2000); + int pos = 0; + + auto is_ptr = device_->open_output_stream(ep_out_); + std::ostream& os = *is_ptr; + + while (num_bytes > 0) { + int n = std::min(num_bytes, static_cast(buf.size())); + try { + seq.fill(buf, n); + os.write(reinterpret_cast(buf.data()), n); + + } catch (usb_error& error) { + std::cerr << std::endl << "ERROR: " << error.what() << " (writing at pos " << pos << ")" << std::endl; + return false; + } + + num_bytes -= n; + pos += n; + update_progress(n); + } + + return true; +} + +bool speed_test::receive(int num_bytes) { + + prng seq(PRNG_INIT); + int pos = 0; + std::vector data; + + auto is_ptr = device_->open_input_stream(ep_in_); + std::istream& is = *is_ptr; + + while (num_bytes > 0) { + int n; + try { + data.resize(2000); + is.read(reinterpret_cast(data.data()), data.capacity()); + n = static_cast(is.gcount()); + if (n <= 0) + break; + data.resize(n); + + int p = seq.verify(data, n); + if (p != -1) { + std::cerr << std::endl << "Invalid data received at pos " << (pos + p) << std::endl; + return false; + } + + } catch (usb_error& error) { + std::cerr << std::endl << "ERROR: " << error.what() << " (reading at pos " << pos << ")" << std::endl; + return false; + } + + num_bytes -= n; + pos += n; + + update_progress(n); + } + + if (num_bytes != 0) { + std::cerr << std::endl << "ERROR: EOF encountered after " << pos << " bytes" << std::endl; + } + + return true; +} + +void speed_test::reset_buffers() { + usb_control_request request_set_value_no_data = { 0 }; + request_set_value_no_data.bmRequestType = usb_control_request::request_type(usb_request_type::direction_out, + usb_request_type::type_vendor, usb_request_type::recipient_interface); + request_set_value_no_data.bRequest = 0x04; + request_set_value_no_data.wIndex = 0; // interface number + device_->control_transfer(request_set_value_no_data); + +} + + +// --- throughput measurement --- + +void speed_test::start_measurement() { + start_time = std::chrono::high_resolution_clock::now(); + processed_bytes = 0; +} + +void speed_test::update_progress(int n) { + std::lock_guard lock(progress_mutex); + processed_bytes += n; +} + +int speed_test::stop_measurement() { + auto end_time = std::chrono::high_resolution_clock::now(); + std::chrono::duration dur = end_time - start_time; + double thr = processed_bytes / dur.count(); + + std::cout << "Throughput: " << std::fixed << std::setprecision(1) << thr << " kByte/s" << std::endl; + + return (int)(thr * 1000); +} diff --git a/reference/windows/USB/speed_test.hpp b/reference/windows/USB/speed_test.hpp new file mode 100644 index 00000000..99f19277 --- /dev/null +++ b/reference/windows/USB/speed_test.hpp @@ -0,0 +1,38 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code common for Linux / macOS / Windows +// + +#pragma once + +#include "usb_device.hpp" + +#include +#include + +class speed_test { +public: + speed_test(usb_device_ptr device, int ep_out, int ep_in); + void run(int num_bytes); + +private: + void reset_buffers(); + bool transmit(int num_bytes); + bool receive(int num_bytes); + + void start_measurement(); + void update_progress(int n); + int stop_measurement(); + + usb_device_ptr device_; + int ep_out_; + int ep_in_; + + std::chrono::time_point start_time; + int processed_bytes; + std::mutex progress_mutex; +}; diff --git a/reference/windows/USB/tests.cpp b/reference/windows/USB/tests.cpp index 73d7da40..54ac0169 100644 --- a/reference/windows/USB/tests.cpp +++ b/reference/windows/USB/tests.cpp @@ -4,11 +4,12 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code common for Linux / macOS / Windows // #include "tests.hpp" #include "assertion.hpp" +#include "speed_test.hpp" #include #include @@ -18,7 +19,7 @@ using random_ushort_engine = std::independent_bits_engine< - std::default_random_engine, CHAR_BIT, unsigned short>; + std::default_random_engine, 16, unsigned short>; void tests::run() { @@ -42,14 +43,22 @@ void tests::run() { void tests::test_current_device() { try { std::cout << "Found test device" << std::endl; + + is_composite = test_device->product_id() == 0xcea0; + loopback_intf = is_composite ? 3 : 0; + loopback_ep_out = is_composite ? 1 : 1; + loopback_ep_in = is_composite ? 2 : 2; + test_device->open(); - test_device->claim_interface(0); + test_device->claim_interface(loopback_intf); test_control_transfers(); test_bulk_transfers(); + test_speed(); - test_device->release_interface(); + test_device->release_interface(loopback_intf); test_device->close(); + std::cout << "Test completed" << std::endl; } catch (const std::exception& e) { @@ -63,7 +72,7 @@ void tests::test_control_transfers() { usb_request_type::type_vendor, usb_request_type::recipient_interface); request_set_value_no_data.bRequest = 0x01; request_set_value_no_data.wValue = 0x9a41; - request_set_value_no_data.wIndex = 0; // interface number + request_set_value_no_data.wIndex = loopback_intf; request_set_value_no_data.wLength = 0; test_device->control_transfer(request_set_value_no_data); @@ -72,7 +81,7 @@ void tests::test_control_transfers() { usb_request_type::type_vendor, usb_request_type::recipient_interface); request_get_data.bRequest = 0x03; request_get_data.wValue = 0; - request_get_data.wIndex = 0; // interface number + request_get_data.wIndex = loopback_intf; request_get_data.wLength = 4; auto data = test_device->control_transfer_in(request_get_data); std::vector expected_data{ 0x41, 0x9a, 0x00, 0x00 }; @@ -84,14 +93,37 @@ void tests::test_control_transfers() { usb_request_type::type_vendor, usb_request_type::recipient_interface); request_set_value_data.bRequest = 0x02; request_set_value_data.wValue = 0; - request_set_value_data.wIndex = 0; // interface number + request_set_value_data.wIndex = loopback_intf; request_set_value_data.wLength = static_cast(sent_value.size()); test_device->control_transfer_out(request_set_value_data, sent_value); data = test_device->control_transfer_in(request_get_data); assert_equals(sent_value, data); + + test_control_transfer_intf(loopback_intf); + + if (is_composite) { + test_device->claim_interface(2); + test_control_transfer_intf(2); + test_device->release_interface(2); + } } +void tests::test_control_transfer_intf(int intf_num) { + usb_control_request request_get_intf_num = { 0 }; + request_get_intf_num.bmRequestType = usb_control_request::request_type(usb_request_type::direction_in, + usb_request_type::type_vendor, usb_request_type::recipient_interface); + request_get_intf_num.bRequest = 0x05; + request_get_intf_num.wValue = 0; + request_get_intf_num.wIndex = intf_num; + request_get_intf_num.wLength = 1; + auto data = test_device->control_transfer_in(request_get_intf_num); + + std::vector expected_data{ (uint8_t)intf_num }; + assert_equals(expected_data, data); +} + + void tests::test_bulk_transfers() { test_loopback(12); test_loopback(130); @@ -111,7 +143,7 @@ void tests::test_loopback(int num_bytes) { std::thread reader([this, &rx_data, num_bytes]() { size_t bytes_read = 0; while (bytes_read < num_bytes) { - auto data = test_device->transfer_in(2, 64); + auto data = test_device->transfer_in(loopback_ep_in); rx_data.insert(rx_data.end(), data.begin(), data.end()); bytes_read += data.size(); } @@ -123,7 +155,7 @@ void tests::test_loopback(int num_bytes) { while (bytes_written < num_bytes) { int size = std::min(chunk_size, num_bytes - bytes_written); std::vector chunk = {random_data.begin() + bytes_written, random_data.begin() + bytes_written + size}; - test_device->transfer_out(1, chunk); + test_device->transfer_out(loopback_ep_out, chunk); bytes_written += size; } @@ -134,6 +166,13 @@ void tests::test_loopback(int num_bytes) { assert_equals(random_data, rx_data); } +void tests::test_speed() { + int packet_size = test_device->get_endpoint(usb_direction::out, 1).packet_size(); + + speed_test test(test_device, 1, 2); + test.run(packet_size == 512 ? 20000000 : 2000000); +} + void tests::on_device(usb_device_ptr device) { if (is_test_device(device)) { test_device = device; @@ -160,5 +199,6 @@ std::vector tests::random_bytes(int num) { } bool tests::is_test_device(usb_device_ptr device) { - return device->vendor_id() == 0xcafe && device->product_id() == 0xceaf; + return device->vendor_id() == 0xcafe + && (device->product_id() == 0xceaf || device->product_id() == 0xcea0); } diff --git a/reference/windows/USB/tests.hpp b/reference/windows/USB/tests.hpp index 992de2b9..dbcd0903 100644 --- a/reference/windows/USB/tests.hpp +++ b/reference/windows/USB/tests.hpp @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code common for Linux / macOS / Windows // #pragma once @@ -20,6 +20,8 @@ class tests { void test_current_device(); void test_control_transfers(); void test_bulk_transfers(); + void test_speed(); + void test_control_transfer_intf(int intf_num); void test_loopback(int num_bytes); @@ -32,5 +34,9 @@ class tests { usb_device_ptr test_device; usb_registry registry; + bool is_composite; + int loopback_intf; + int loopback_ep_out; + int loopback_ep_in; }; diff --git a/reference/windows/USB/usb_device.cpp b/reference/windows/USB/usb_device.cpp index 78e007d6..bcb8b72f 100644 --- a/reference/windows/USB/usb_device.cpp +++ b/reference/windows/USB/usb_device.cpp @@ -4,22 +4,52 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code for Windows // #include "usb_device.hpp" #include "usb_error.hpp" +#include "usb_iostream.hpp" +#include "device_info_set.h" #include "scope.hpp" +#include "config_parser.hpp" +#include "usb_registry.hpp" +#include + +#include #include +#include +#include -usb_device::usb_device(const std::wstring& device_path, int vendor_id, int product_id) -: device_path_(device_path), device_handle_(nullptr), interface_handle_(nullptr), - vendor_id_(vendor_id), product_id_(product_id), is_open_(false) { - - //manufacturer_ = iokit_helper::ioreg_get_property_as_string(service, CFSTR(kUSBVendorString)); - //product_ = iokit_helper::ioreg_get_property_as_string(service, CFSTR(kUSBProductString)); - //serial_number_ = iokit_helper::ioreg_get_property_as_string(service, CFSTR(kUSBSerialNumberString)); +usb_device::usb_device(usb_registry* registry, std::wstring&& device_path, int vendor_id, int product_id, const std::vector& config_desc, bool is_composite) +: registry_(registry), vendor_id_(vendor_id), product_id_(product_id), is_open_(false), device_path_(std::move(device_path)), is_composite_(is_composite) { + + config_parser parser{}; + parser.parse(config_desc.data(), static_cast(config_desc.size())); + interfaces_ = std::move(parser.interfaces); + functions_ = std::move(parser.functions); + + build_handles(device_path_); +} + +void usb_device::set_product_names(const std::string& manufacturer, const std::string& product, const std::string& serial_number) { + manufacturer_ = manufacturer; + product_ = product; + serial_number_ = serial_number; +} + +void usb_device::build_handles(const std::wstring& device_path) { + for (const usb_interface& intf : interfaces_) { + int intf_number = intf.number(); + auto function = get_function(intf_number); + + std::wstring path; + if (intf_number == 0) + path = device_path; + + interface_handles_.push_back(interface_handle(intf_number, function->first_interface(), std::move(path))); + } } usb_device::~usb_device() { @@ -40,13 +70,38 @@ std::string usb_device::description() const { return desc; } +const std::vector& usb_device::interfaces() const { + return interfaces_; +} + +const usb_interface& usb_device::get_interface(int interface_number) const { + for (const usb_interface& intf : interfaces_) { + if (intf.number() == interface_number) + return intf; + } + + return usb_interface::invalid; +} + +const usb_endpoint& usb_device::get_endpoint(usb_direction direction, int endpoint_number) const { + for (const usb_interface& intf : interfaces_) { + for (const usb_endpoint& ep : intf.alternate().endpoints()) { + if (ep.direction() == direction && ep.number() == endpoint_number) + return ep; + } + } + + return usb_endpoint::invalid; +} + + bool usb_device::is_open() const { return is_open_; } void usb_device::open() { if (is_open()) - throw new usb_error("USB device is already open", 0); + throw usb_error("USB device is already open", 0); is_open_ = true; } @@ -54,84 +109,154 @@ void usb_device::open() { void usb_device::close() { if (!is_open()) return; - - if (interface_handle_ != nullptr) - release_interface(); + + for (auto& itf : interfaces_) + if (itf.is_claimed()) + release_interface(itf.number()); is_open_ = false; } void usb_device::claim_interface(int interface_number) { + // When a device is plugged in, a notification is sent. For composite devices, it is a notification + // that the composite device is ready. Each composite function will be registered separately and + // the related information will be available with a delay. So for composite functions, several + // retries might be needed until the device path is available. + int num_retries = 30; // 30 x 100ms + while (true) { + if (try_claim_interface(interface_number)) + return; // success + + num_retries -= 1; + if (num_retries == 0) + throw usb_error("claiming interface failed (function has no device interface GUID/path, might be missing WinUSB driver)"); + + // sleep and retry + std::cerr << "Sleeping for 100ms..." << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } +} + +bool usb_device::try_claim_interface(int interface_number) { + + if (!is_open()) + throw usb_error("USB device is not open"); + + usb_interface* intf = get_intf_ptr(interface_number); + if (intf == nullptr) + throw usb_error("no such interface"); + if (intf->is_claimed()) + throw usb_error("interface has already been claimed"); - if (interface_handle_ != nullptr) - throw usb_error("an interface has already been claimed"); - - auto handle = CreateFileW(device_path_.c_str(), - GENERIC_WRITE | GENERIC_READ, - FILE_SHARE_WRITE | FILE_SHARE_READ, - nullptr, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, - nullptr); - if (handle == INVALID_HANDLE_VALUE) - usb_error::throw_error("Cannot open USB device"); - - - if (!WinUsb_Initialize(handle, &interface_handle_)) { - CloseHandle(handle); - usb_error::throw_error("Cannot open USB device"); - return; + interface_handle* intf_handle = get_interface_handle(interface_number); + interface_handle* first_intf_handle = get_interface_handle(intf_handle->first_interface_num); + + // both the device and the first interface must be opened for any interface belonging to the same function + if (first_intf_handle->device_handle == nullptr) { + auto device_path = get_interface_device_path(first_intf_handle->interface_num); + if (device_path.empty()) + return false; + + std::wcerr << "opening device " << device_path << std::endl; + + // open device + first_intf_handle->device_handle = CreateFileW(device_path.c_str(), + GENERIC_WRITE | GENERIC_READ, + FILE_SHARE_WRITE | FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + nullptr); + if (first_intf_handle->device_handle == INVALID_HANDLE_VALUE) + usb_error::throw_error("failed to claim interface (cannot open USB device)"); + + // open first interface + if (!WinUsb_Initialize(first_intf_handle->device_handle, &first_intf_handle->winusb_handle)) { + auto err = GetLastError(); + CloseHandle(first_intf_handle->device_handle); + first_intf_handle->device_handle = nullptr; + throw usb_error("failed to claim interface (cannot open associated interface)", err); + } + + registry_->add_to_completion_port(first_intf_handle->device_handle); + } + + // open associated interface + if (intf_handle != first_intf_handle) { + if (!WinUsb_GetAssociatedInterface(first_intf_handle->winusb_handle, intf_handle->interface_num - first_intf_handle->interface_num - 1, &intf_handle->winusb_handle)) + throw usb_error("cannot open associated interface", GetLastError()); } - device_handle_ = handle; + first_intf_handle->device_open_count += 1; + intf->set_claimed(true); + return true; } -void usb_device::release_interface() { - if (interface_handle_ == nullptr) - throw usb_error("no interface has been claimed"); +void usb_device::release_interface(int interface_number) { - if (interface_handle_ != nullptr) { - WinUsb_Free(interface_handle_); - interface_handle_ = nullptr; + if (!is_open()) + throw usb_error("USB device is not open"); + + usb_interface* intf = get_intf_ptr(interface_number); + if (intf == nullptr) + throw usb_error("no such interface"); + if (!intf->is_claimed()) + throw usb_error("interface has not been claimed"); + + interface_handle* intf_handle = get_interface_handle(interface_number); + interface_handle* first_intf_handle = get_interface_handle(intf_handle->first_interface_num); + + intf->set_claimed(false); + + if (intf_handle != first_intf_handle) { + // close assicated interface + if (!WinUsb_Free(intf_handle->winusb_handle)) + throw usb_error("failed to release associated interface", GetLastError()); + intf_handle->winusb_handle = nullptr; } - if (device_handle_ != nullptr) { - CloseHandle(device_handle_); - device_handle_ = nullptr; + + // close device if needed + first_intf_handle->device_open_count -= 1; + if (first_intf_handle->device_open_count == 0) { + WinUsb_Free(first_intf_handle->winusb_handle); + CloseHandle(first_intf_handle->device_handle); + first_intf_handle->device_handle = nullptr; } } -std::vector usb_device::transfer_in(int endpoint_number, int data_len, int timeout) { - if (interface_handle_ == nullptr) - throw usb_error("no interface has been claimed"); +std::vector usb_device::transfer_in(int endpoint_number, int timeout) { - UCHAR endpoint_address = endpoint_number + 128; + auto winusb_handle = check_valid_endpoint(usb_direction::in, endpoint_number)->winusb_handle; + UCHAR endpoint_address = ep_address(usb_direction::in, endpoint_number); + auto endpoint = get_endpoint_ptr(usb_direction::in, endpoint_number); ULONG value = timeout; - if (!WinUsb_SetPipePolicy(interface_handle_, endpoint_address, PIPE_TRANSFER_TIMEOUT, sizeof(value), &value)) + if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address, PIPE_TRANSFER_TIMEOUT, sizeof(value), &value)) usb_error::throw_error("Failed to set endpoint timeout"); - std::vector data(data_len); + std::vector data(endpoint->packet_size()); DWORD len = 0; - if (!WinUsb_ReadPipe(interface_handle_, endpoint_address, static_cast(data.data()), data_len, &len, nullptr)) + if (!WinUsb_ReadPipe(winusb_handle, endpoint_address, static_cast(data.data()), endpoint->packet_size(), &len, nullptr)) usb_error::throw_error("Cannot receive from USB endpoint"); data.resize(len); return data; } -void usb_device::transfer_out(int endpoint_number, const std::vector& data, int timeout) { - if (interface_handle_ == nullptr) - throw usb_error("no interface has been claimed"); +void usb_device::transfer_out(int endpoint_number, const std::vector& data, int len, int timeout) { + if (len < 0 || len > data.size()) + len = static_cast(data.size()); - UCHAR endpoint_address = endpoint_number; + auto winusb_handle = check_valid_endpoint(usb_direction::out, endpoint_number)->winusb_handle; + UCHAR endpoint_address = ep_address(usb_direction::out, endpoint_number); ULONG value = timeout; - if (!WinUsb_SetPipePolicy(interface_handle_, endpoint_address, PIPE_TRANSFER_TIMEOUT, sizeof(value), &value)) + if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address, PIPE_TRANSFER_TIMEOUT, sizeof(value), &value)) usb_error::throw_error("Failed to set endpoint timeout"); - DWORD len = 0; - if (!WinUsb_WritePipe(interface_handle_, endpoint_address, const_cast(data.data()), static_cast(data.size()), &len, nullptr)) + DWORD tlen = 0; + if (!WinUsb_WritePipe(winusb_handle, endpoint_address, const_cast(data.data()), len, &tlen, nullptr)) usb_error::throw_error("Failed to transmit to USB endpoint"); } @@ -139,9 +264,11 @@ int usb_device::control_transfer_core(const usb_control_request &request, uint8_ if (!is_open()) throw usb_error("USB device is not open"); + + auto winusb_handle = get_control_transfer_interface_handle(request)->winusb_handle; ULONG value = timeout; - if (!WinUsb_SetPipePolicy(interface_handle_, 0, PIPE_TRANSFER_TIMEOUT, sizeof(value), &value)) + if (!WinUsb_SetPipePolicy(winusb_handle, 0, PIPE_TRANSFER_TIMEOUT, sizeof(value), &value)) usb_error::throw_error("Failed to set endpoint timeout"); WINUSB_SETUP_PACKET setup_packet = { 0 }; @@ -152,27 +279,71 @@ int usb_device::control_transfer_core(const usb_control_request &request, uint8_ setup_packet.Length = request.wLength; DWORD len = 0; - if (!WinUsb_ControlTransfer(interface_handle_, setup_packet, data, request.wLength, &len, nullptr)) + if (!WinUsb_ControlTransfer(winusb_handle, setup_packet, data, request.wLength, &len, nullptr)) usb_error::throw_error("Control transfer failed"); return len; } -void usb_device::control_transfer(const usb_control_request& request, int timeout ) { +usb_device::interface_handle* usb_device::get_control_transfer_interface_handle(const usb_control_request& request) { + usb_request_type recipient = static_cast(static_cast(request.bmRequestType) | 0x03); + int recipient_index = request.wIndex & 0xff; + + int intf_num = -1; + if (recipient == usb_request_type::recipient_interface) { + + intf_num = recipient_index; + + } else if (recipient == usb_request_type::recipient_endpoint) { + + int endpoint_number = recipient_index & 0x7f; + usb_direction direction = static_cast(recipient_index & 0x80); + if (endpoint_number != 0) { + usb_interface* intf = get_endpoint_interface(direction, endpoint_number); + if (intf == nullptr ) + throw usb_error("invalid endpoint number for control request"); + intf_num = intf->number(); + } + + } + + // for control transfer to device, use any claimed interface + if (intf_num < 0) { + for (auto& intf : interfaces_) { + if (intf.is_claimed()) { + intf_num = intf.number(); + break; + } + } + } + + if (intf_num >= 0) { + usb_interface* intf = get_intf_ptr(intf_num); + if (intf == nullptr) + throw usb_error("invalid interface number for control request"); + if (!intf->is_claimed()) + throw usb_error("interface for control request has not been claimed"); + return get_interface_handle(intf_num); + } + + throw usb_error("no interface has been claimed"); +} + +void usb_device::control_transfer(const usb_control_request& request, int timeout) { if (request.wLength != 0) throw usb_error("'control_transfer' only supports request without data phase but 'wLength' != 0"); control_transfer_core(request, nullptr, timeout); } -void usb_device::control_transfer_out(const usb_control_request& request, const std::vector& data, int timeout ) { +void usb_device::control_transfer_out(const usb_control_request& request, const std::vector& data, int timeout) { if ((request.bmRequestType & 0x80) != 0) throw usb_error("direction mismatch between 'control_transfer_out' and direction bit in 'bmRequestType'"); control_transfer_core(request, const_cast(data.data()), timeout); } -std::vector usb_device::control_transfer_in(const usb_control_request& request, int timeout ) { +std::vector usb_device::control_transfer_in(const usb_control_request& request, int timeout) { if ((request.bmRequestType & 0x80) == 0) throw usb_error("direction mismatch between 'control_transfer_in' and direction bit in 'bmRequestType'"); @@ -181,3 +352,209 @@ std::vector usb_device::control_transfer_in(const usb_control_request& data.resize(request.wLength); return data; } + +usb_composite_function* usb_device::get_function(int intf_number) { + auto iter = std::find_if(functions_.begin(), functions_.end(), [intf_number](const usb_composite_function& f) { + return intf_number >= f.first_interface() && intf_number < f.first_interface() + f.num_interfaces(); + }); + + if (iter == functions_.end()) + return nullptr; + + return &*iter; +} + +usb_device::interface_handle* usb_device::get_interface_handle(int intf_number) { + for (auto& intf : interface_handles_) + if (intf.interface_num == intf_number) + return &intf; + + return nullptr; +} + +usb_interface* usb_device::get_intf_ptr(int intf_number) { + for (auto& intf : interfaces_) + if (intf.number() == intf_number) + return &intf; + + return nullptr; +} + +usb_interface* usb_device::get_endpoint_interface(usb_direction direction, int endpoint_number) { + for (auto& intf : interfaces_) + for (auto& ep : intf.alternate().endpoints()) + if (ep.number() == endpoint_number && ep.direction() == direction) + return &intf; + + return nullptr; +} + +const usb_endpoint* usb_device::get_endpoint_ptr(usb_direction direction, int endpoint_number) { + for (auto& intf : interfaces_) + for (auto& ep : intf.alternate().endpoints()) + if (ep.number() == endpoint_number && ep.direction() == direction) + return &ep; + + return nullptr; +} + +usb_device::interface_handle* usb_device::check_valid_endpoint(usb_direction direction, int endpoint_number) { + + if (!is_open()) + throw usb_error("USB device is not open"); + + const usb_endpoint* ep = get_endpoint_ptr(direction, endpoint_number); + if (ep == nullptr) + throw usb_error("no such endpoint"); + if (ep->transfer_type() != usb_transfer_type::bulk && ep->transfer_type() != usb_transfer_type::interrupt) + throw usb_error("invalid transfer type for operation"); + + usb_interface* intf = get_endpoint_interface(direction, endpoint_number); + if (!intf->is_claimed()) + throw usb_error("endpoint's interface has not been claimed"); + + return get_interface_handle(intf->number()); +} + +std::unique_ptr usb_device::open_input_stream(int endpoint_number) { + return std::unique_ptr(new usb_istream(registry_->get_shared_ptr(this), endpoint_number)); +} + +std::unique_ptr usb_device::open_output_stream(int endpoint_number) { + return std::unique_ptr(new usb_ostream(registry_->get_shared_ptr(this), endpoint_number)); +} + +void usb_device::add_completion_handler(OVERLAPPED* overlapped, usb_io_callback* completion_handler) { + registry_->add_completion_handler(overlapped, completion_handler); +} + +void usb_device::remove_completion_handler(OVERLAPPED* overlapped) { + registry_->remove_completion_handler(overlapped); +} + +void usb_device::configure_for_async_io(usb_direction direction, int endpoint_number) { + auto winusb_handle = check_valid_endpoint(direction, endpoint_number)->winusb_handle; + UCHAR endpoint_address = ep_address(direction, endpoint_number); + + ULONG timeout = 0; + if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address, PIPE_TRANSFER_TIMEOUT, sizeof(timeout), &timeout)) + usb_error::throw_error("Failed to set endpoint timeout"); + + UCHAR raw_io = 1; + if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address, RAW_IO, sizeof(raw_io), &raw_io)) + usb_error::throw_error("Failed to set endpoint for raw IO"); +} + +void usb_device::submit_transfer_in(int endpoint_number, uint8_t* buffer, int buffer_len, OVERLAPPED* overlapped) { + + auto winusb_handle = check_valid_endpoint(usb_direction::in, endpoint_number)->winusb_handle; + UCHAR endpoint_address = ep_address(usb_direction::in, endpoint_number); + + if (!WinUsb_ReadPipe(winusb_handle, endpoint_address, buffer, buffer_len, nullptr, overlapped)) { + DWORD err = GetLastError(); + if (err == ERROR_IO_PENDING) + return; + throw usb_error("Failed to submit transfer IN", err); + } +} + +void usb_device::submit_transfer_out(int endpoint_number, uint8_t* data, int data_len, OVERLAPPED* overlapped) { + + auto winusb_handle = check_valid_endpoint(usb_direction::out, endpoint_number)->winusb_handle; + UCHAR endpoint_address = ep_address(usb_direction::out, endpoint_number); + + if (!WinUsb_WritePipe(winusb_handle, endpoint_address, data, data_len, nullptr, overlapped)) { + DWORD err = GetLastError(); + if (err == ERROR_IO_PENDING) + return; + throw usb_error("Failed to submit transfer OUT", err); + } +} + +void usb_device::cancel_transfer(usb_direction direction, int endpoint_number, OVERLAPPED* overlapped) { + auto handle_info = check_valid_endpoint(direction, endpoint_number); + + if (!CancelIoEx(handle_info->device_handle, overlapped)) + usb_error::throw_error("Error on cancelling transfer"); +} + +std::wstring usb_device::get_interface_device_path(int interface_num) { + if (!is_composite_) + return device_path_; + + auto it = interface_device_paths_.find(interface_num); + if (it != interface_device_paths_.end()) + return it->second; + + auto dev_info_set = device_info_set::of_path(device_path_); + + auto children_instance_ids = dev_info_set.get_device_property_string_list(DEVPKEY_Device_Children); + + std::wcerr << "children IDs: "; + for (auto it = children_instance_ids.begin(); it < children_instance_ids.end(); it++) { + if (it != children_instance_ids.begin()) + std::wcerr << ", "; + std::wcerr << *it; + } + std::wcerr << std::endl; + + std::wstring child_path; + for (auto& child_id : children_instance_ids) { + child_path = get_child_device_path(child_id, interface_num); + if (!child_path.empty()) + return child_path; + } + + return {}; // retry later +} + +std::wstring usb_device::get_child_device_path(const std::wstring& child_id, int interface_num) { + + auto dev_info_set = device_info_set::of_instance(child_id); + + auto hardware_ids = dev_info_set.get_device_property_string_list(DEVPKEY_Device_HardwareIds); + if (hardware_ids.empty()) { + std::wcerr << "child device " << child_id << " has no hardware IDs" << std::endl; + return {}; // continue with next child + } + + auto intf_num = extract_interface_number(hardware_ids); + if (intf_num == -1) { + std::wcerr << "child device " << child_id << " has no interface number" << std::endl; + return {}; // continue with next child + } + + if (intf_num != interface_num) + return {}; // continue with next child + + auto device_path = dev_info_set.get_device_path_by_guid(child_id); + if (device_path.empty()) { + std::wcerr << "child device " << child_id << " has no device path" << std::endl; + throw usb_error("claiming interface failed (function has no device interface GUID/path, might be missing WinUSB driver)"); + } + + std::wcerr << "child device: interface=" << intf_num << ", device path=" << device_path << std::endl; + interface_device_paths_[interface_num] = device_path; + return device_path; // success +} + +static const std::wregex multiple_interface_id_pattern(L"USB\\\\VID_[0-9A-Fa-f]{4}&PID_[0-9A-Fa-f]{4}&MI_([0-9A-Fa-f]{2})"); + +int usb_device::extract_interface_number(const std::vector& hardware_ids) { + // Also see https://docs.microsoft.com/en-us/windows-hardware/drivers/install/standard-usb-identifiers#multiple-interface-usb-devices + + for (auto& id : hardware_ids) { + auto matches = std::wsmatch{}; + if (std::regex_search(id, matches, multiple_interface_id_pattern)) + return std::stoul(matches[1].str(), nullptr, 16); + } + + return -1; +} + + +// --- interface_handle + +usb_device::interface_handle::interface_handle(int intf_num, int first_num, std::wstring&& path) + : interface_num(intf_num), first_interface_num(first_num), + device_handle(nullptr), winusb_handle(nullptr), device_open_count(0) { } diff --git a/reference/windows/USB/usb_device.hpp b/reference/windows/USB/usb_device.hpp index bc5c9efa..6f5760c5 100644 --- a/reference/windows/USB/usb_device.hpp +++ b/reference/windows/USB/usb_device.hpp @@ -4,11 +4,14 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code for Windows // #pragma once +#include +#include +#include #include #include #include @@ -20,6 +23,9 @@ #undef LowSpeed #include +#include "configuration.hpp" + +typedef std::function usb_io_callback; /** * USB control request type. @@ -72,6 +78,8 @@ struct usb_control_request { }; +class usb_registry; + /** * USB device. * @@ -93,7 +101,26 @@ class usb_device { std::string serial_number() const { return serial_number_; } /// Descriptive string including VID, PID, manufacturer, product name and serial number std::string description() const; + /// List of interfaces + const std::vector& interfaces() const; + + /** + * Get the USB interface. + * + * @param interface_number interface number + * @return interface or `nullptr` if no such interface exists + */ + const usb_interface& get_interface(int interface_number) const; + /** + * Get a USB endpoint. + * + * @param direction endpoint direction + * @param endpoint_number endpoint number (between 1 and 127) + * @return endpoint or `nullptr` if endpoint does not exist + */ + const usb_endpoint& get_endpoint(usb_direction direction, int endpoint_number) const; + /// Opens the device for communication void open(); @@ -106,25 +133,22 @@ class usb_device { /** * Claims an interface * - * A single interface can be claimed. - * * @param interface_number interface number */ void claim_interface(int interface_number); /** - * Releases the claimed interface. + * Releases a claimed interface. + * + * @param interface_number interface number */ - void release_interface(); + void release_interface(int interface_number); /** * Receives data from a bulk or interrupt endpoint. * * The amount of bytes read will be influced by the underlying USB packets. - * If a short packet is sent, the function will return after having read fewer bytes - * than specified. The function will fail if a bigger packet has been received than - * will fit into the given buffer. So the specified data length should be big enough for - * the maximum packet size (64 bytes for full-speed USB). + * It can be 0 (if the device sends a ZLP) up to the maximum packet size. * * The timeout specifies the maximum time it may take to complete the operation. * If the operation does not complete within that time, the function returns after reading @@ -133,11 +157,10 @@ class usb_device { * Interrupt endpoints do not support timeouts. Thus, 0 has to be specified. * * @param endpoint_number endpoint number (between 1 and 127) - * @param data_len maximum length to read (in bytes) * @param timeout timeout (in ms, 0 for no timeout) * @return received data */ - std::vector transfer_in(int endpoint_number, int data_len, int timeout = 0); + std::vector transfer_in(int endpoint_number, int timeout = 0); /** * Transmits data to a bulk or interrupt endpoint. @@ -150,9 +173,10 @@ class usb_device { * * @param endpoint_number endpoint number (between 1 and 127) * @param data data to transmit + * @param len data length, in bytes (-1 for entire data vector) * @param timeout timeout (in ms, 0 for no timeout) */ - void transfer_out(int endpoint_number, const std::vector& data, int timeout = 0); + void transfer_out(int endpoint_number, const std::vector& data, int len = -1, int timeout = 0); /** * Send a control request with no Data phase. @@ -190,23 +214,87 @@ class usb_device { */ std::vector control_transfer_in(const usb_control_request& request, int timeout = 0); + /** + * Open a new input stream for a bulk endpoint. + * + * The input stream is optimized for maximum throughput. + * + * Do not use the input stream concurrently with other transfer operations on the same endpoint. The input stream + * buffers data for high throughput. When the stream is closed, any data in the buffers will be lost. + * + * @param endpoint_number endpoint number (between 1 and 127) + */ + std::unique_ptr open_input_stream(int endpoint_number); + + /** + * Open a new output stream for a bulk endpoint. + * + * The output stream is optimized for maximum throughput. + * + * Do not use the output stream concurrently with other transfer operations on the same endpoint. + * + * @param endpoint_number endpoint number (between 1 and 127) + */ + std::unique_ptr open_output_stream(int endpoint_number); + private: - usb_device(const std::wstring& device_path, int vendor_id, int product_id); + struct interface_handle { + int interface_num; + int first_interface_num; + HANDLE device_handle; + WINUSB_INTERFACE_HANDLE winusb_handle; + int device_open_count; + + interface_handle(int intf_num, int first_num, std::wstring&& path); + }; + + usb_device(usb_registry* registry, std::wstring&& device_path, int vendor_id, int product_id, const std::vector& config_desc, bool is_composite); + void set_product_names(const std::string& manufacturer, const std::string& product, const std::string& serial_number); + void build_handles(const std::wstring& device_path); + bool try_claim_interface(int interface_number); + std::wstring get_interface_device_path(int interface_num); + std::wstring get_child_device_path(const std::wstring& child_id, int interface_num); + static int extract_interface_number(const std::vector& hardware_ids); + int control_transfer_core(const usb_control_request& request, uint8_t* data, int timeout); - const wchar_t* device_path() { return device_path_.c_str(); } + usb_composite_function* get_function(int intf_number); - std::wstring device_path_; - HANDLE device_handle_; - WINUSB_INTERFACE_HANDLE interface_handle_; + interface_handle* get_interface_handle(int intf_number); + usb_interface* get_intf_ptr(int intf_number); + usb_interface* get_endpoint_interface(usb_direction direction, int endpoint_number); + const usb_endpoint* get_endpoint_ptr(usb_direction direction, int endpoint_number); + interface_handle* get_control_transfer_interface_handle(const usb_control_request& request); + interface_handle* check_valid_endpoint(usb_direction direction, int endpoint_number); + static uint8_t ep_address(usb_direction direction, int endpoint_number) { + return static_cast(direction) | static_cast(endpoint_number); + } + + void configure_for_async_io(usb_direction direction, int endpoint_number); + void add_completion_handler(OVERLAPPED* overlapped, usb_io_callback* completion_handler); + void remove_completion_handler(OVERLAPPED* overlapped); + void submit_transfer_in(int endpoint_number, uint8_t* buffer, int buffer_len, OVERLAPPED* overlapped); + void submit_transfer_out(int endpoint_number, uint8_t* data, int data_len, OVERLAPPED* overlapped); + void cancel_transfer(usb_direction direction, int endpoint_number, OVERLAPPED* overlapped); + + usb_registry* registry_; bool is_open_; int product_id_; int vendor_id_; + std::wstring device_path_; std::string manufacturer_; std::string product_; std::string serial_number_; - + + bool is_composite_; + std::vector interfaces_; + std::vector functions_; + std::vector interface_handles_; + std::map interface_device_paths_; + friend class usb_registry; + friend class usb_istreambuf; + friend class usb_ostreambuf; }; typedef std::shared_ptr usb_device_ptr; diff --git a/reference/windows/USB/usb_error.cpp b/reference/windows/USB/usb_error.cpp index 660b3e4c..2c5da8e4 100644 --- a/reference/windows/USB/usb_error.cpp +++ b/reference/windows/USB/usb_error.cpp @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code for Windows // #include "usb_error.hpp" @@ -36,7 +36,9 @@ std::string usb_error::full_message(const char* message, int code) { LPSTR messageBuffer = nullptr; size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, code, 0, (LPSTR)&messageBuffer, 0, NULL); + nullptr, code, 0, (LPSTR)&messageBuffer, 0, nullptr); + while (size > 0 && (messageBuffer[size - 1] == L'\r' || messageBuffer[size - 1] == '\n')) + size--; std::string msg(message); msg += " ("; diff --git a/reference/windows/USB/usb_error.hpp b/reference/windows/USB/usb_error.hpp index ed529720..7f83beae 100644 --- a/reference/windows/USB/usb_error.hpp +++ b/reference/windows/USB/usb_error.hpp @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code for Windows // #pragma once diff --git a/reference/windows/USB/usb_iostream.cpp b/reference/windows/USB/usb_iostream.cpp new file mode 100644 index 00000000..5d98701a --- /dev/null +++ b/reference/windows/USB/usb_iostream.cpp @@ -0,0 +1,231 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code for Windows +// + +#include "usb_iostream.hpp" +#include "usb_error.hpp" +#include + +// --- usb_istreambuf --- + +usb_istreambuf::usb_istreambuf(usb_device_ptr device, int endpoint_number) +: device(device), endpoint_number(endpoint_number), is_closed(false) { + + setg(nullptr, nullptr, nullptr); + + device->configure_for_async_io(usb_direction::in, endpoint_number); + + buffer_size = 4 * device->get_endpoint(usb_direction::in, endpoint_number).packet_size(); + + // allocate the buffers and submit requests + memset(requests, 0, sizeof(requests)); + for (int i = 0; i < max_outstanding_requests; i++) { + transfer_request* request = &requests[i]; + request->buffer = new uint8_t[buffer_size]; + request->io_completion = [this, request]() { on_completed(request); }; + device->add_completion_handler(&request->overlapped, &request->io_completion); + + if (i == 0) + current_request = request; + else + submit_transfer(request); + } +} + +usb_istreambuf::~usb_istreambuf() { + close(); + + for (int i = 0; i < max_outstanding_requests; i++) + delete[] requests[i].buffer; +} + +void usb_istreambuf::close() { + is_closed = true; + setg(nullptr, nullptr, nullptr); + + // cancel outstanding requests + for (int i = 0; i < max_outstanding_requests; i++) { + if (!requests[i].is_completed) + device->cancel_transfer(usb_direction::in, endpoint_number, &requests[i].overlapped); + } + + // wait until completion handlers have been called + while (num_outstanding_requests > 0) + wait_for_request_completion(); + + // remove completion handlers + for (int i = 0; i < max_outstanding_requests; i++) + device->remove_completion_handler(&requests->overlapped); +} + +void usb_istreambuf::submit_transfer(transfer_request* request) { + request->is_completed = false; + device->submit_transfer_in(endpoint_number, request->buffer, buffer_size, &request->overlapped); + num_outstanding_requests += 1; +} + +void usb_istreambuf::on_completed(transfer_request* request) { + request->is_completed = true; + completed_request_queue.put(request); +} + +usb_istreambuf::transfer_request* usb_istreambuf::wait_for_request_completion() { + transfer_request* request = completed_request_queue.take(); + num_outstanding_requests -= 1; + return request; +} + +usb_istreambuf::int_type usb_istreambuf::underflow() { + if (is_closed) + return traits_type::eof(); + + if (gptr() < egptr()) + return traits_type::to_int_type(*gptr()); + + // loop until non-ZLP has been received + do { + submit_transfer(current_request); + + current_request = wait_for_request_completion(); + if (current_request->result_code() != S_OK) + throw usb_error("transfer IN failed", current_request->result_code()); + + char* buf = reinterpret_cast(current_request->buffer); + int size = current_request->result_size(); + setg(buf, buf, buf + size); + + } while (current_request->result_size() == 0); + + return traits_type::to_int_type(*gptr()); +} + + +// --- usb_ostreambuf --- + +usb_ostreambuf::usb_ostreambuf(usb_device_ptr device, int endpoint_number) +: device(device), endpoint_number(endpoint_number), needs_zlp(false) { + + device->configure_for_async_io(usb_direction::out, endpoint_number); + + packet_size = device->get_endpoint(usb_direction::out, endpoint_number).packet_size(); + buffer_size = 1 * packet_size; + + // create requests + memset(requests, 0, sizeof(requests)); + for (int i = 0; i < max_outstanding_requests; i++) { + transfer_request* request = &requests[i]; + request->buffer = new uint8_t[buffer_size]; + request->io_completion = [this, request](void) { on_completed(request); }; + device->add_completion_handler(&request->overlapped, &request->io_completion); + } + + fill_queue(); +} + +void usb_ostreambuf::fill_queue() { + for (int i = 1; i < max_outstanding_requests; i++) + available_request_queue.put(&requests[i]); + + // configure stream buffer for first request + current_request = &requests[0]; + char* buf = reinterpret_cast(current_request->buffer); + setp(buf, buf + buffer_size); +} + +usb_ostreambuf::~usb_ostreambuf() { + sync(); + + // free buffers + for (int i = 0; i < max_outstanding_requests; i++) { + device->remove_completion_handler(&requests[i].overlapped); + delete[] requests[i].buffer; + } +} + +int usb_ostreambuf::sync() { + // submit request if there is any data in the current buffer + auto size = pptr() - pbase(); + if (size > 0) + submit_transfer((int)size); + + // send a zero-length packet if required + if (needs_zlp) + submit_transfer(0); + + // Wait until all buffers have been transmitted by removing them from the + // queue and reinserting them. One request is the current request. + // So the queue only contains max_outstanding_requests - 1 requests. + for (int i = 0; i < max_outstanding_requests - 1; i++) + wait_for_available_transfer(); + + fill_queue(); + + return 0; +} + +int usb_ostreambuf::overflow (int c) { + // submit request + auto size = pptr() - pbase(); + submit_transfer((int)size); + + // insert char + if (c != traits_type::eof()) { + *pptr() = (char)c; + pbump(1); + } + + return c; +} + +void usb_ostreambuf::submit_transfer(int size) { + device->submit_transfer_out(endpoint_number, current_request->buffer, size, ¤t_request->overlapped); + needs_zlp = size == packet_size; + + current_request = wait_for_available_transfer(); + + // configure stream buffer + char* buf = reinterpret_cast(current_request->buffer); + setp(buf, buf + buffer_size); +} + +void usb_ostreambuf::on_completed(transfer_request* request) { + available_request_queue.put(request); +} + +usb_ostreambuf::transfer_request* usb_ostreambuf::wait_for_available_transfer() { + auto request = available_request_queue.take(); + + // check for error + DWORD result = request->result_code(); + if (result != S_OK) + throw usb_error("transfer OUT failed", result); + + return request; +} + + +// --- usb_istream --- + +usb_istream::usb_istream(usb_device_ptr device, int ep_num) + : std::istream(new usb_istreambuf(device, ep_num)) {} + +usb_istream::~usb_istream() { + // deallocate stream buffer + delete rdbuf(); +} + + +// --- usb_ostream --- + +usb_ostream::usb_ostream(usb_device_ptr device, int ep_num) + : std::ostream(new usb_ostreambuf(device, ep_num)) {} + +usb_ostream::~usb_ostream() { + // deallocate stream buffer + delete rdbuf(); +} diff --git a/reference/windows/USB/usb_iostream.hpp b/reference/windows/USB/usb_iostream.hpp new file mode 100644 index 00000000..db3dcac2 --- /dev/null +++ b/reference/windows/USB/usb_iostream.hpp @@ -0,0 +1,170 @@ +// +// Java Does USB +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Reference C++ code for Windows +// + +#pragma once + +#include +#include +#include +#include "usb_device.hpp" +#include "blocking_queue.hpp" + +/** + * Input stream buffer for USB bulk or interrupt endpoint. + * + * The stream buffer is internally used by an input stream. It submits multiple asynchronous IO requests to + * achieve maximum throughput. + */ +class usb_istreambuf : public std::streambuf { +public: + /// Constructor + usb_istreambuf(usb_device_ptr device, int ep_num); + /// Destructor + virtual ~usb_istreambuf(); + +protected: + /// Called when the internal buffer has no further data to read. + virtual int_type underflow(); + +private: + /// Transfer request + struct transfer_request { + /// buffer for recevied data + uint8_t* buffer; + /// IO completion handler + usb_io_callback io_completion; + /// data structure for overlapped requests + OVERLAPPED overlapped; + /// indicates if the request has completed + bool is_completed; + + DWORD result_code() { + return static_cast(overlapped.Internal); + } + + DWORD result_size() { + return static_cast(overlapped.InternalHigh); + } + }; + + void submit_transfer(transfer_request* request); + + void close(); + void on_completed(transfer_request* request); + transfer_request* wait_for_request_completion(); + + /// Maximum number of concurrently outstanding requests + static constexpr int max_outstanding_requests = 4; + + /// USB device + usb_device_ptr device; + /// endpoint number + int endpoint_number; + /// Indicates that this stream buffer is closed + bool is_closed; + /// buffer size + int buffer_size; + /// transfer requests + transfer_request requests[max_outstanding_requests]; + /// queue with completed requests + blocking_queue completed_request_queue; + /// number of outstanding requests (requests pending with OS and requests in queue) + int num_outstanding_requests; + /// current request being read from + transfer_request* current_request; + + friend class usb_istream; +}; + +/** + * Output stream buffer for USB bulk or interrupt endpoint. + * + * The stream buffer is internally used by an output stream. It submits multiple asynchronous IO requests to + * achieve maximum throughput. + */ +class usb_ostreambuf : public std::streambuf { +public: + /// Constructor + usb_ostreambuf(usb_device_ptr device, int ep_num); + /// Destructor + virtual ~usb_ostreambuf(); + + virtual int sync(); + +protected: + /// Called when the internal buffer has no space left to add more data. + virtual int overflow (int c); + +private: + /// Transfer request + struct transfer_request { + /// buffer for recevied data + uint8_t* buffer; + /// overlapped data structure + OVERLAPPED overlapped; + /// IO completion handler + usb_io_callback io_completion; + + DWORD result_code() { + return static_cast(overlapped.Internal); + } + + DWORD result_size() { + return static_cast(overlapped.InternalHigh); + } + }; + + void fill_queue(); + void submit_transfer(int size); + transfer_request* wait_for_available_transfer(); + + void on_completed(transfer_request* request); + + /// Maximum number of concurrently outstanding requests + static constexpr int max_outstanding_requests = 4; + + /// USB device + usb_device_ptr device; + /// endpoint number + int endpoint_number; + /// Indicates if a zero-length packet is required + bool needs_zlp; + /// packet size + int packet_size; + /// buffer size + int buffer_size; + /// transfer requests + transfer_request requests[max_outstanding_requests]; + /// queue with available requests + blocking_queue available_request_queue; + /// current request being written to + transfer_request* current_request; +}; + +/** + * Input stream for reading from a USB bulk endpoint + */ +class usb_istream : public std::istream { +public: + /// Constructor + usb_istream(usb_device_ptr device, int ep_num); + /// Destructor + ~usb_istream(); +}; + +/** + * Output stream for writing to a USB bulk endpoint + */ +class usb_ostream : public std::ostream { +public: + /// Constructor + usb_ostream(usb_device_ptr device, int ep_num); + /// Destructor + ~usb_ostream(); +}; diff --git a/reference/windows/USB/usb_registry.cpp b/reference/windows/USB/usb_registry.cpp index c508e59f..309c6390 100644 --- a/reference/windows/USB/usb_registry.cpp +++ b/reference/windows/USB/usb_registry.cpp @@ -4,11 +4,12 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code for Windows // #include "usb_registry.hpp" #include "usb_device.hpp" +#include "device_info_set.h" #include "usb_error.hpp" #include "scope.hpp" @@ -25,17 +26,21 @@ #pragma comment (lib, "SetupAPI.lib") #pragma comment (lib, "Winusb.lib") - usb_registry::usb_registry() : on_connected_callback(nullptr), on_disconnected_callback(nullptr), - is_device_list_ready(false), background_thread_id_(0), message_window(nullptr) { + is_device_list_ready(false), monitor_thread_id_(0), message_window(nullptr), + async_io_completion_port(nullptr) { } usb_registry::~usb_registry() { - SendMessage(message_window, WM_CLOSE, 0, 0); monitor_thread.join(); + if (async_io_completion_port != nullptr) { + PostQueuedCompletionStatus(async_io_completion_port, 0, -1, nullptr); + async_io_thread.join(); + CloseHandle(async_io_completion_port); + } } std::vector usb_registry::get_devices() { @@ -53,7 +58,7 @@ void usb_registry::set_on_device_disconnected(std::function wait_lock(monitor_mutex); + std::unique_lock wait_lock(monitor_mutex); monitor_condition.wait(wait_lock, [this] { return is_device_list_ready; }); } @@ -62,7 +67,7 @@ static const LPCWSTR WINDOW_NAME = L"USB device monitor"; void usb_registry::monitor() { - background_thread_id_ = GetCurrentThreadId(); + monitor_thread_id_ = GetCurrentThreadId(); HMODULE instance = GetModuleHandleW(nullptr); WNDCLASSEXW wx = { 0 }; @@ -101,7 +106,7 @@ void usb_registry::monitor() { notification_filter.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE; HDEVNOTIFY notify_handle = RegisterDeviceNotificationW(message_window, ¬ification_filter, DEVICE_NOTIFY_WINDOW_HANDLE /* | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES */); - if (notify_handle == NULL) + if (notify_handle == nullptr) usb_error::throw_error("internal error (RegisterDeviceNotificationW)"); auto notify_handle_guard = make_scope_exit([notify_handle]() { UnregisterDeviceNotification(notify_handle); }); @@ -119,51 +124,53 @@ void usb_registry::monitor() { void usb_registry::detect_present_devices() { - // get device information set of all USB devices present - HDEVINFO dev_info_set_hdl = SetupDiGetClassDevsW(&GUID_DEVINTERFACE_USB_DEVICE, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - if (dev_info_set_hdl == INVALID_HANDLE_VALUE) - throw usb_error("internal error (SetupDiGetClassDevsA)", GetLastError()); + // get device information set of all present USB devices + auto dev_info_set = device_info_set::of_present_devices(GUID_DEVINTERFACE_USB_DEVICE); - // ensure the result id destroyed when the scope is left - auto dev_info_set_guard = make_scope_exit([dev_info_set_hdl]() { - SetupDiDestroyDeviceInfoList(dev_info_set_hdl); - }); + std::map hub_handles{}; - SP_DEVINFO_DATA dev_info = { sizeof(dev_info) }; + auto hub_handle_guard = make_scope_exit([&hub_handles]() { + for (auto& hub : hub_handles) + CloseHandle(hub.second); + }); // iterate over the set - for (int i = 0; ; i++) { - if (!SetupDiEnumDeviceInfo(dev_info_set_hdl, i, &dev_info)) { - DWORD err = GetLastError(); - if (err == ERROR_NO_MORE_ITEMS) - break; - throw usb_error("Internal error (SetupDiEnumDeviceInfo)", err); - } + while (dev_info_set.next()) { + + auto instance_id = dev_info_set.get_device_property_string(DEVPKEY_Device_InstanceId); + auto device_path = device_info_set::get_device_path(instance_id, GUID_DEVINTERFACE_USB_DEVICE); + + std::wcerr << "Device present: InstanceId=" << instance_id << ", DevicePath=" << device_path << std::endl; // create new device - auto device = create_device(dev_info_set_hdl, &dev_info); + auto device = create_device_from_device_info(dev_info_set, std::move(device_path), hub_handles); devices.push_back(device); } } -std::shared_ptr usb_registry::create_device(HDEVINFO dev_info_set_hdl, SP_DEVINFO_DATA* dev_info) { - - DWORD usb_port_num = get_device_property_int(dev_info_set_hdl, dev_info, &DEVPKEY_Device_Address); - std::wstring instance_id = get_device_property_string(dev_info_set_hdl, dev_info, &DEVPKEY_Device_InstanceId); - std::wstring parent_instance_id = get_device_property_string(dev_info_set_hdl, dev_info, &DEVPKEY_Device_Parent); +std::shared_ptr usb_registry::create_device_from_device_info(device_info_set& dev_info_set, std::wstring&& device_path, std::map& hub_handles) { - std::wstring hub_path = get_device_path(parent_instance_id, &GUID_DEVINTERFACE_USB_HUB); + DWORD usb_port_num = dev_info_set.get_device_property_int(DEVPKEY_Device_Address); + std::wstring parent_instance_id = dev_info_set.get_device_property_string(DEVPKEY_Device_Parent); + std::wstring hub_path = device_info_set::get_device_path(parent_instance_id, GUID_DEVINTERFACE_USB_HUB); - // open parent (hub) - HANDLE hub_handle = CreateFileW(hub_path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr); - if (hub_handle == INVALID_HANDLE_VALUE) - usb_error::throw_error("Cannot open USB hub"); + // open parent (hub) if not open + HANDLE hub_handle; + auto it = hub_handles.find(hub_path); + if (it != hub_handles.end()) { + hub_handle = it->second; + } + else { + hub_handle = CreateFileW(hub_path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr); + if (hub_handle == INVALID_HANDLE_VALUE) + usb_error::throw_error("Cannot open USB hub"); + hub_handles[hub_path] = hub_handle; + } - auto hub_handle_guard = make_scope_exit([hub_handle]() { - CloseHandle(hub_handle); - }); + return create_device(std::move(device_path), dev_info_set.is_composite_device(), hub_handle, usb_port_num); +} - auto path = get_device_path(instance_id, &GUID_DEVINTERFACE_USB_DEVICE); +std::shared_ptr usb_registry::create_device(std::wstring&& device_path, bool is_composite, HANDLE hub_handle, DWORD usb_port_num) { // get device descriptor USB_NODE_CONNECTION_INFORMATION_EX conn_info = { 0 }; @@ -172,8 +179,19 @@ std::shared_ptr usb_registry::create_device(HDEVINFO dev_info_set_hd if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, &conn_info, sizeof(conn_info), &conn_info, sizeof(conn_info), &size, nullptr)) usb_error::throw_error("Internal error (cannot get device descriptor)"); + int vendorId = conn_info.DeviceDescriptor.idVendor; + int productId = conn_info.DeviceDescriptor.idProduct; + + // get configuration descriptor + auto config_desc = get_descriptor(hub_handle, usb_port_num, USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, 0); + // Create new device - std::shared_ptr device(new usb_device(path, conn_info.DeviceDescriptor.idVendor, conn_info.DeviceDescriptor.idProduct)); + std::shared_ptr device(new usb_device(this, std::move(device_path), vendorId, productId, config_desc, is_composite)); + device->set_product_names( + get_string(hub_handle, usb_port_num, conn_info.DeviceDescriptor.iManufacturer), + get_string(hub_handle, usb_port_num, conn_info.DeviceDescriptor.iProduct), + get_string(hub_handle, usb_port_num, conn_info.DeviceDescriptor.iSerialNumber) + ); return device; } @@ -186,19 +204,19 @@ LRESULT usb_registry::handle_windows_message(HWND hWnd, UINT uMsg, WPARAM wParam CREATESTRUCT* cs = reinterpret_cast(lParam); self = reinterpret_cast(cs->lpCreateParams); SetLastError(ERROR_SUCCESS); - LONG_PTR result = SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast(self)); + LONG_PTR result = SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(self)); break; } case WM_DESTROY: { - LONG_PTR result = SetWindowLongPtr(hWnd, GWLP_USERDATA, NULL); + LONG_PTR result = SetWindowLongPtrW(hWnd, GWLP_USERDATA, NULL); PostQuitMessage(0); break; } } if (self != nullptr && self->handle_message(hWnd, uMsg, wParam, lParam)) - return NULL; + return 0; return DefWindowProcW(hWnd, uMsg, wParam, lParam); } @@ -214,43 +232,37 @@ bool usb_registry::handle_message(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lP DEV_BROADCAST_DEVICEINTERFACE_W* broadcast = reinterpret_cast(lParam); if (wParam == DBT_DEVICEARRIVAL) { + std::wcerr << "Device added: DevicePath=" << broadcast->dbcc_name << std::endl; on_device_connected(broadcast->dbcc_name); - } - else { + } else { + std::wcerr << "Device removed: DevicePath=" << broadcast->dbcc_name << std::endl; on_device_disconnected(broadcast->dbcc_name); } return true; } void usb_registry::on_device_connected(const WCHAR* path) { - // get as set of all USB devices present - HDEVINFO dev_info_set_hdl = SetupDiGetClassDevsW(&GUID_DEVINTERFACE_USB_DEVICE, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - if (dev_info_set_hdl == INVALID_HANDLE_VALUE) - usb_error::throw_error("internal error (SetupDiGetClassDevsW)"); - - // ensure the result is destroyed when the scope is left - auto dev_info_set_guard = make_scope_exit([dev_info_set_hdl]() { - SetupDiDestroyDeviceInfoList(dev_info_set_hdl); - }); - SP_DEVICE_INTERFACE_DATA dev_intf_data = { sizeof(dev_intf_data) }; - if (!SetupDiOpenDeviceInterfaceW(dev_info_set_hdl, path, 0, &dev_intf_data)) - usb_error::throw_error("internal error (SetupDiOpenDeviceInterfaceW)"); + usb_device_ptr device; + try { + // create device information set + auto dev_info_set = device_info_set::of_path(path); - auto dev_intf_data_guard = make_scope_exit([dev_info_set_hdl, &dev_intf_data]() { - SetupDiDeleteDeviceInterfaceData(dev_info_set_hdl, &dev_intf_data); - }); + std::map hub_handles{}; + auto hub_handle_guard = make_scope_exit([&hub_handles]() { + for (auto& hub : hub_handles) + CloseHandle(hub.second); + }); - SP_DEVINFO_DATA dev_info = { sizeof(dev_info) }; - if (!SetupDiGetDeviceInterfaceDetailW(dev_info_set_hdl, &dev_intf_data, nullptr, 0, nullptr, &dev_info)) { - DWORD err = GetLastError(); - if (err != ERROR_INSUFFICIENT_BUFFER) - throw usb_error("internal error (SetupDiGetDeviceInterfaceDetailW)", err); + // create new device + device = create_device_from_device_info(dev_info_set, path, hub_handles); + devices.push_back(device); + } + catch (const std::exception& e) { + std::cerr << "Exception while connecting device: " << e.what() << std::endl; + std::cerr << "Ignoring." << std::endl; + return; } - - // create new device - auto device = create_device(dev_info_set_hdl, &dev_info); - devices.push_back(device); // Call callback function if (on_connected_callback != nullptr) { @@ -258,24 +270,32 @@ void usb_registry::on_device_connected(const WCHAR* path) { on_connected_callback(device); } catch (const std::exception& e) { - std::cerr << "Unhandled exception: " << e.what() << std::endl; + std::cerr << "Unhandled exception in callback: " << e.what() << std::endl; } catch (...) { - std::cerr << "Unhandled exception (not derived from std::exception)" << std::endl; + std::cerr << "Unhandled exception in callback (not derived from std::exception)" << std::endl; } } } void usb_registry::on_device_disconnected(const WCHAR* path) { - // find device in device list - auto it = std::find_if(devices.cbegin(), devices.cend(), [path](auto device) { return lstrcmpiW(path, device->device_path()) == 0; }); - if (it == devices.cend()) - return; // not part of the device list + usb_device_ptr device; + try { + // find device in device list + auto it = std::find_if(devices.cbegin(), devices.cend(), [path](auto device) { return lstrcmpiW(path, device->device_path_.c_str()) == 0; }); + if (it == devices.cend()) + return; // not part of the device list - // remove from device list - usb_device_ptr device = *it; - devices.erase(it); + // remove from device list + device = *it; + devices.erase(it); + } + catch (const std::exception& e) { + std::cerr << "Exception while disconnecting device: " << e.what() << std::endl; + std::cerr << "Ignoring." << std::endl; + return; + } // call callback function if (on_disconnected_callback != nullptr) { @@ -291,68 +311,127 @@ void usb_registry::on_device_disconnected(const WCHAR* path) { } } -uint32_t usb_registry::get_device_property_int(HDEVINFO dev_info, SP_DEVINFO_DATA* dev_info_data, const DEVPROPKEY* prop_key) { - // query property value - DEVPROPTYPE property_type; - uint32_t property_value = -1; - if (!SetupDiGetDevicePropertyW(dev_info, dev_info_data, prop_key, &property_type, reinterpret_cast(&property_value), sizeof(property_value), nullptr, 0)) - usb_error::throw_error("internal error (SetupDiGetDevicePropertyW)"); +std::shared_ptr usb_registry::get_shared_ptr(usb_device* device) { + auto it = std::find_if(devices.cbegin(), devices.cend(), [device](auto dev) { return dev.get() == device; }); + if (it == devices.cend()) + return nullptr; + + return *it; +} + +void usb_registry::async_io_run() { + + while (true) { + OVERLAPPED* overlapped = nullptr; + DWORD num_bytes = 0; + ULONG_PTR completion_key = 0; + if (!GetQueuedCompletionStatus(async_io_completion_port, &num_bytes, &completion_key, &overlapped, INFINITE) && overlapped == nullptr) + usb_error::throw_error("internal error (GetQueuedCompletionStatus)"); - // check property type - if (property_type != DEVPROP_TYPE_UINT32) - throw usb_error("internal error (SetupDiGetDevicePropertyW)"); + if (overlapped == nullptr) + return; // registry is closing - return property_value; + usb_io_callback* completion_handler = get_completion_handler(overlapped); + if (overlapped == nullptr) + continue; // might be completion from synchronous operation + + (*completion_handler)(); + } } -std::wstring usb_registry::get_device_property_string(HDEVINFO dev_info, SP_DEVINFO_DATA* dev_info_data, const DEVPROPKEY* prop_key) { +void usb_registry::add_to_completion_port(HANDLE handle) { + HANDLE port_handle = CreateIoCompletionPort(handle, async_io_completion_port, 0xd03fbc01, 0); + if (port_handle == nullptr) + usb_error::throw_error("internal error (CreateIoCompletionPort)"); - // query length - DWORD required_size = 0; - DEVPROPTYPE property_type; - if (!SetupDiGetDevicePropertyW(dev_info, dev_info_data, prop_key, &property_type, nullptr, 0, &required_size, 0)) { - DWORD err = GetLastError(); - if (err != ERROR_INSUFFICIENT_BUFFER) - throw usb_error("internal error (SetupDiGetDevicePropertyW)", err); + if (async_io_completion_port == nullptr) { + async_io_completion_port = port_handle; + async_io_thread = std::thread(&usb_registry::async_io_run, this); } +} - // check property type - if (property_type != DEVPROP_TYPE_STRING) - throw usb_error("internal error (SetupDiGetDevicePropertyW)"); +void usb_registry::add_completion_handler(OVERLAPPED* overlapped, usb_io_callback* completion_handler) { + std::lock_guard lock(async_io_mutex); - // query property value - std::wstring property_value; - property_value.resize(required_size / sizeof(WCHAR)); - if (!SetupDiGetDevicePropertyW(dev_info, dev_info_data, prop_key, &property_type, reinterpret_cast(&property_value[0]), required_size, nullptr, 0)) - usb_error::throw_error("internal error (SetupDiGetDevicePropertyW)"); + async_io_completion_handlers.insert(std::pair(overlapped, completion_handler)); +} - return property_value; +void usb_registry::remove_completion_handler(OVERLAPPED* overlapped) { + std::lock_guard lock(async_io_mutex); + + async_io_completion_handlers.erase(overlapped); } -std::wstring usb_registry::get_device_path(const std::wstring& instance_id, const GUID* interface_guid) { +usb_io_callback* usb_registry::get_completion_handler(OVERLAPPED* overlapped) { + std::lock_guard lock(async_io_mutex); + + auto it = async_io_completion_handlers.find(overlapped); + if (it == async_io_completion_handlers.end()) + return nullptr; - // get device info set for instance - HDEVINFO dev_info_set_hdl = SetupDiGetClassDevsW(interface_guid, instance_id.c_str(), nullptr, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - if (dev_info_set_hdl == INVALID_HANDLE_VALUE) - usb_error::throw_error("internal error (SetupDiGetClassDevsW)"); + return it->second; +} - // ensure the result is destroyed when the scope is left - auto dev_info_set_guard = make_scope_exit([dev_info_set_hdl]() { - SetupDiDestroyDeviceInfoList(dev_info_set_hdl); +std::vector usb_registry::get_descriptor(HANDLE hub_handle, ULONG usb_port_num, uint16_t descriptor_type, int index, int language_id, int request_size) { + int size = sizeof(USB_DESCRIPTOR_REQUEST) + (request_size != 0 ? request_size : 255); + uint8_t* descriptor_request_buffer = new uint8_t[size]; + auto dev_info_set_guard = make_scope_exit([descriptor_request_buffer]() { + delete[] descriptor_request_buffer; }); - // retrieve first element of enumeration - SP_DEVICE_INTERFACE_DATA dev_intf_data = { sizeof(dev_intf_data) }; - if (!SetupDiEnumDeviceInterfaces(dev_info_set_hdl, nullptr, interface_guid, 0, &dev_intf_data)) - usb_error::throw_error("internal error (SetupDiEnumDeviceInterfaces)"); - - // retrieve path - uint8_t dev_path_buf[MAX_PATH * sizeof(WCHAR) + sizeof(DWORD)]; - memset(dev_path_buf, 0, sizeof(dev_path_buf)); - PSP_DEVICE_INTERFACE_DETAIL_DATA_W intf_detail_data = reinterpret_cast(dev_path_buf); - intf_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W); - if (!SetupDiGetDeviceInterfaceDetailW(dev_info_set_hdl, &dev_intf_data, intf_detail_data, sizeof(dev_path_buf), nullptr, nullptr)) - throw usb_error("Internal error (SetupDiGetDeviceInterfaceDetailA)", GetLastError()); - - return intf_detail_data->DevicePath; -} \ No newline at end of file + // setup request data structure + USB_DESCRIPTOR_REQUEST* descriptor_request = reinterpret_cast(descriptor_request_buffer); + descriptor_request->ConnectionIndex = usb_port_num; + descriptor_request->SetupPacket.bmRequest = 0x80; // device-to-host / type standard / recipient device + descriptor_request->SetupPacket.bRequest = 0x06; // GET_DESCRIPTOR + descriptor_request->SetupPacket.wValue = (descriptor_type << 8) | index; + descriptor_request->SetupPacket.wIndex = language_id; + descriptor_request->SetupPacket.wLength = static_cast(size - sizeof(USB_DESCRIPTOR_REQUEST)); + + // get descriptor + DWORD bytesReturned = 0; + if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, descriptor_request, size, descriptor_request, size, &bytesReturned, nullptr)) + throw usb_error("Cannot retrieve descriptor (DeviceIoControl)", GetLastError()); + int data_size = bytesReturned - sizeof(USB_DESCRIPTOR_REQUEST); + + if (data_size <= 2) + throw usb_error("invalid descriptor"); + + // determine expected size of descriptor + int expected_size; + if (descriptor_type != USB_CONFIGURATION_DESCRIPTOR_TYPE) { + expected_size = descriptor_request->Data[0]; + } + else { + auto config_desc = reinterpret_cast(descriptor_request->Data); + expected_size = config_desc->wTotalLength; + } + + // check against effective size + if (data_size < expected_size) { + if (request_size != 0) + throw usb_error("Unexpected descriptor size"); + + // repeat with larger size + return get_descriptor(hub_handle, usb_port_num, descriptor_type, index, language_id, expected_size); + } + + return std::vector(descriptor_request->Data, descriptor_request->Data + data_size); +} + +std::string usb_registry::get_string(HANDLE hub_handle, ULONG usb_port_num, int index) { + if (index == 0) + return ""; + + std::vector str_desc_raw = get_descriptor(hub_handle, usb_port_num, USB_STRING_DESCRIPTOR_TYPE, index, 0x0409); + USB_STRING_DESCRIPTOR* str_desc = reinterpret_cast(str_desc_raw.data()); + + // required length of UTF-8 string + int len = WideCharToMultiByte(CP_UTF8, 0, str_desc->bString, str_desc->bLength / 2 - 1, nullptr, 0, nullptr, nullptr); + + // convert to UTF-8 + std::string result; + result.resize(len, 'x'); + WideCharToMultiByte(CP_UTF8, 0, str_desc->bString, str_desc->bLength / 2 - 1, &result[0], len, nullptr, nullptr); + return result; +} diff --git a/reference/windows/USB/usb_registry.hpp b/reference/windows/USB/usb_registry.hpp index 5b2d967f..321bf18c 100644 --- a/reference/windows/USB/usb_registry.hpp +++ b/reference/windows/USB/usb_registry.hpp @@ -4,7 +4,7 @@ // Licensed under MIT License // https://opensource.org/licenses/MIT // -// Reference C++ code for macOS +// Reference C++ code for Windows // #pragma once @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -24,6 +25,8 @@ #undef LowSpeed #include +class device_info_set; + /** * Registry of connected USB devices. */ @@ -49,28 +52,45 @@ class usb_registry { void monitor(); void detect_present_devices(); - + std::shared_ptr create_device_from_device_info(device_info_set& dev_info_set, std::wstring&& device_path, std::map& hub_handles); + std::shared_ptr create_device(std::wstring&& device_path, bool is_composite, HANDLE hub_handle, DWORD usb_port_num); + + static std::string get_string(HANDLE hub_handle, ULONG usb_port_num, int index); + static std::vector get_descriptor(HANDLE hub_handle, ULONG usb_port_num, uint16_t descriptor_type, int index, int language_id, int request_size = 0); + void on_device_connected(const WCHAR* path); void on_device_disconnected(const WCHAR* path); bool handle_message(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); static LRESULT handle_windows_message(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); - std::shared_ptr create_device(HDEVINFO dev_info_set_hdl, SP_DEVINFO_DATA* dev_info); - - static uint32_t get_device_property_int(HDEVINFO dev_info, SP_DEVINFO_DATA* dev_info_data, const DEVPROPKEY* prop_key); - static std::wstring get_device_property_string(HDEVINFO dev_info, SP_DEVINFO_DATA* dev_info_data, const DEVPROPKEY* prop_key); - static std::wstring get_device_path(const std::wstring& instance_id, const GUID* interface_guid); std::vector devices; std::function on_connected_callback; std::function on_disconnected_callback; + std::shared_ptr get_shared_ptr(usb_device* device); + + void async_io_run(); + void add_to_completion_port(HANDLE); + void add_completion_handler(OVERLAPPED* overlapped, usb_io_callback* completion_handler); + void remove_completion_handler(OVERLAPPED* overlapped); + usb_io_callback* get_completion_handler(OVERLAPPED* overlapped); + std::thread monitor_thread; bool is_device_list_ready; std::mutex monitor_mutex; std::condition_variable monitor_condition; - DWORD background_thread_id_; + DWORD monitor_thread_id_; HWND message_window; + + std::thread async_io_thread; + std::mutex async_io_mutex; + HANDLE async_io_completion_port; + std::map async_io_completion_handlers; + + friend class usb_device; + friend class usb_istreambuf; + friend class usb_ostreambuf; }; diff --git a/test-devices/composite-stm32/.vscode/settings.json b/test-devices/composite-stm32/.vscode/settings.json index 7e488cff..87eaf860 100644 --- a/test-devices/composite-stm32/.vscode/settings.json +++ b/test-devices/composite-stm32/.vscode/settings.json @@ -1,7 +1,9 @@ { "files.associations": { - "usbd.h": "c", - "usb_bos.h": "c", - "usb_bos_desc.h": "c" + "tusb_option.h": "c", + "tusb_config.h": "c", + "cdc_device.h": "c", + "usbd_pvt.h": "c", + "usbd.h": "c" } } \ No newline at end of file diff --git a/test-devices/composite-stm32/README.md b/test-devices/composite-stm32/README.md index 6f28875c..bdea2084 100644 --- a/test-devices/composite-stm32/README.md +++ b/test-devices/composite-stm32/README.md @@ -1,26 +1,32 @@ # Composite Test Device -Firmware for a composite device consisting of virtual serial port (aka CDC ACM) on interface 0 and 1 and a vendor-specific class on interface 2. This code is for an STM32F103C8 microcontroller. This microcontroller is found on many inexpensive development board, most notabily on the so called *Blue Pill*. Such boards are available for about 3 USD, including the required ST-Link programmer. +Firmware for a composite device consisting of virtual serial port (aka CDC ACM) on interface 0 and 1 and a vendor-specific class on interface 2. This is the code for STM32 microcontrollers. It is found on many inexpensive development board, most notabily on the so called *Blue Pill* and *Black Pill* boards. They are available for about 3 USD. +## Supported boards -## Endpoint overview +- BlackPill with STM32F401CC microcontroller +- BlackPill with STM32F411CE microcontroller +- BluePill with STM32F103C8 microcontroller + +To upload the firmware, the STM32F4x microcontroller have a built-in USB bootloader. The STM32F1x microcontrollers need an ST-Link debug adapter (or a USB-to-serial converter). + + +## Test features ### Endpoints -| Interface | Endpoint | Transfer Type | Direction | Packet Size | Function | +| Endpoint | Transfer Type | Direction | Packet Size | Interface | Function | | - | - | - | - | - | - | -| – | 0x00 | Control | Bidirectional | | See *Control requests* below | -| 0 | 0x84 | Interrupt | Device to host | 16 bytes | CDC: Serial state events (not used) | -| 1 | 0x05 | Bulk | Host to device | 64 bytes | CDC: Serial data from host to device | -| 1 | 0x85 | Bulk | Device to host | 64 bytes | CDC: Serial data from device to host | -| 2 | 0x01 | Bulk | Host to device | 64 bytes | Loopback: all data received on this endpoint are then transmitted on endpoint 0x82. | -| 2 | 0x82 | Bulk | Device to host | 64 bytes | Loopback: Transmits the data received on endpoint 0x01. | -| 2 | 0x03 | Interrupt | Host to device | 16 bytes | Echo: All packets received on this endpoint are transmitted twice on endpoint 0x83. | -| 2 | 0x83 | Interrupt | Device to host | 16 bytes | Echo: Transmits all packets received on endpoint 0x03 twice. | +| 0x00 | Control | Bidirectional | | 2 | See *Control requests* below | +| 0x81 | Bulk | Device to host | 64 bytes | 1 | CDC: Serial data from device to host. | +| 0x02 | Bulk | Host to device | 64 bytes | 1 | CDC: Serial data from host to device. | +| 0x83 | Interrupt | Device to host | 64 bytes | 0 | CDC: Serial state events (not used). | +| 0x01 | Bulk | Host to device | 64 bytes | 3 | Loopback: all data received on this endpoint is transmitted on endpoint 0x82. | +| 0x82 | Bulk | Device to host | 64 bytes | 3 | Loopback: Transmits the data received on endpoint 0x01. | -The virtual serial port on interfaces 0 and 1 implements the CDC ACM class. All operating systems will recognize it as serial port and will automatically make it available as such. No drivers need to be installed. The implementations connects the incoming and outgoing data in a loopback configuration. So all data sent from the host to the device is send back to the host. Control requests to configure baud rates, parity etc. are accpeted but have no effect. And the implementation does not send any state events. +The virtual serial port on interfaces 0 and 1 implements the CDC ACM class. All operating systems will recognize it as serial port and will automatically make it available as such. No drivers need to be installed. The implementations connects the incoming and outgoing data in a loopback configuration. So all data sent from the host to the device is send back to the host. Control requests to configure baud rates, parity etc. are accepted but have no effect. And the implementation does not send any state events. -The bulk endpoints 0x01 and 0x82 use an internal buffer of about 1000 bytes. Data up to this amount can be sent and received sequentially. If more data is sent without receiving at the same time, flow control kicks in and endpoint 0x01 will stop receiving data until there is room in the buffer. +The bulk endpoints 0x01 and 0x82 use an internal buffer of about 500 bytes. Data up to this amount can be sent and received sequentially. If more data is sent without receiving at the same time, flow control kicks in and endpoint 0x01 will stop receiving data until there is room in the buffer. ### Control requests @@ -28,36 +34,78 @@ The bulk endpoints 0x01 and 0x82 use an internal buffer of about 1000 bytes. Dat Several vendor-specific control requests are supported for testing: | `bmRequest` | `bRequest` | `wValue` | `wIndex` | `wLength` | Data | Action | -| -:| -:| -:| -:| -:| - | - | -| 0x41 | 0x01 | *value* | 2 | 0 | none | Host to device: *value* is saved in device | -| 0x41 | 0x02 | 0 | 2 | 4 | *value* (32-bit LE) | Host to device: *value* is saved in device | -| 0xC1 | 0x03 | 0 | 2 | 4 | *value* (32-bit LE) | Device to host: saved *value* is transmitted | +| - | - | - | - | - | - | - | +| 0x41 | 0x01 | *value* | 0 | 0 | none | Host to device: *value* is saved in device | +| 0x41 | 0x02 | 0 | 0 | 4 | *value* (32-bit LE) | Host to device: *value* is saved in device | +| 0xC1 | 0x03 | 0 | 0 | 4 | *value* (32-bit LE) | Device to host: saved *value* is transmitted | +| 0xC1 | 0x05 | 0 | 0 | 1 | *interface number* | Device to host: interface number is transmitted | -Note: The lower byte of `wIndex` contains the interface number. -Additionally, the control request of CDC ACM PTSN (for configuring and querying the serial port) are accepted but have no effect in most cases. - -## Building +## Building the firmware This project requires [PlatformIO](https://platformio.org/). The easiest way to get up and running is to use Visual Studio Code and then install the [PlatformIO IDE extension](https://marketplace.visualstudio.com/items?itemName=platformio.platformio-ide). -After the extension is installed, open this folder and then click checkbox icon (*Build* action) in the status bar. +After the extension is installed, open this folder and select your board type by clicking on "Default (tinyusb-stm32)" in the status bar. Wait until the status bar no longer indicates activity. Then click the checkbox icon (*Build* action) in the status bar. + +To load the firmware onto the board, either connect it via ST-Link programmer to the 4 pins on the short side of the board or use the built-in USB bootloader (BlackPill only). -To upload the code to the microcontroller: +For upload with a ST-Link programmer: - Connect the ST-Link programmer to the development board - Connect the ST-Link programmer to your computer - Click the arrow icon (*Upload* action) in the status bar +For other means of upload, see *Binary releases* below. + + +## Binary releases + +The directory `bin` contains a pre-built firmware: + +- `blackpill-f401cc.bin`: Firmware for BlackPill with STM32F401CC microcontroller +- `blackpill-f411ce.bin`: Firmware for BlackPill with STM32F411CE microcontroller +- `bluepill-f103c8.bin`: Firmware for BluePill with STM32F103C8 microcontroller + +### Upload using built-in bootloader + +To upload using the BlackPill's built-in bootloader: -## Binary release +1. Install the *dfu-util* command-line utility (typically using a package manager like *HomeBrew* on macOS, *Chocolatey* on Windows, or *Apt* on Linux). +2. Press the *Boot* button while connecting the board via USB to your computer. By pressing the *Boot* button, the device enters bootloader mode. +3. Verify with `dfu-util --list` that the bootloader is available via USB. If not unplug the device and repeat step 2. +4. Run the below command from the project directory. +5. Unplug and reconnect the board from your computer. Both the power and user LED should be lit and the device should appear as a serial device (aka as COM port on Windows). -The directory `bin` contains a pre-built firmware. In order to upload it, a utility is needed, either [STM32CubeProgrammer](https://www.st.com/en/development-tools/stm32cubeprog.html) (requires an STM account, does not properly work on macOS) or the [open-source ST-Link command line utility](https://github.com/stlink-org/stlink). See the respective web site for installation instructions. +``` +dfu-util --device 0483:df11 --alt 0 --dfuse-address 0x08000000 --reset --download bin/blackpill-fxxx.bin +``` + +Make sure you change the filename `blackpill-fxxx.bin` to the name matching your board. + +If you built the firmware yourself, you will find the firmware file in `.pio/build/blackpill-f401cc/firmware.bin` (and similar for other boards). + +### Upload using ST-Link programmer + +In order to upload it using the ST-Link programmer: -If the commmand line utility is used, run these commands to upload it: +1. Install the *stlink* command-line utility (typically using a package manager like *HomeBrew* on macOS, *Chocolatey* on Windows, or *Apt* on Linux). +2. Unplug the microcontroller board from your computer (in case it is connected). +3. Connect the ST-Link via jumper wires to your board (4 pins on the short side of the board). +4. Connect the ST-Link via USB cable to your computer. +5. Run the below command from the project directory. +6. Unplug the microcontroller board from the ST-Link and connect it to your computer (via USB). ``` -cd stm32-loopback/bin -st-flash write firmware.bin 0x08000000 +st-flash write bin/bluepill-f103c8.bin 0x08000000 ``` + +Make sure you change the filename `bluepill-f103c8.bin` to the name matching your board. + +If you built the firmware yourself, you will find the firmware file in `.pio/build/bluepill-f103c8/firmware.bin` (and similar for other boards). + +## Implementation + +This code uses the CMSIS 5 library (mainly for startup code and register definitions) and TinyUSB for USB. For easier use with PlatformIO, a copy of TinyUSB is integrated into the project. The used TinyUSB code in `lib/tinyusb` is an unmodified subset of the library. + +Since the official TinyUSB vendor class is rather limited, an alternative implementation is provided (see [vendor_custom.h](src/vendor_custom.h) and [vendor_custom.c](src/vendor_custom.c)). \ No newline at end of file diff --git a/test-devices/composite-stm32/bin/blackpill-f401cc.bin b/test-devices/composite-stm32/bin/blackpill-f401cc.bin new file mode 100755 index 00000000..bba469dc Binary files /dev/null and b/test-devices/composite-stm32/bin/blackpill-f401cc.bin differ diff --git a/test-devices/composite-stm32/bin/blackpill-f411ce.bin b/test-devices/composite-stm32/bin/blackpill-f411ce.bin new file mode 100755 index 00000000..5662aad9 Binary files /dev/null and b/test-devices/composite-stm32/bin/blackpill-f411ce.bin differ diff --git a/test-devices/composite-stm32/bin/bluepill-f103c8.bin b/test-devices/composite-stm32/bin/bluepill-f103c8.bin new file mode 100755 index 00000000..05dc11a3 Binary files /dev/null and b/test-devices/composite-stm32/bin/bluepill-f103c8.bin differ diff --git a/test-devices/composite-stm32/bin/firmware.bin b/test-devices/composite-stm32/bin/firmware.bin deleted file mode 100755 index 4ae97923..00000000 Binary files a/test-devices/composite-stm32/bin/firmware.bin and /dev/null differ diff --git a/test-devices/composite-stm32/bin/save_bin.sh b/test-devices/composite-stm32/bin/save_bin.sh deleted file mode 100755 index ce4662a7..00000000 --- a/test-devices/composite-stm32/bin/save_bin.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -cp ../.pio/build/composite-stm32/firmware.bin . diff --git a/test-devices/composite-stm32/bin/upload_firmware.sh b/test-devices/composite-stm32/bin/upload_firmware.sh deleted file mode 100755 index 20ad778e..00000000 --- a/test-devices/composite-stm32/bin/upload_firmware.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -st-flash write firmware.bin 0x08000000 diff --git a/test-devices/composite-stm32/copy_tinyusb.sh b/test-devices/composite-stm32/copy_tinyusb.sh new file mode 100755 index 00000000..1b1a2f3b --- /dev/null +++ b/test-devices/composite-stm32/copy_tinyusb.sh @@ -0,0 +1,17 @@ +#!/bin/sh +TINYUSB_DIR=../../../tinyusb +rm -rf lib/tinyusb/* +mkdir lib/tinyusb/osal +mkdir lib/tinyusb/class +mkdir lib/tinyusb/portable +mkdir lib/tinyusb/portable/synopsys +mkdir lib/tinyusb/portable/st +cp -R $TINYUSB_DIR/src/class/cdc lib/tinyusb/class +cp -R $TINYUSB_DIR/src/common lib/tinyusb +cp -R $TINYUSB_DIR/src/device lib/tinyusb +cp $TINYUSB_DIR/src/osal/osal.h lib/tinyusb/osal +cp $TINYUSB_DIR/src/osal/osal_none.h lib/tinyusb/osal +cp -R $TINYUSB_DIR/src/portable/synopsys/dwc2 lib/tinyusb/portable/synopsys +cp -R $TINYUSB_DIR/src/portable/st/stm32_fsdev lib/tinyusb/portable/st +cp $TINYUSB_DIR/src/*.c lib/tinyusb +cp $TINYUSB_DIR/src/*.h lib/tinyusb diff --git a/test-devices/composite-stm32/include/circ_buf.h b/test-devices/composite-stm32/include/circ_buf.h deleted file mode 100644 index 4d9a6970..00000000 --- a/test-devices/composite-stm32/include/circ_buf.h +++ /dev/null @@ -1,155 +0,0 @@ -// -// Java Does USB -// Loopback device for testing -// -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// -// Circular buffer for raw binary data. -// -// The circular buffer allows a reader and writer to -// use the buffer concurrently. -// - -#pragma once - -#include -#include - -#include - -/** - * Circular buffer for raw binary data. - * - * The circular buffer allows a reader and writer to - * use the buffer concurrently. - * - * @param N number of bytes that fit into the buffer - */ -template -struct circ_buf { - private: - static constexpr int BUF_SIZE = N + 1; - - // 0 <= head < BUF_SIZE - // 0 <= tail < BUF_SIZE - // head == tail: buffer is empty - // Therefore, the buffer must never be filled completely. - volatile int buf_head = 0; // updated when adding data - volatile int buf_tail = 0; // updated when removing data - - uint8_t buffer[BUF_SIZE]; - - public: - /// Creates a new instance - circ_buf(); - - /// Returns the maximum number of bytes that can be added to the buffer - int avail_size(); - - /// Returns the number of bytes in the buffer - int data_size(); - - /** - * Gets the oldest data from the buffer and removes it. - * @param buf buffer to copy data to - * @param max_len maximum number of bytes to copy - * @return the effective number of bytes - */ - int get_data(uint8_t *buf, int max_len); - - /** - * Adds data to the buffer - * - * @param buf the buffer with the data - * @param len the number of bytes to add - */ - void add_data(const uint8_t *buf, int len); - - /// Resets (empties) the circular buffer - void reset(); -}; - -template -circ_buf::circ_buf() : buf_head(0), buf_tail(0) {} - -template -int circ_buf::avail_size() { - int head = buf_head; - int tail = buf_tail; - - if (head >= tail) { - return BUF_SIZE - (head - tail) - 1; - } else { - return tail - head - 1; - } -} - -template -int circ_buf::data_size() { - int head = buf_head; - int tail = buf_tail; - - if (head >= tail) { - return head - tail; - } else { - return BUF_SIZE - (tail - head); - } -} - -template -int circ_buf::get_data(uint8_t *buf, int max_len) { - int tail = buf_tail; - int head = buf_head; - - if (tail == head) - return 0; - - // get available data (without wrap around) - int len = (head > tail ? head : BUF_SIZE) - tail; - - // limit data to max_len - len = std::min(len, max_len); - - // copy data - memcpy(buf, buffer + tail, len); - - // update tail - tail += len; - if (tail >= BUF_SIZE) - tail -= BUF_SIZE; - buf_tail = tail; - - // sufficient data or no more data - if (len == max_len || tail != 0) - return len; - - // copy more data - return get_data(buf + len, max_len - len) + len; -} - -template -void circ_buf::add_data(const uint8_t *buf, int len) { - int head = buf_head; - - // copy first part (from head to end of circular buffer) - int n = std::min(len, BUF_SIZE - head); - memcpy(buffer + head, buf, n); - - // copy second part if needed (to start of circular buffer) - if (n < len) - memcpy(buffer, buf + n, len - n); - - // update head - head += len; - if (head >= BUF_SIZE) - head -= BUF_SIZE; - buf_head = head; -} - -template -void circ_buf::reset() { - buf_head = 0; - buf_tail = 0; -} diff --git a/test-devices/composite-stm32/include/common.h b/test-devices/composite-stm32/include/common.h deleted file mode 100644 index 427b2eb4..00000000 --- a/test-devices/composite-stm32/include/common.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// Java Does USB -// Loopback device for testing -// -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// -// Commmon functions -// - -#pragma once - -#include - -/** - * @brief Initializes systick services - */ -void systick_init(); - -/** - * @brief Gets the time. - * - * @return number of milliseconds since a fixed time in the past - */ -uint32_t millis(); - -/** - * @brief Delays execution (busy wait) - * @param ms delay length, in milliseconds - */ -void delay(uint32_t ms); diff --git a/test-devices/composite-stm32/include/usb_bos.h b/test-devices/composite-stm32/include/usb_bos.h deleted file mode 100644 index f73ebd65..00000000 --- a/test-devices/composite-stm32/include/usb_bos.h +++ /dev/null @@ -1,263 +0,0 @@ -// -// Java Does USB -// Loopback device for testing -// -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// -// USB binary device object store (BOS) -// - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/// Descriptor type Binary Device Object Store (BOS) - -static const uint8_t USB_DT_BOS = 15; -/// Descriptor type Device Capability -#define USB_DT_DEVICE_CAPABILITY 16 - -/// Microsoft WCID string index -static const uint8_t USB_WIN_MSFT_WCID_STR_IDX = 0xee; - -/// Microsoft compatible ID feature descriptor request index (wIndex) -static const uint16_t USB_WIN_COMP_ID_REQ_INDEX = 0x0004; - -#define USB_WIN_WCID_DEFAULT_VENDOR_CODE 0xf0 - - - -/// USB BOS device capability types -typedef enum { - /// USB BOS device capability type for Wireless USB-specific device level capabilities - USB_DEV_CAPA_WIRELESS_USB = 0x01, - /// USB BOS device capability type for USB 2.0 extension descriptor - USB_DEV_CAPA_USB_2_0_EXTENSION = 0x02, - /// USB BOS device capability type for SuperSpeed USB specific device level capabilities - USB_DEV_CAPA_SUPERSPEED_USB = 0x03, - /// USB BOS device capability type for instance unique ID used to identify the instance across all operating modes - USB_DEV_CAPA_CONTAINER_ID = 0x04, - /// USB BOS device capability type for device capability specific to a particular platform/operating system - USB_DEV_CAPA_PLATFORM = 0x05, - /// USB BOS device capability type for various PD capabilities of this device - USB_DEV_CAPA_POWER_DELIVERY_CAPABILITY = 0x06, - /// USB BOS device capability type for information on each battery supported by the device - USB_DEV_CAPA_BATTERY_INFO_CAPABILITY = 0x07, - /// USB BOS device capability type for consumer characteristics of a port on the device - USB_DEV_CAPA_PD_CONSUMER_PORT_CAPABILITY = 0x08, - /// USB BOS device capability type for provider characteristics of a port on the device - USB_DEV_CAPA_PD_PROVIDER_PORT_CAPABILITY = 0x09, - /// USB BOS device capability type for SuperSpeed Plus USB specific device level capabilities - USB_DEV_CAPA_SUPERSPEED_PLUS = 0x0a, - /// USB BOS device capability type for precision time measurement (PTM) capability descriptor - USB_DEV_CAPA_PRECISION_TIME_MEASUREMENT = 0x0b, - /// USB BOS device capability type for wireless USB 1.1-specific device level capabilities - USB_DEV_CAPA_WIRELESS_USB_EXT = 0x0c, - /// USB BOS device capability type for billboard capability - USB_DEV_CAPA_BILLBOARD = 0x0d, - /// USB BOS device capability type for authentication capability descriptor - USB_DEV_CAPA_AUTHENTICATION = 0x0e, - /// USB BOS device capability type billboard ex capability - USB_DEV_CAPA_BILLBOARD_EX = 0x0f, - /// USB BOS device capability type for summarizing configuration information for a function implemented by the device - USB_DEV_CAPA_CONFIGURATION_SUMMARY = 0x10, -} usb_dev_capa_type_e; - -/// USB BOS descriptor -typedef struct usb_bos_desc { - /// Size of this descriptor - uint8_t bLength; - /// Type of this descriptor (use USB_DT_BOS) - uint8_t bDescriptorType; - /// Length of this descriptor and all of its sub descriptors - uint16_t wTotalLength; - /// The number of separate device capability descriptors in the BOS - uint8_t bNumDeviceCaps; -} __attribute__((packed)) usb_bos_desc; - -/// USB BOS device capability descriptor (generic) -typedef struct usb_bos_device_capability_desc { - /// Size of this descriptor - uint8_t bLength; - /// Type of this descriptor (use USB_DT_DEVICE_CAPABILITY) - uint8_t bDescriptorType; - /// Device capability type (see usb_dev_capa_type_e) - uint8_t bDevCapabilityType; - /// Capability-specific data - uint8_t data[]; -} __attribute__((packed)) usb_bos_device_capability_desc; - -/// USB BOS device capability platform descriptor -typedef struct usb_bos_platform_desc { - /// Size of this descriptor - uint8_t bLength; - /// Type of this descriptor (use USB_DT_DEVICE_CAPABILITY) - uint8_t bDescriptorType; - /// Device capability type (use USB_DEV_CAPA_PLATFORM) - uint8_t bDevCapabilityType; - /// Reserved. Set to 0. - uint8_t bReserved; - /// A 128-bit number (UUID) that uniquely identifies a platform specific capability of the device - uint8_t platformCapabilityUUID[16]; - /// Platform-specific capability data - uint8_t capabilityData[]; -} __attribute__((packed)) usb_bos_platform_desc; - - -/// Microsoft OS 2.0 request `wIndex` value to retrieve MS OS 2.0 vendor-specific descriptor -static const uint8_t USB_MSOS20_CTRL_INDEX_DESC = 0x07; -/// Microsoft OS 2.0 request `wIndex` value to set alternate enumeration -static const uint8_t USB_MSOS20_CTRL_INDEX_SET_ALT_ENUM = 0x08; - -/// UUID for Microsoft OS 2.0 platform capability: {d8dd60df-4589-4cc7-9cd2-659d9e648a9f} -#define USB_PLATFORM_CAPABILITY_MICROSOFT_OS20_UUID {0xDF, 0x60, 0xDD, 0xD8, 0x89, 0x45, 0xC7, 0x4C, 0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F} - -/// Microsoft OS 2.0 descriptor types -typedef enum { - /// Microsoft OS 2.0 descriptor type for set header - USB_MSOS20_DT_SET_HEADER_DESCRIPTOR = 0x00, - /// Microsoft OS 2.0 descriptor type for configuration subset header - USB_MSOS20_DT_SUBSET_HEADER_CONFIGURATION = 0x01, - /// Microsoft OS 2.0 descriptor type for function subset header - USB_MSOS20_DT_SUBSET_HEADER_FUNCTION = 0x02, - /// Microsoft OS 2.0 feature descriptor type for compatible ID descriptor - USB_MSOS20_DT_FEATURE_COMPATBLE_ID = 0x03, - /// Microsoft OS 2.0 feature descriptor type for registry propery descriptor - USB_MSOS20_DT_FEATURE_REG_PROPERTY = 0x04, - /// Microsoft OS 2.0 feature descriptor type for minimum USB resume time descriptor - USB_MSOS20_DT_FEATURE_MIN_RESUME_TIME = 0x05, - /// Microsoft OS 2.0 feature descriptor type for model ID descriptor - USB_MSOS20_DT_FEATURE_MODEL_ID = 0x06, - /// Microsoft OS 2.0 feature descriptor type for CCGP device descriptor - USB_MSOS20_DT_FEATURE_CCGP_DEVICE = 0x07, - /// Microsoft OS 2.0 feature descriptor type for vendor revision descriptor - USB_MSOS20_DT_FEATURE_VENDOR_REVISION = 0x08, -} usb_msos20_desc_type_e; - -/// Microsoft OS 2.0 property types -typedef enum { - /// A NULL-terminated Unicode String (REG_SZ) - USB_MSOS20_PROP_DATA_TYPE_STRING = 1, - /// A NULL-terminated Unicode String that includes environment variables (REG_EXPAND_SZ) - USB_MSOS20_PROP_DATA_TYPE_STRING_EXPAND = 2, - /// Free-form binary (REG_BINARY) - USB_MSOS20_PROP_DATA_TYPE_BINARY = 3, - /// A little-endian 32-bit integer (REG_DWORD_LITTLE_ENDIAN) - USB_MSOS20_PROP_DATA_TYPE_INT32LE = 4, - /// A big-endian 32-bit integer (REG_DWORD_BIG_ENDIAN) - USB_MSOS20_PROP_DATA_TYPE_INT32BE = 5, - /// A NULL-terminated Unicode string that contains a symbolic link (REG_LINK) - USB_MSOS20_PROP_DATA_TYPE_STRING_LINK = 6, - /// Multiple NULL-terminated Unicode strings (REG_MULTI_SZ) - USB_MSOS20_PROP_DATA_TYPE_STRING_MULTI = 7, -} usb_msos20_prop_data_type_e; - -/// Microsoft OS 2.0 descriptor Windows version -typedef enum { - /// Windows version 8.1 - USB_MSOS20_WIN_VER_8_1 = 0x06030000, - /// Windows version 10 - USB_MSOS20_WIN_VER_10 = 0x0a000000, -} usb_msos20_win_ver_e; - -/// USB BOS device capability platform descriptor for Microsoft OS 2.0 -typedef struct usb_msos20_platform_desc { - /// Size of this descriptor - uint8_t bLength; - /// Type of this descriptor (use USB_DT_DEVICE_CAPABILITY) - uint8_t bDescriptorType; - /// Device capability type (use USB_DEV_CAPA_PLATFORM) - uint8_t bDevCapabilityType; - /// Reserved. Set to 0. - uint8_t bReserved; - /// A 128-bit number / UUID (use USB_PLATFORM_CAPABILITY_MICROSOFT_OS20_UUID) - uint8_t platformCapabilityUUID[16]; - /// Minimum Windows version (see usb_msos20_win_ver_e) - uint32_t dwWindowsVersion; - /// The length, in bytes, of the MS OS 2.0 descriptor set - uint16_t wMSOSDescriptorSetTotalLength; - /// Vendor defined code to use to retrieve this version of the MS OS 2.0 descriptor and also to set alternate enumeration behavior on the device - uint8_t bMS_VendorCode; - /// A non-zero value to send to the device to indicate that the device may return non-default USB descriptors for enumeration. If the device does not support alternate enumeration, this value shall be 0. - uint8_t bAltEnumCode; -} __attribute__((packed)) usb_msos20_platform_desc; - -/// Microsoft OS 2.0 descriptor set header -typedef struct usb_msos20_desc_set_header -{ - /// The length, in bytes, of this header - uint16_t wLength; - /// The type of this descriptor (use USB_MSOS20_DT_SET_HEADER_DESCRIPTOR) - uint16_t wDescriptorType; - /// Windows version (see usb_msos20_win_ver_e) - uint32_t dwWindowsVersion; - /// The size of entire MS OS 2.0 descriptor set. The value shall match the value in the descriptor set information structure. - uint16_t wTotalLength; -} __attribute__((packed)) usb_msos20_desc_set_header; - -/// Microsoft OS 2.0 descriptor configuration subset header -typedef struct usb_msos20_desc_subset_header_config -{ - /// The length, in bytes, of this header - uint16_t wLength; - /// The type of this descriptor (use USB_MSOS20_DT_SUBSET_HEADER_CONFIGURATION) - uint16_t wDescriptorType; - /// The configuration value for the USB configuration to which this subset applies - uint8_t bConfigurationValue; - /// Reserved. Set to 0. - uint8_t bReserved; - /// The size of entire configuration subset including this header. - uint16_t wTotalLength; -} __attribute__((packed)) usb_msos20_desc_subset_header_config; - -/// Microsoft OS 2.0 descriptor function subset header -typedef struct usb_msos20_desc_subset_header_function -{ - /// The length, in bytes, of this header - uint16_t wLength; - /// The type of this descriptor (use USB_MSOS20_DT_SUBSET_HEADER_FUNCTION) - uint16_t wDescriptorType; - /// The interface number for the first interface of the function to which this subset applies. - uint8_t bFirstInterface; - /// Reserved. Set to 0. - uint8_t bReserved; - /// The size of entire function subset including this header. - uint16_t wTotalLength; -} __attribute__((packed)) usb_msos20_desc_subset_header_function; - -/// Microsoft OS 2.0 compatible ID descriptor -typedef struct usb_msos20_desc_compatible_id -{ - /// The length, in bytes, of this header - uint16_t wLength; - /// The type of this descriptor (use USB_MSOS20_DT_FEATURE_COMPATBLE_ID) - uint16_t wDescriptorType; - /// Compatible ID string - char compatibleID[8]; - /// Sub-compatible ID string - char subCompatibleID[8]; -} __attribute__((packed)) usb_msos20_desc_compatible_id; - -extern const usb_msos20_platform_desc msos_desc; - -/** - * Register the control request handler to respond to BOS request (for automatic WinUSB installation) - * - * @param device USB device - * @param bos_descs array of BOS descriptors - * @param num_bos_descs number of BOS descriptors - * @param msos_desc_set MS OS 2.0 descriptor set - */ -void usb_dev_register_bos(usbd_device* device, - const usb_bos_device_capability_desc* const * bos_descs, int num_bos_descs, - const usb_msos20_desc_set_header* msos_desc_set, uint8_t msos_vendor_code); - -#ifdef __cplusplus -} -#endif diff --git a/test-devices/composite-stm32/include/usb_bos_desc.h b/test-devices/composite-stm32/include/usb_bos_desc.h deleted file mode 100644 index eacfbf57..00000000 --- a/test-devices/composite-stm32/include/usb_bos_desc.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// Java Does USB -// Loopback device for testing -// -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// -// USB BOS descriptor -// - -#pragma once - -#include "usb_bos.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define MSOS_VENDOR_CODE 0x44 - -extern const usb_bos_device_capability_desc* const bos_descs[1]; -extern const usb_msos20_desc_set_header* msos_desc_set; - -#ifdef __cplusplus -} -#endif diff --git a/test-devices/composite-stm32/include/usb_descriptor.h b/test-devices/composite-stm32/include/usb_descriptor.h deleted file mode 100644 index 0d01413b..00000000 --- a/test-devices/composite-stm32/include/usb_descriptor.h +++ /dev/null @@ -1,35 +0,0 @@ -// -// Java Does USB -// Loopback device for testing -// -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// -// USB descriptor -// - -#pragma once - -#include - -#define INTR_MAX_PACKET_SIZE 16 -#define BULK_MAX_PACKET_SIZE 64 - -// Endpoints -#define EP_LOOPBACK_RX 0x01 -#define EP_LOOPBACK_TX 0x82 -#define EP_ECHO_RX 0x03 -#define EP_ECHO_TX 0x83 -#define EP_CDC_COMM 0x84 -#define EP_CDC_DATA_RX 0x05 -#define EP_CDC_DATA_TX 0x85 - -// USB descriptor string table -extern const char *const usb_desc_strings[4]; -// USB device descriptor -extern const struct usb_device_descriptor usb_device_desc; -// USB device configurations -extern const struct usb_config_descriptor usb_config_descs[]; - -void usb_init_serial_num(); diff --git a/test-devices/composite-stm32/lib/config/tusb_config.h b/test-devices/composite-stm32/lib/config/tusb_config.h new file mode 100644 index 00000000..130bdf8a --- /dev/null +++ b/test-devices/composite-stm32/lib/config/tusb_config.h @@ -0,0 +1,131 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Board Specific Configuration +//--------------------------------------------------------------------+ + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_TUD_RHPORT +#define BOARD_TUD_RHPORT 0 +#endif + +// RHPort max operational speed can defined by board.mk +#ifndef BOARD_TUD_MAX_SPEED +#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU +#error CFG_TUSB_MCU must be defined +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +#ifndef CFG_TUSB_DEBUG +#define CFG_TUSB_DEBUG 0 +#endif + +// Enable Device stack +#define CFG_TUD_ENABLED 1 + +// Default is max speed that hardware controller could support with on-chip PHY +#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE +#define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +//------------- CLASS -------------// +#ifndef CFG_TUD_CDC +#define CFG_TUD_CDC 0 +#endif +#ifndef CFG_TUD_MSC +#define CFG_TUD_MSC 0 +#endif +#ifndef CFG_TUD_HID +#define CFG_TUD_HID 0 +#endif +#ifndef CFG_TUD_MIDI +#define CFG_TUD_MIDI 0 +#endif +#ifndef CFG_TUD_VENDOR +#define CFG_TUD_VENDOR 0 +#endif + +// HID buffer size Should be sufficient to hold ID (if any) + Data +#define CFG_TUD_HID_EP_BUFSIZE 64 + +// Vendor FIFO size of TX and RX +// If not configured vendor endpoints will not be buffered +#define CFG_TUD_VENDOR_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_VENDOR_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + +#ifndef CFG_TUD_CDC_RX_BUFSIZE +#define CFG_TUD_CDC_RX_BUFSIZE 64 +#endif + +#ifndef CFG_TUD_CDC_TX_BUFSIZE +#define CFG_TUD_CDC_TX_BUFSIZE 64 +#endif + + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc.h b/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc.h new file mode 100644 index 00000000..5cbd658f --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc.h @@ -0,0 +1,424 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +/** \ingroup group_class + * \defgroup ClassDriver_CDC Communication Device Class (CDC) + * Currently only Abstract Control Model subclass is supported + * @{ */ + +#ifndef _TUSB_CDC_H__ +#define _TUSB_CDC_H__ + +#include "common/tusb_common.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/** \defgroup ClassDriver_CDC_Common Common Definitions + * @{ */ + +//--------------------------------------------------------------------+ +// CDC Communication Interface Class +//--------------------------------------------------------------------+ + +/// Communication Interface Subclass Codes +typedef enum +{ + CDC_COMM_SUBCLASS_DIRECT_LINE_CONTROL_MODEL = 0x01 , ///< Direct Line Control Model [USBPSTN1.2] + CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL = 0x02 , ///< Abstract Control Model [USBPSTN1.2] + CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL = 0x03 , ///< Telephone Control Model [USBPSTN1.2] + CDC_COMM_SUBCLASS_MULTICHANNEL_CONTROL_MODEL = 0x04 , ///< Multi-Channel Control Model [USBISDN1.2] + CDC_COMM_SUBCLASS_CAPI_CONTROL_MODEL = 0x05 , ///< CAPI Control Model [USBISDN1.2] + CDC_COMM_SUBCLASS_ETHERNET_CONTROL_MODEL = 0x06 , ///< Ethernet Networking Control Model [USBECM1.2] + CDC_COMM_SUBCLASS_ATM_NETWORKING_CONTROL_MODEL = 0x07 , ///< ATM Networking Control Model [USBATM1.2] + CDC_COMM_SUBCLASS_WIRELESS_HANDSET_CONTROL_MODEL = 0x08 , ///< Wireless Handset Control Model [USBWMC1.1] + CDC_COMM_SUBCLASS_DEVICE_MANAGEMENT = 0x09 , ///< Device Management [USBWMC1.1] + CDC_COMM_SUBCLASS_MOBILE_DIRECT_LINE_MODEL = 0x0A , ///< Mobile Direct Line Model [USBWMC1.1] + CDC_COMM_SUBCLASS_OBEX = 0x0B , ///< OBEX [USBWMC1.1] + CDC_COMM_SUBCLASS_ETHERNET_EMULATION_MODEL = 0x0C , ///< Ethernet Emulation Model [USBEEM1.0] + CDC_COMM_SUBCLASS_NETWORK_CONTROL_MODEL = 0x0D ///< Network Control Model [USBNCM1.0] +} cdc_comm_sublcass_type_t; + +/// Communication Interface Protocol Codes +typedef enum +{ + CDC_COMM_PROTOCOL_NONE = 0x00 , ///< No specific protocol + CDC_COMM_PROTOCOL_ATCOMMAND = 0x01 , ///< AT Commands: V.250 etc + CDC_COMM_PROTOCOL_ATCOMMAND_PCCA_101 = 0x02 , ///< AT Commands defined by PCCA-101 + CDC_COMM_PROTOCOL_ATCOMMAND_PCCA_101_AND_ANNEXO = 0x03 , ///< AT Commands defined by PCCA-101 & Annex O + CDC_COMM_PROTOCOL_ATCOMMAND_GSM_707 = 0x04 , ///< AT Commands defined by GSM 07.07 + CDC_COMM_PROTOCOL_ATCOMMAND_3GPP_27007 = 0x05 , ///< AT Commands defined by 3GPP 27.007 + CDC_COMM_PROTOCOL_ATCOMMAND_CDMA = 0x06 , ///< AT Commands defined by TIA for CDMA + CDC_COMM_PROTOCOL_ETHERNET_EMULATION_MODEL = 0x07 ///< Ethernet Emulation Model +} cdc_comm_protocol_type_t; + +//------------- SubType Descriptor in COMM Functional Descriptor -------------// +/// Communication Interface SubType Descriptor +typedef enum +{ + CDC_FUNC_DESC_HEADER = 0x00 , ///< Header Functional Descriptor, which marks the beginning of the concatenated set of functional descriptors for the interface. + CDC_FUNC_DESC_CALL_MANAGEMENT = 0x01 , ///< Call Management Functional Descriptor. + CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT = 0x02 , ///< Abstract Control Management Functional Descriptor. + CDC_FUNC_DESC_DIRECT_LINE_MANAGEMENT = 0x03 , ///< Direct Line Management Functional Descriptor. + CDC_FUNC_DESC_TELEPHONE_RINGER = 0x04 , ///< Telephone Ringer Functional Descriptor. + CDC_FUNC_DESC_TELEPHONE_CALL_AND_LINE_STATE_REPORTING_CAPACITY = 0x05 , ///< Telephone Call and Line State Reporting Capabilities Functional Descriptor. + CDC_FUNC_DESC_UNION = 0x06 , ///< Union Functional Descriptor + CDC_FUNC_DESC_COUNTRY_SELECTION = 0x07 , ///< Country Selection Functional Descriptor + CDC_FUNC_DESC_TELEPHONE_OPERATIONAL_MODES = 0x08 , ///< Telephone Operational ModesFunctional Descriptor + CDC_FUNC_DESC_USB_TERMINAL = 0x09 , ///< USB Terminal Functional Descriptor + CDC_FUNC_DESC_NETWORK_CHANNEL_TERMINAL = 0x0A , ///< Network Channel Terminal Descriptor + CDC_FUNC_DESC_PROTOCOL_UNIT = 0x0B , ///< Protocol Unit Functional Descriptor + CDC_FUNC_DESC_EXTENSION_UNIT = 0x0C , ///< Extension Unit Functional Descriptor + CDC_FUNC_DESC_MULTICHANEL_MANAGEMENT = 0x0D , ///< Multi-Channel Management Functional Descriptor + CDC_FUNC_DESC_CAPI_CONTROL_MANAGEMENT = 0x0E , ///< CAPI Control Management Functional Descriptor + CDC_FUNC_DESC_ETHERNET_NETWORKING = 0x0F , ///< Ethernet Networking Functional Descriptor + CDC_FUNC_DESC_ATM_NETWORKING = 0x10 , ///< ATM Networking Functional Descriptor + CDC_FUNC_DESC_WIRELESS_HANDSET_CONTROL_MODEL = 0x11 , ///< Wireless Handset Control Model Functional Descriptor + CDC_FUNC_DESC_MOBILE_DIRECT_LINE_MODEL = 0x12 , ///< Mobile Direct Line Model Functional Descriptor + CDC_FUNC_DESC_MOBILE_DIRECT_LINE_MODEL_DETAIL = 0x13 , ///< MDLM Detail Functional Descriptor + CDC_FUNC_DESC_DEVICE_MANAGEMENT_MODEL = 0x14 , ///< Device Management Model Functional Descriptor + CDC_FUNC_DESC_OBEX = 0x15 , ///< OBEX Functional Descriptor + CDC_FUNC_DESC_COMMAND_SET = 0x16 , ///< Command Set Functional Descriptor + CDC_FUNC_DESC_COMMAND_SET_DETAIL = 0x17 , ///< Command Set Detail Functional Descriptor + CDC_FUNC_DESC_TELEPHONE_CONTROL_MODEL = 0x18 , ///< Telephone Control Model Functional Descriptor + CDC_FUNC_DESC_OBEX_SERVICE_IDENTIFIER = 0x19 , ///< OBEX Service Identifier Functional Descriptor + CDC_FUNC_DESC_NCM = 0x1A , ///< NCM Functional Descriptor +}cdc_func_desc_type_t; + +//--------------------------------------------------------------------+ +// CDC Data Interface Class +//--------------------------------------------------------------------+ + +// SUBCLASS code of Data Interface is not used and should/must be zero + +// Data Interface Protocol Codes +typedef enum{ + CDC_DATA_PROTOCOL_ISDN_BRI = 0x30, ///< Physical interface protocol for ISDN BRI + CDC_DATA_PROTOCOL_HDLC = 0x31, ///< HDLC + CDC_DATA_PROTOCOL_TRANSPARENT = 0x32, ///< Transparent + CDC_DATA_PROTOCOL_Q921_MANAGEMENT = 0x50, ///< Management protocol for Q.921 data link protocol + CDC_DATA_PROTOCOL_Q921_DATA_LINK = 0x51, ///< Data link protocol for Q.931 + CDC_DATA_PROTOCOL_Q921_TEI_MULTIPLEXOR = 0x52, ///< TEI-multiplexor for Q.921 data link protocol + CDC_DATA_PROTOCOL_V42BIS_DATA_COMPRESSION = 0x90, ///< Data compression procedures + CDC_DATA_PROTOCOL_EURO_ISDN = 0x91, ///< Euro-ISDN protocol control + CDC_DATA_PROTOCOL_V24_RATE_ADAPTION_TO_ISDN = 0x92, ///< V.24 rate adaptation to ISDN + CDC_DATA_PROTOCOL_CAPI_COMMAND = 0x93, ///< CAPI Commands + CDC_DATA_PROTOCOL_HOST_BASED_DRIVER = 0xFD, ///< Host based driver. Note: This protocol code should only be used in messages between host and device to identify the host driver portion of a protocol stack. + CDC_DATA_PROTOCOL_IN_PROTOCOL_UNIT_FUNCTIONAL_DESCRIPTOR = 0xFE ///< The protocol(s) are described using a ProtocolUnit Functional Descriptors on Communications Class Interface +}cdc_data_protocol_type_t; + +//--------------------------------------------------------------------+ +// Management Element Request (Control Endpoint) +//--------------------------------------------------------------------+ + +/// Communication Interface Management Element Request Codes +typedef enum { + CDC_REQUEST_SEND_ENCAPSULATED_COMMAND = 0x00, ///< is used to issue a command in the format of the supported control protocol of the Communications Class interface + CDC_REQUEST_GET_ENCAPSULATED_RESPONSE = 0x01, ///< is used to request a response in the format of the supported control protocol of the Communications Class interface. + CDC_REQUEST_SET_COMM_FEATURE = 0x02, + CDC_REQUEST_GET_COMM_FEATURE = 0x03, + CDC_REQUEST_CLEAR_COMM_FEATURE = 0x04, + + CDC_REQUEST_SET_AUX_LINE_STATE = 0x10, + CDC_REQUEST_SET_HOOK_STATE = 0x11, + CDC_REQUEST_PULSE_SETUP = 0x12, + CDC_REQUEST_SEND_PULSE = 0x13, + CDC_REQUEST_SET_PULSE_TIME = 0x14, + CDC_REQUEST_RING_AUX_JACK = 0x15, + + CDC_REQUEST_SET_LINE_CODING = 0x20, + CDC_REQUEST_GET_LINE_CODING = 0x21, + CDC_REQUEST_SET_CONTROL_LINE_STATE = 0x22, + CDC_REQUEST_SEND_BREAK = 0x23, + + CDC_REQUEST_SET_RINGER_PARMS = 0x30, + CDC_REQUEST_GET_RINGER_PARMS = 0x31, + CDC_REQUEST_SET_OPERATION_PARMS = 0x32, + CDC_REQUEST_GET_OPERATION_PARMS = 0x33, + CDC_REQUEST_SET_LINE_PARMS = 0x34, + CDC_REQUEST_GET_LINE_PARMS = 0x35, + CDC_REQUEST_DIAL_DIGITS = 0x36, + CDC_REQUEST_SET_UNIT_PARAMETER = 0x37, + CDC_REQUEST_GET_UNIT_PARAMETER = 0x38, + CDC_REQUEST_CLEAR_UNIT_PARAMETER = 0x39, + CDC_REQUEST_GET_PROFILE = 0x3A, + + CDC_REQUEST_SET_ETHERNET_MULTICAST_FILTERS = 0x40, + CDC_REQUEST_SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x41, + CDC_REQUEST_GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x42, + CDC_REQUEST_SET_ETHERNET_PACKET_FILTER = 0x43, + CDC_REQUEST_GET_ETHERNET_STATISTIC = 0x44, + + CDC_REQUEST_SET_ATM_DATA_FORMAT = 0x50, + CDC_REQUEST_GET_ATM_DEVICE_STATISTICS = 0x51, + CDC_REQUEST_SET_ATM_DEFAULT_VC = 0x52, + CDC_REQUEST_GET_ATM_VC_STATISTICS = 0x53, + + CDC_REQUEST_MDLM_SEMANTIC_MODEL = 0x60, +} cdc_management_request_t; + +typedef enum { + CDC_CONTROL_LINE_STATE_DTR = 0x01, + CDC_CONTROL_LINE_STATE_RTS = 0x02, +} cdc_control_line_state_t; + +typedef enum { + CDC_LINE_CODING_STOP_BITS_1 = 0, // 1 bit + CDC_LINE_CODING_STOP_BITS_1_5 = 1, // 1.5 bits + CDC_LINE_CODING_STOP_BITS_2 = 2, // 2 bits +} cdc_line_coding_stopbits_t; + +// TODO Backward compatible for typos. Maybe removed in the future release +#define CDC_LINE_CONDING_STOP_BITS_1 CDC_LINE_CODING_STOP_BITS_1 +#define CDC_LINE_CONDING_STOP_BITS_1_5 CDC_LINE_CODING_STOP_BITS_1_5 +#define CDC_LINE_CONDING_STOP_BITS_2 CDC_LINE_CODING_STOP_BITS_2 + +typedef enum { + CDC_LINE_CODING_PARITY_NONE = 0, + CDC_LINE_CODING_PARITY_ODD = 1, + CDC_LINE_CODING_PARITY_EVEN = 2, + CDC_LINE_CODING_PARITY_MARK = 3, + CDC_LINE_CODING_PARITY_SPACE = 4, +} cdc_line_coding_parity_t; + +//--------------------------------------------------------------------+ +// Management Element Notification (Notification Endpoint) +//--------------------------------------------------------------------+ + +/// 6.3 Notification Codes +typedef enum { + CDC_NOTIF_NETWORK_CONNECTION = 0x00, ///< This notification allows the device to notify the host about network connection status. + CDC_NOTIF_RESPONSE_AVAILABLE = 0x01, ///< This notification allows the device to notify the hostthat a response is available. This response can be retrieved with a subsequent \ref CDC_REQUEST_GET_ENCAPSULATED_RESPONSE request. + CDC_NOTIF_AUX_JACK_HOOK_STATE = 0x08, + CDC_NOTIF_RING_DETECT = 0x09, + CDC_NOTIF_SERIAL_STATE = 0x20, + CDC_NOTIF_CALL_STATE_CHANGE = 0x28, + CDC_NOTIF_LINE_STATE_CHANGE = 0x29, + CDC_NOTIF_CONNECTION_SPEED_CHANGE = 0x2A, ///< This notification allows the device to inform the host-networking driver that a change in either the upstream or the downstream bit rate of the connection has occurred + CDC_NOTIF_MDLM_SEMANTIC_MODEL_NOTIFICATION = 0x40, +}cdc_notification_request_t; + +//--------------------------------------------------------------------+ +// Class Specific Functional Descriptor (Communication Interface) +//--------------------------------------------------------------------+ + +// Start of all packed definitions for compiler without per-type packed +TU_ATTR_PACKED_BEGIN +TU_ATTR_BIT_FIELD_ORDER_BEGIN + +/// Header Functional Descriptor (Communication Interface) +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUNC_DESC_ + uint16_t bcdCDC ; ///< CDC release number in Binary-Coded Decimal +}cdc_desc_func_header_t; + +/// Union Functional Descriptor (Communication Interface) +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + uint8_t bControlInterface ; ///< Interface number of Communication Interface + uint8_t bSubordinateInterface ; ///< Array of Interface number of Data Interface +}cdc_desc_func_union_t; + +#define cdc_desc_func_union_n_t(no_slave)\ + struct TU_ATTR_PACKED { \ + uint8_t bLength ;\ + uint8_t bDescriptorType ;\ + uint8_t bDescriptorSubType ;\ + uint8_t bControlInterface ;\ + uint8_t bSubordinateInterface[no_slave] ;\ +} + +/// Country Selection Functional Descriptor (Communication Interface) +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + uint8_t iCountryCodeRelDate ; ///< Index of a string giving the release date for the implemented ISO 3166 Country Codes. + uint16_t wCountryCode ; ///< Country code in the format as defined in [ISO3166], release date as specified inoffset 3 for the first supported country. +}cdc_desc_func_country_selection_t; + +#define cdc_desc_func_country_selection_n_t(no_country) \ + struct TU_ATTR_PACKED { \ + uint8_t bLength ;\ + uint8_t bDescriptorType ;\ + uint8_t bDescriptorSubType ;\ + uint8_t iCountryCodeRelDate ;\ + uint16_t wCountryCode[no_country] ;\ +} + +//--------------------------------------------------------------------+ +// PUBLIC SWITCHED TELEPHONE NETWORK (PSTN) SUBCLASS +//--------------------------------------------------------------------+ + +/// \brief Call Management Functional Descriptor +/// \details This functional descriptor describes the processing of calls for the Communications Class interface. +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + + struct { + uint8_t handle_call : 1; ///< 0 - Device sends/receives call management information only over the Communications Class interface. 1 - Device can send/receive call management information over a Data Class interface. + uint8_t send_recv_call : 1; ///< 0 - Device does not handle call management itself. 1 - Device handles call management itself. + uint8_t TU_RESERVED : 6; + } bmCapabilities; + + uint8_t bDataInterface; +}cdc_desc_func_call_management_t; + +typedef struct TU_ATTR_PACKED +{ + uint8_t support_comm_request : 1; ///< Device supports the request combination of Set_Comm_Feature, Clear_Comm_Feature, and Get_Comm_Feature. + uint8_t support_line_request : 1; ///< Device supports the request combination of Set_Line_Coding, Set_Control_Line_State, Get_Line_Coding, and the notification Serial_State. + uint8_t support_send_break : 1; ///< Device supports the request Send_Break + uint8_t support_notification_network_connection : 1; ///< Device supports the notification Network_Connection. + uint8_t TU_RESERVED : 4; +}cdc_acm_capability_t; + +TU_VERIFY_STATIC(sizeof(cdc_acm_capability_t) == 1, "mostly problem with compiler"); + +/// Abstract Control Management Functional Descriptor +/// This functional descriptor describes the commands supported by by the Communications Class interface with SubClass code of \ref CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + cdc_acm_capability_t bmCapabilities ; +}cdc_desc_func_acm_t; + +/// \brief Direct Line Management Functional Descriptor +/// \details This functional descriptor describes the commands supported by the Communications Class interface with SubClass code of \ref CDC_FUNC_DESC_DIRECT_LINE_MANAGEMENT +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + struct { + uint8_t require_pulse_setup : 1; ///< Device requires extra Pulse_Setup request during pulse dialing sequence to disengage holding circuit. + uint8_t support_aux_request : 1; ///< Device supports the request combination of Set_Aux_Line_State, Ring_Aux_Jack, and notification Aux_Jack_Hook_State. + uint8_t support_pulse_request : 1; ///< Device supports the request combination of Pulse_Setup, Send_Pulse, and Set_Pulse_Time. + uint8_t TU_RESERVED : 5; + } bmCapabilities; +}cdc_desc_func_direct_line_management_t; + +/// \brief Telephone Ringer Functional Descriptor +/// \details The Telephone Ringer functional descriptor describes the ringer capabilities supported by the Communications Class interface, +/// with the SubClass code of \ref CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + uint8_t bRingerVolSteps ; + uint8_t bNumRingerPatterns ; +}cdc_desc_func_telephone_ringer_t; + +/// \brief Telephone Operational Modes Functional Descriptor +/// \details The Telephone Operational Modes functional descriptor describes the operational modes supported by +/// the Communications Class interface, with the SubClass code of \ref CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + struct { + uint8_t simple_mode : 1; + uint8_t standalone_mode : 1; + uint8_t computer_centric_mode : 1; + uint8_t TU_RESERVED : 5; + } bmCapabilities; +}cdc_desc_func_telephone_operational_modes_t; + +/// \brief Telephone Call and Line State Reporting Capabilities Descriptor +/// \details The Telephone Call and Line State Reporting Capabilities functional descriptor describes the abilities of a +/// telephone device to report optional call and line states. +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + struct { + uint32_t interrupted_dialtone : 1; ///< 0 : Reports only dialtone (does not differentiate between normal and interrupted dialtone). 1 : Reports interrupted dialtone in addition to normal dialtone + uint32_t ringback_busy_fastbusy : 1; ///< 0 : Reports only dialing state. 1 : Reports ringback, busy, and fast busy states. + uint32_t caller_id : 1; ///< 0 : Does not report caller ID. 1 : Reports caller ID information. + uint32_t incoming_distinctive : 1; ///< 0 : Reports only incoming ringing. 1 : Reports incoming distinctive ringing patterns. + uint32_t dual_tone_multi_freq : 1; ///< 0 : Cannot report dual tone multi-frequency (DTMF) digits input remotely over the telephone line. 1 : Can report DTMF digits input remotely over the telephone line. + uint32_t line_state_change : 1; ///< 0 : Does not support line state change notification. 1 : Does support line state change notification + uint32_t TU_RESERVED0 : 2; + uint32_t TU_RESERVED1 : 16; + uint32_t TU_RESERVED2 : 8; + } bmCapabilities; +}cdc_desc_func_telephone_call_state_reporting_capabilities_t; + +// TODO remove +static inline uint8_t cdc_functional_desc_typeof(uint8_t const * p_desc) +{ + return p_desc[2]; +} + +//--------------------------------------------------------------------+ +// Requests +//--------------------------------------------------------------------+ +typedef struct TU_ATTR_PACKED +{ + uint32_t bit_rate; + uint8_t stop_bits; ///< 0: 1 stop bit - 1: 1.5 stop bits - 2: 2 stop bits + uint8_t parity; ///< 0: None - 1: Odd - 2: Even - 3: Mark - 4: Space + uint8_t data_bits; ///< can be 5, 6, 7, 8 or 16 +} cdc_line_coding_t; + +TU_VERIFY_STATIC(sizeof(cdc_line_coding_t) == 7, "size is not correct"); + +typedef struct TU_ATTR_PACKED +{ + uint16_t dtr : 1; + uint16_t rts : 1; + uint16_t : 6; + uint16_t : 8; +} cdc_line_control_state_t; + +TU_VERIFY_STATIC(sizeof(cdc_line_control_state_t) == 2, "size is not correct"); + +TU_ATTR_PACKED_END // End of all packed definitions +TU_ATTR_BIT_FIELD_ORDER_END + +#ifdef __cplusplus + } +#endif + +#endif + +/** @} */ diff --git a/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc_device.c b/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc_device.c new file mode 100644 index 00000000..2e0a0c30 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc_device.c @@ -0,0 +1,517 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if (CFG_TUD_ENABLED && CFG_TUD_CDC) + +#include "device/usbd.h" +#include "device/usbd_pvt.h" + +#include "cdc_device.h" + +// Level where CFG_TUSB_DEBUG must be at least for this driver is logged +#ifndef CFG_TUD_CDC_LOG_LEVEL + #define CFG_TUD_CDC_LOG_LEVEL CFG_TUD_LOG_LEVEL +#endif + +#define TU_LOG_DRV(...) TU_LOG(CFG_TUD_CDC_LOG_LEVEL, __VA_ARGS__) + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +#define BULK_PACKET_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + +typedef struct +{ + uint8_t itf_num; + uint8_t ep_notif; + uint8_t ep_in; + uint8_t ep_out; + + // Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send) + uint8_t line_state; + + /*------------- From this point, data is not cleared by bus reset -------------*/ + char wanted_char; + TU_ATTR_ALIGNED(4) cdc_line_coding_t line_coding; + + // FIFO + tu_fifo_t rx_ff; + tu_fifo_t tx_ff; + + uint8_t rx_ff_buf[CFG_TUD_CDC_RX_BUFSIZE]; + uint8_t tx_ff_buf[CFG_TUD_CDC_TX_BUFSIZE]; + + OSAL_MUTEX_DEF(rx_ff_mutex); + OSAL_MUTEX_DEF(tx_ff_mutex); + + // Endpoint Transfer buffer + CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CDC_EP_BUFSIZE]; + CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CDC_EP_BUFSIZE]; + +}cdcd_interface_t; + +#define ITF_MEM_RESET_SIZE offsetof(cdcd_interface_t, wanted_char) + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ +CFG_TUD_MEM_SECTION tu_static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC]; + +static bool _prep_out_transaction (cdcd_interface_t* p_cdc) +{ + uint8_t const rhport = 0; + uint16_t available = tu_fifo_remaining(&p_cdc->rx_ff); + + // Prepare for incoming data but only allow what we can store in the ring buffer. + // TODO Actually we can still carry out the transfer, keeping count of received bytes + // and slowly move it to the FIFO when read(). + // This pre-check reduces endpoint claiming + TU_VERIFY(available >= sizeof(p_cdc->epout_buf)); + + // claim endpoint + TU_VERIFY(usbd_edpt_claim(rhport, p_cdc->ep_out)); + + // fifo can be changed before endpoint is claimed + available = tu_fifo_remaining(&p_cdc->rx_ff); + + if ( available >= sizeof(p_cdc->epout_buf) ) + { + return usbd_edpt_xfer(rhport, p_cdc->ep_out, p_cdc->epout_buf, sizeof(p_cdc->epout_buf)); + }else + { + // Release endpoint since we don't make any transfer + usbd_edpt_release(rhport, p_cdc->ep_out); + + return false; + } +} + +//--------------------------------------------------------------------+ +// APPLICATION API +//--------------------------------------------------------------------+ +bool tud_cdc_n_connected(uint8_t itf) +{ + // DTR (bit 0) active is considered as connected + return tud_ready() && tu_bit_test(_cdcd_itf[itf].line_state, 0); +} + +uint8_t tud_cdc_n_get_line_state (uint8_t itf) +{ + return _cdcd_itf[itf].line_state; +} + +void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding) +{ + (*coding) = _cdcd_itf[itf].line_coding; +} + +void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted) +{ + _cdcd_itf[itf].wanted_char = wanted; +} + + +//--------------------------------------------------------------------+ +// READ API +//--------------------------------------------------------------------+ +uint32_t tud_cdc_n_available(uint8_t itf) +{ + return tu_fifo_count(&_cdcd_itf[itf].rx_ff); +} + +uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize) +{ + cdcd_interface_t* p_cdc = &_cdcd_itf[itf]; + uint32_t num_read = tu_fifo_read_n(&p_cdc->rx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX)); + _prep_out_transaction(p_cdc); + return num_read; +} + +bool tud_cdc_n_peek(uint8_t itf, uint8_t* chr) +{ + return tu_fifo_peek(&_cdcd_itf[itf].rx_ff, chr); +} + +void tud_cdc_n_read_flush (uint8_t itf) +{ + cdcd_interface_t* p_cdc = &_cdcd_itf[itf]; + tu_fifo_clear(&p_cdc->rx_ff); + _prep_out_transaction(p_cdc); +} + +//--------------------------------------------------------------------+ +// WRITE API +//--------------------------------------------------------------------+ +uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize) +{ + cdcd_interface_t* p_cdc = &_cdcd_itf[itf]; + uint16_t ret = tu_fifo_write_n(&p_cdc->tx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX)); + + // flush if queue more than packet size + if ( tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE + #if CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE + || tu_fifo_full(&p_cdc->tx_ff) // check full if fifo size is less than packet size + #endif + ) { + tud_cdc_n_write_flush(itf); + } + + return ret; +} + +uint32_t tud_cdc_n_write_flush (uint8_t itf) +{ + cdcd_interface_t* p_cdc = &_cdcd_itf[itf]; + + // Skip if usb is not ready yet + TU_VERIFY( tud_ready(), 0 ); + + // No data to send + if ( !tu_fifo_count(&p_cdc->tx_ff) ) return 0; + + uint8_t const rhport = 0; + + // Claim the endpoint + TU_VERIFY( usbd_edpt_claim(rhport, p_cdc->ep_in), 0 ); + + // Pull data from FIFO + uint16_t const count = tu_fifo_read_n(&p_cdc->tx_ff, p_cdc->epin_buf, sizeof(p_cdc->epin_buf)); + + if ( count ) + { + TU_ASSERT( usbd_edpt_xfer(rhport, p_cdc->ep_in, p_cdc->epin_buf, count), 0 ); + return count; + }else + { + // Release endpoint since we don't make any transfer + // Note: data is dropped if terminal is not connected + usbd_edpt_release(rhport, p_cdc->ep_in); + return 0; + } +} + +uint32_t tud_cdc_n_write_available (uint8_t itf) +{ + return tu_fifo_remaining(&_cdcd_itf[itf].tx_ff); +} + +bool tud_cdc_n_write_clear (uint8_t itf) +{ + return tu_fifo_clear(&_cdcd_itf[itf].tx_ff); +} + +//--------------------------------------------------------------------+ +// USBD Driver API +//--------------------------------------------------------------------+ +void cdcd_init(void) +{ + tu_memclr(_cdcd_itf, sizeof(_cdcd_itf)); + + for(uint8_t i=0; iwanted_char = (char) -1; + + // default line coding is : stop bit = 1, parity = none, data bits = 8 + p_cdc->line_coding.bit_rate = 115200; + p_cdc->line_coding.stop_bits = 0; + p_cdc->line_coding.parity = 0; + p_cdc->line_coding.data_bits = 8; + + // Config RX fifo + tu_fifo_config(&p_cdc->rx_ff, p_cdc->rx_ff_buf, TU_ARRAY_SIZE(p_cdc->rx_ff_buf), 1, false); + + // Config TX fifo as overwritable at initialization and will be changed to non-overwritable + // if terminal supports DTR bit. Without DTR we do not know if data is actually polled by terminal. + // In this way, the most current data is prioritized. + tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, TU_ARRAY_SIZE(p_cdc->tx_ff_buf), 1, true); + + #if OSAL_MUTEX_REQUIRED + osal_mutex_t mutex_rd = osal_mutex_create(&p_cdc->rx_ff_mutex); + osal_mutex_t mutex_wr = osal_mutex_create(&p_cdc->tx_ff_mutex); + TU_ASSERT(mutex_rd != NULL && mutex_wr != NULL, ); + + tu_fifo_config_mutex(&p_cdc->rx_ff, NULL, mutex_rd); + tu_fifo_config_mutex(&p_cdc->tx_ff, mutex_wr, NULL); + #endif + } +} + +bool cdcd_deinit(void) { + #if OSAL_MUTEX_REQUIRED + for(uint8_t i=0; irx_ff.mutex_rd; + osal_mutex_t mutex_wr = p_cdc->tx_ff.mutex_wr; + + if (mutex_rd) { + osal_mutex_delete(mutex_rd); + tu_fifo_config_mutex(&p_cdc->rx_ff, NULL, NULL); + } + + if (mutex_wr) { + osal_mutex_delete(mutex_wr); + tu_fifo_config_mutex(&p_cdc->tx_ff, NULL, NULL); + } + } + #endif + + return true; +} + +void cdcd_reset(uint8_t rhport) +{ + (void) rhport; + + for(uint8_t i=0; irx_ff); + tu_fifo_clear(&p_cdc->tx_ff); + tu_fifo_set_overwritable(&p_cdc->tx_ff, true); + } +} + +uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) +{ + // Only support ACM subclass + TU_VERIFY( TUSB_CLASS_CDC == itf_desc->bInterfaceClass && + CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass, 0); + + // Find available interface + cdcd_interface_t * p_cdc = NULL; + for(uint8_t cdc_id=0; cdc_iditf_num = itf_desc->bInterfaceNumber; + + uint16_t drv_len = sizeof(tusb_desc_interface_t); + uint8_t const * p_desc = tu_desc_next( itf_desc ); + + // Communication Functional Descriptors + while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len ) + { + drv_len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } + + if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) ) + { + // notification endpoint + tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc; + + TU_ASSERT( usbd_edpt_open(rhport, desc_ep), 0 ); + p_cdc->ep_notif = desc_ep->bEndpointAddress; + + drv_len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } + + //------------- Data Interface (if any) -------------// + if ( (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && + (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) ) + { + // next to endpoint descriptor + drv_len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + + // Open endpoint pair + TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in), 0 ); + + drv_len += 2*sizeof(tusb_desc_endpoint_t); + } + + // Prepare for incoming data + _prep_out_transaction(p_cdc); + + return drv_len; +} + +// Invoked when a control transfer occurred on an interface of this class +// Driver response accordingly to the request and the transfer stage (setup/data/ack) +// return false to stall control endpoint (e.g unsupported request) +bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) +{ + // Handle class request only + TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); + + uint8_t itf = 0; + cdcd_interface_t* p_cdc = _cdcd_itf; + + // Identify which interface to use + for ( ; ; itf++, p_cdc++) + { + if (itf >= TU_ARRAY_SIZE(_cdcd_itf)) return false; + + if ( p_cdc->itf_num == request->wIndex ) break; + } + + switch ( request->bRequest ) + { + case CDC_REQUEST_SET_LINE_CODING: + if (stage == CONTROL_STAGE_SETUP) + { + TU_LOG_DRV(" Set Line Coding\r\n"); + tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t)); + } + else if ( stage == CONTROL_STAGE_ACK) + { + if ( tud_cdc_line_coding_cb ) tud_cdc_line_coding_cb(itf, &p_cdc->line_coding); + } + break; + + case CDC_REQUEST_GET_LINE_CODING: + if (stage == CONTROL_STAGE_SETUP) + { + TU_LOG_DRV(" Get Line Coding\r\n"); + tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t)); + } + break; + + case CDC_REQUEST_SET_CONTROL_LINE_STATE: + if (stage == CONTROL_STAGE_SETUP) + { + tud_control_status(rhport, request); + } + else if (stage == CONTROL_STAGE_ACK) + { + // CDC PSTN v1.2 section 6.3.12 + // Bit 0: Indicates if DTE is present or not. + // This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR (Data Terminal Ready) + // Bit 1: Carrier control for half-duplex modems. + // This signal corresponds to V.24 signal 105 and RS-232 signal RTS (Request to Send) + bool const dtr = tu_bit_test(request->wValue, 0); + bool const rts = tu_bit_test(request->wValue, 1); + + p_cdc->line_state = (uint8_t) request->wValue; + + // Disable fifo overwriting if DTR bit is set + tu_fifo_set_overwritable(&p_cdc->tx_ff, !dtr); + + TU_LOG_DRV(" Set Control Line State: DTR = %d, RTS = %d\r\n", dtr, rts); + + // Invoke callback + if ( tud_cdc_line_state_cb ) tud_cdc_line_state_cb(itf, dtr, rts); + } + break; + case CDC_REQUEST_SEND_BREAK: + if (stage == CONTROL_STAGE_SETUP) + { + tud_control_status(rhport, request); + } + else if (stage == CONTROL_STAGE_ACK) + { + TU_LOG_DRV(" Send Break\r\n"); + if ( tud_cdc_send_break_cb ) tud_cdc_send_break_cb(itf, request->wValue); + } + break; + + default: return false; // stall unsupported request + } + + return true; +} + +bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) +{ + (void) result; + + uint8_t itf; + cdcd_interface_t* p_cdc; + + // Identify which interface to use + for (itf = 0; itf < CFG_TUD_CDC; itf++) + { + p_cdc = &_cdcd_itf[itf]; + if ( ( ep_addr == p_cdc->ep_out ) || ( ep_addr == p_cdc->ep_in ) ) break; + } + TU_ASSERT(itf < CFG_TUD_CDC); + + // Received new data + if ( ep_addr == p_cdc->ep_out ) + { + tu_fifo_write_n(&p_cdc->rx_ff, p_cdc->epout_buf, (uint16_t) xferred_bytes); + + // Check for wanted char and invoke callback if needed + if ( tud_cdc_rx_wanted_cb && (((signed char) p_cdc->wanted_char) != -1) ) + { + for ( uint32_t i = 0; i < xferred_bytes; i++ ) + { + if ( (p_cdc->wanted_char == p_cdc->epout_buf[i]) && !tu_fifo_empty(&p_cdc->rx_ff) ) + { + tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char); + } + } + } + + // invoke receive callback (if there is still data) + if (tud_cdc_rx_cb && !tu_fifo_empty(&p_cdc->rx_ff) ) tud_cdc_rx_cb(itf); + + // prepare for OUT transaction + _prep_out_transaction(p_cdc); + } + + // Data sent to host, we continue to fetch from tx fifo to send. + // Note: This will cause incorrect baudrate set in line coding. + // Though maybe the baudrate is not really important !!! + if ( ep_addr == p_cdc->ep_in ) + { + // invoke transmit callback to possibly refill tx fifo + if ( tud_cdc_tx_complete_cb ) tud_cdc_tx_complete_cb(itf); + + if ( 0 == tud_cdc_n_write_flush(itf) ) + { + // If there is no data left, a ZLP should be sent if + // xferred_bytes is multiple of EP Packet size and not zero + if ( !tu_fifo_count(&p_cdc->tx_ff) && xferred_bytes && (0 == (xferred_bytes & (BULK_PACKET_SIZE-1))) ) + { + if ( usbd_edpt_claim(rhport, p_cdc->ep_in) ) + { + usbd_edpt_xfer(rhport, p_cdc->ep_in, NULL, 0); + } + } + } + } + + // nothing to do with notif endpoint for now + + return true; +} + +#endif diff --git a/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc_device.h b/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc_device.h new file mode 100644 index 00000000..20e90845 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc_device.h @@ -0,0 +1,260 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_CDC_DEVICE_H_ +#define _TUSB_CDC_DEVICE_H_ + +#include "cdc.h" + +//--------------------------------------------------------------------+ +// Class Driver Configuration +//--------------------------------------------------------------------+ +#if !defined(CFG_TUD_CDC_EP_BUFSIZE) && defined(CFG_TUD_CDC_EPSIZE) + #warning CFG_TUD_CDC_EPSIZE is renamed to CFG_TUD_CDC_EP_BUFSIZE, please update to use the new name + #define CFG_TUD_CDC_EP_BUFSIZE CFG_TUD_CDC_EPSIZE +#endif + +#ifndef CFG_TUD_CDC_EP_BUFSIZE + #define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/** \addtogroup CDC_Serial Serial + * @{ + * \defgroup CDC_Serial_Device Device + * @{ */ + +//--------------------------------------------------------------------+ +// Application API (Multiple Ports) +// CFG_TUD_CDC > 1 +//--------------------------------------------------------------------+ + +// Check if terminal is connected to this port +bool tud_cdc_n_connected (uint8_t itf); + +// Get current line state. Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send) +uint8_t tud_cdc_n_get_line_state (uint8_t itf); + +// Get current line encoding: bit rate, stop bits parity etc .. +void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding); + +// Set special character that will trigger tud_cdc_rx_wanted_cb() callback on receiving +void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted); + +// Get the number of bytes available for reading +uint32_t tud_cdc_n_available (uint8_t itf); + +// Read received bytes +uint32_t tud_cdc_n_read (uint8_t itf, void* buffer, uint32_t bufsize); + +// Read a byte, return -1 if there is none +static inline +int32_t tud_cdc_n_read_char (uint8_t itf); + +// Clear the received FIFO +void tud_cdc_n_read_flush (uint8_t itf); + +// Get a byte from FIFO without removing it +bool tud_cdc_n_peek (uint8_t itf, uint8_t* ui8); + +// Write bytes to TX FIFO, data may remain in the FIFO for a while +uint32_t tud_cdc_n_write (uint8_t itf, void const* buffer, uint32_t bufsize); + +// Write a byte +static inline +uint32_t tud_cdc_n_write_char (uint8_t itf, char ch); + +// Write a null-terminated string +static inline +uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str); + +// Force sending data if possible, return number of forced bytes +uint32_t tud_cdc_n_write_flush (uint8_t itf); + +// Return the number of bytes (characters) available for writing to TX FIFO buffer in a single n_write operation. +uint32_t tud_cdc_n_write_available (uint8_t itf); + +// Clear the transmit FIFO +bool tud_cdc_n_write_clear (uint8_t itf); + +//--------------------------------------------------------------------+ +// Application API (Single Port) +//--------------------------------------------------------------------+ +static inline bool tud_cdc_connected (void); +static inline uint8_t tud_cdc_get_line_state (void); +static inline void tud_cdc_get_line_coding (cdc_line_coding_t* coding); +static inline void tud_cdc_set_wanted_char (char wanted); + +static inline uint32_t tud_cdc_available (void); +static inline int32_t tud_cdc_read_char (void); +static inline uint32_t tud_cdc_read (void* buffer, uint32_t bufsize); +static inline void tud_cdc_read_flush (void); +static inline bool tud_cdc_peek (uint8_t* ui8); + +static inline uint32_t tud_cdc_write_char (char ch); +static inline uint32_t tud_cdc_write (void const* buffer, uint32_t bufsize); +static inline uint32_t tud_cdc_write_str (char const* str); +static inline uint32_t tud_cdc_write_flush (void); +static inline uint32_t tud_cdc_write_available (void); +static inline bool tud_cdc_write_clear (void); + +//--------------------------------------------------------------------+ +// Application Callback API (weak is optional) +//--------------------------------------------------------------------+ + +// Invoked when received new data +TU_ATTR_WEAK void tud_cdc_rx_cb(uint8_t itf); + +// Invoked when received `wanted_char` +TU_ATTR_WEAK void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char); + +// Invoked when a TX is complete and therefore space becomes available in TX buffer +TU_ATTR_WEAK void tud_cdc_tx_complete_cb(uint8_t itf); + +// Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE +TU_ATTR_WEAK void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts); + +// Invoked when line coding is change via SET_LINE_CODING +TU_ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding); + +// Invoked when received send break +TU_ATTR_WEAK void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms); + +//--------------------------------------------------------------------+ +// Inline Functions +//--------------------------------------------------------------------+ +static inline int32_t tud_cdc_n_read_char (uint8_t itf) +{ + uint8_t ch; + return tud_cdc_n_read(itf, &ch, 1) ? (int32_t) ch : -1; +} + +static inline uint32_t tud_cdc_n_write_char(uint8_t itf, char ch) +{ + return tud_cdc_n_write(itf, &ch, 1); +} + +static inline uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str) +{ + return tud_cdc_n_write(itf, str, strlen(str)); +} + +static inline bool tud_cdc_connected (void) +{ + return tud_cdc_n_connected(0); +} + +static inline uint8_t tud_cdc_get_line_state (void) +{ + return tud_cdc_n_get_line_state(0); +} + +static inline void tud_cdc_get_line_coding (cdc_line_coding_t* coding) +{ + tud_cdc_n_get_line_coding(0, coding); +} + +static inline void tud_cdc_set_wanted_char (char wanted) +{ + tud_cdc_n_set_wanted_char(0, wanted); +} + +static inline uint32_t tud_cdc_available (void) +{ + return tud_cdc_n_available(0); +} + +static inline int32_t tud_cdc_read_char (void) +{ + return tud_cdc_n_read_char(0); +} + +static inline uint32_t tud_cdc_read (void* buffer, uint32_t bufsize) +{ + return tud_cdc_n_read(0, buffer, bufsize); +} + +static inline void tud_cdc_read_flush (void) +{ + tud_cdc_n_read_flush(0); +} + +static inline bool tud_cdc_peek (uint8_t* ui8) +{ + return tud_cdc_n_peek(0, ui8); +} + +static inline uint32_t tud_cdc_write_char (char ch) +{ + return tud_cdc_n_write_char(0, ch); +} + +static inline uint32_t tud_cdc_write (void const* buffer, uint32_t bufsize) +{ + return tud_cdc_n_write(0, buffer, bufsize); +} + +static inline uint32_t tud_cdc_write_str (char const* str) +{ + return tud_cdc_n_write_str(0, str); +} + +static inline uint32_t tud_cdc_write_flush (void) +{ + return tud_cdc_n_write_flush(0); +} + +static inline uint32_t tud_cdc_write_available(void) +{ + return tud_cdc_n_write_available(0); +} + +static inline bool tud_cdc_write_clear(void) +{ + return tud_cdc_n_write_clear(0); +} + +/** @} */ +/** @} */ + +//--------------------------------------------------------------------+ +// INTERNAL USBD-CLASS DRIVER API +//--------------------------------------------------------------------+ +void cdcd_init (void); +bool cdcd_deinit (void); +void cdcd_reset (uint8_t rhport); +uint16_t cdcd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len); +bool cdcd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); +bool cdcd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CDC_DEVICE_H_ */ diff --git a/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc_host.c b/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc_host.c new file mode 100644 index 00000000..133a10f6 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc_host.c @@ -0,0 +1,1670 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + * + * Contribution + * - Heiko Kuester: CH34x support + */ + +#include "tusb_option.h" + +#if (CFG_TUH_ENABLED && CFG_TUH_CDC) + +#include "host/usbh.h" +#include "host/usbh_pvt.h" + +#include "cdc_host.h" + +// Level where CFG_TUSB_DEBUG must be at least for this driver is logged +#ifndef CFG_TUH_CDC_LOG_LEVEL + #define CFG_TUH_CDC_LOG_LEVEL CFG_TUH_LOG_LEVEL +#endif + +#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_CDC_LOG_LEVEL, __VA_ARGS__) + +//--------------------------------------------------------------------+ +// Host CDC Interface +//--------------------------------------------------------------------+ + +typedef struct { + uint8_t daddr; + uint8_t bInterfaceNumber; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + + uint8_t ep_notif; + uint8_t serial_drid; // Serial Driver ID + bool mounted; // Enumeration is complete + cdc_acm_capability_t acm_capability; + + TU_ATTR_ALIGNED(4) cdc_line_coding_t line_coding; // Baudrate, stop bits, parity, data width + uint8_t line_state; // DTR (bit0), RTS (bit1) + + #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X || CFG_TUH_CDC_CH34X + cdc_line_coding_t requested_line_coding; + // 1 byte padding + #endif + + tuh_xfer_cb_t user_control_cb; + + struct { + tu_edpt_stream_t tx; + tu_edpt_stream_t rx; + + uint8_t tx_ff_buf[CFG_TUH_CDC_TX_BUFSIZE]; + CFG_TUH_MEM_ALIGN uint8_t tx_ep_buf[CFG_TUH_CDC_TX_EPSIZE]; + + uint8_t rx_ff_buf[CFG_TUH_CDC_TX_BUFSIZE]; + CFG_TUH_MEM_ALIGN uint8_t rx_ep_buf[CFG_TUH_CDC_TX_EPSIZE]; + } stream; +} cdch_interface_t; + +CFG_TUH_MEM_SECTION +static cdch_interface_t cdch_data[CFG_TUH_CDC]; + +//--------------------------------------------------------------------+ +// Serial Driver +//--------------------------------------------------------------------+ + +//------------- ACM prototypes -------------// +static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len); +static void acm_process_config(tuh_xfer_t* xfer); + +static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool acm_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + +//------------- FTDI prototypes -------------// +#if CFG_TUH_CDC_FTDI +#include "serial/ftdi_sio.h" + +static uint16_t const ftdi_vid_pid_list[][2] = {CFG_TUH_CDC_FTDI_VID_PID_LIST}; + +static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len); +static void ftdi_process_config(tuh_xfer_t* xfer); + +static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ftdi_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +#endif + +//------------- CP210X prototypes -------------// +#if CFG_TUH_CDC_CP210X +#include "serial/cp210x.h" + +static uint16_t const cp210x_vid_pid_list[][2] = {CFG_TUH_CDC_CP210X_VID_PID_LIST}; + +static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len); +static void cp210x_process_config(tuh_xfer_t* xfer); + +static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool cp210x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +#endif + +//------------- CH34x prototypes -------------// +#if CFG_TUH_CDC_CH34X +#include "serial/ch34x.h" + +static uint16_t const ch34x_vid_pid_list[][2] = {CFG_TUH_CDC_CH34X_VID_PID_LIST}; + +static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len); +static void ch34x_process_config(tuh_xfer_t* xfer); + +static bool ch34x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ch34x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +#endif + +//------------- Common -------------// +enum { + SERIAL_DRIVER_ACM = 0, + +#if CFG_TUH_CDC_FTDI + SERIAL_DRIVER_FTDI, +#endif + +#if CFG_TUH_CDC_CP210X + SERIAL_DRIVER_CP210X, +#endif + +#if CFG_TUH_CDC_CH34X + SERIAL_DRIVER_CH34X, +#endif + + SERIAL_DRIVER_COUNT +}; + +typedef struct { + uint16_t const (*vid_pid_list)[2]; + uint16_t const vid_pid_count; + bool (*const open)(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len); + void (*const process_set_config)(tuh_xfer_t* xfer); + bool (*const set_control_line_state)(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + bool (*const set_baudrate)(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + bool (*const set_data_format)(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + bool (*const set_line_coding)(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +} cdch_serial_driver_t; + +// Note driver list must be in the same order as SERIAL_DRIVER enum +static const cdch_serial_driver_t serial_drivers[] = { + { + .vid_pid_list = NULL, + .vid_pid_count = 0, + .open = acm_open, + .process_set_config = acm_process_config, + .set_control_line_state = acm_set_control_line_state, + .set_baudrate = acm_set_baudrate, + .set_data_format = acm_set_data_format, + .set_line_coding = acm_set_line_coding + }, + + #if CFG_TUH_CDC_FTDI + { + .vid_pid_list = ftdi_vid_pid_list, + .vid_pid_count = TU_ARRAY_SIZE(ftdi_vid_pid_list), + .open = ftdi_open, + .process_set_config = ftdi_process_config, + .set_control_line_state = ftdi_sio_set_modem_ctrl, + .set_baudrate = ftdi_sio_set_baudrate, + .set_data_format = ftdi_set_data_format, + .set_line_coding = ftdi_set_line_coding + }, + #endif + + #if CFG_TUH_CDC_CP210X + { + .vid_pid_list = cp210x_vid_pid_list, + .vid_pid_count = TU_ARRAY_SIZE(cp210x_vid_pid_list), + .open = cp210x_open, + .process_set_config = cp210x_process_config, + .set_control_line_state = cp210x_set_modem_ctrl, + .set_baudrate = cp210x_set_baudrate, + .set_data_format = cp210x_set_data_format, + .set_line_coding = cp210x_set_line_coding + }, + #endif + + #if CFG_TUH_CDC_CH34X + { + .vid_pid_list = ch34x_vid_pid_list, + .vid_pid_count = TU_ARRAY_SIZE(ch34x_vid_pid_list), + .open = ch34x_open, + .process_set_config = ch34x_process_config, + .set_control_line_state = ch34x_set_modem_ctrl, + .set_baudrate = ch34x_set_baudrate, + .set_data_format = ch34x_set_data_format, + .set_line_coding = ch34x_set_line_coding + }, + #endif +}; + +TU_VERIFY_STATIC(TU_ARRAY_SIZE(serial_drivers) == SERIAL_DRIVER_COUNT, "Serial driver count mismatch"); + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ + +static inline cdch_interface_t* get_itf(uint8_t idx) { + TU_ASSERT(idx < CFG_TUH_CDC, NULL); + cdch_interface_t* p_cdc = &cdch_data[idx]; + + return (p_cdc->daddr != 0) ? p_cdc : NULL; +} + +static inline uint8_t get_idx_by_ep_addr(uint8_t daddr, uint8_t ep_addr) { + for(uint8_t i=0; idaddr == daddr) && + (ep_addr == p_cdc->ep_notif || ep_addr == p_cdc->stream.rx.ep_addr || ep_addr == p_cdc->stream.tx.ep_addr)) { + return i; + } + } + + return TUSB_INDEX_INVALID_8; +} + +static cdch_interface_t* make_new_itf(uint8_t daddr, tusb_desc_interface_t const *itf_desc) { + for(uint8_t i=0; idaddr = daddr; + p_cdc->bInterfaceNumber = itf_desc->bInterfaceNumber; + p_cdc->bInterfaceSubClass = itf_desc->bInterfaceSubClass; + p_cdc->bInterfaceProtocol = itf_desc->bInterfaceProtocol; + p_cdc->line_state = 0; + return p_cdc; + } + } + + return NULL; +} + +static bool open_ep_stream_pair(cdch_interface_t* p_cdc , tusb_desc_endpoint_t const *desc_ep); +static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t itf_num); +static void cdch_internal_control_complete(tuh_xfer_t* xfer); + +//--------------------------------------------------------------------+ +// APPLICATION API +//--------------------------------------------------------------------+ + +uint8_t tuh_cdc_itf_get_index(uint8_t daddr, uint8_t itf_num) { + for (uint8_t i = 0; i < CFG_TUH_CDC; i++) { + const cdch_interface_t* p_cdc = &cdch_data[i]; + if (p_cdc->daddr == daddr && p_cdc->bInterfaceNumber == itf_num) return i; + } + + return TUSB_INDEX_INVALID_8; +} + +bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info) { + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc && info); + + info->daddr = p_cdc->daddr; + + // re-construct descriptor + tusb_desc_interface_t* desc = &info->desc; + desc->bLength = sizeof(tusb_desc_interface_t); + desc->bDescriptorType = TUSB_DESC_INTERFACE; + + desc->bInterfaceNumber = p_cdc->bInterfaceNumber; + desc->bAlternateSetting = 0; + desc->bNumEndpoints = 2u + (p_cdc->ep_notif ? 1u : 0u); + desc->bInterfaceClass = TUSB_CLASS_CDC; + desc->bInterfaceSubClass = p_cdc->bInterfaceSubClass; + desc->bInterfaceProtocol = p_cdc->bInterfaceProtocol; + desc->iInterface = 0; // not used yet + + return true; +} + +bool tuh_cdc_mounted(uint8_t idx) { + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + return p_cdc->mounted; +} + +bool tuh_cdc_get_dtr(uint8_t idx) { + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_DTR) ? true : false; +} + +bool tuh_cdc_get_rts(uint8_t idx) { + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_RTS) ? true : false; +} + +bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding) { + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + *line_coding = p_cdc->line_coding; + + return true; +} + +//--------------------------------------------------------------------+ +// Write +//--------------------------------------------------------------------+ + +uint32_t tuh_cdc_write(uint8_t idx, void const* buffer, uint32_t bufsize) { + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return tu_edpt_stream_write(&p_cdc->stream.tx, buffer, bufsize); +} + +uint32_t tuh_cdc_write_flush(uint8_t idx) { + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return tu_edpt_stream_write_xfer(&p_cdc->stream.tx); +} + +bool tuh_cdc_write_clear(uint8_t idx) { + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return tu_edpt_stream_clear(&p_cdc->stream.tx); +} + +uint32_t tuh_cdc_write_available(uint8_t idx) { + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return tu_edpt_stream_write_available(&p_cdc->stream.tx); +} + +//--------------------------------------------------------------------+ +// Read +//--------------------------------------------------------------------+ + +uint32_t tuh_cdc_read (uint8_t idx, void* buffer, uint32_t bufsize) { + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return tu_edpt_stream_read(&p_cdc->stream.rx, buffer, bufsize); +} + +uint32_t tuh_cdc_read_available(uint8_t idx) { + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return tu_edpt_stream_read_available(&p_cdc->stream.rx); +} + +bool tuh_cdc_peek(uint8_t idx, uint8_t* ch) { + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return tu_edpt_stream_peek(&p_cdc->stream.rx, ch); +} + +bool tuh_cdc_read_clear (uint8_t idx) { + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + bool ret = tu_edpt_stream_clear(&p_cdc->stream.rx); + tu_edpt_stream_read_xfer(&p_cdc->stream.rx); + return ret; +} + +//--------------------------------------------------------------------+ +// Control Endpoint API +//--------------------------------------------------------------------+ + +static void process_internal_control_complete(tuh_xfer_t* xfer, uint8_t itf_num) { + uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); + cdch_interface_t* p_cdc = get_itf(idx); + TU_ASSERT(p_cdc, ); + uint16_t const value = tu_le16toh(xfer->setup->wValue); + + if (xfer->result == XFER_RESULT_SUCCESS) { + switch (p_cdc->serial_drid) { + case SERIAL_DRIVER_ACM: + switch (xfer->setup->bRequest) { + case CDC_REQUEST_SET_CONTROL_LINE_STATE: + p_cdc->line_state = (uint8_t) value; + break; + + case CDC_REQUEST_SET_LINE_CODING: { + uint16_t const len = tu_min16(sizeof(cdc_line_coding_t), tu_le16toh(xfer->setup->wLength)); + memcpy(&p_cdc->line_coding, xfer->buffer, len); + break; + } + + default: break; + } + break; + + #if CFG_TUH_CDC_FTDI + case SERIAL_DRIVER_FTDI: + switch (xfer->setup->bRequest) { + case FTDI_SIO_MODEM_CTRL: + p_cdc->line_state = (uint8_t) value; + break; + + case FTDI_SIO_SET_BAUD_RATE: + p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate; + break; + + default: break; + } + break; + #endif + + #if CFG_TUH_CDC_CP210X + case SERIAL_DRIVER_CP210X: + switch(xfer->setup->bRequest) { + case CP210X_SET_MHS: + p_cdc->line_state = (uint8_t) value; + break; + + case CP210X_SET_BAUDRATE: { + uint32_t baudrate; + memcpy(&baudrate, xfer->buffer, sizeof(uint32_t)); + p_cdc->line_coding.bit_rate = tu_le32toh(baudrate); + break; + } + + default: break; + } + break; + #endif + + #if CFG_TUH_CDC_CH34X + case SERIAL_DRIVER_CH34X: + switch (xfer->setup->bRequest) { + case CH34X_REQ_WRITE_REG: + // register write request + switch (value) { + case CH34X_REG16_DIVISOR_PRESCALER: + // baudrate + p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate; + break; + + case CH32X_REG16_LCR2_LCR: + // data format + p_cdc->line_coding.stop_bits = p_cdc->requested_line_coding.stop_bits; + p_cdc->line_coding.parity = p_cdc->requested_line_coding.parity; + p_cdc->line_coding.data_bits = p_cdc->requested_line_coding.data_bits; + break; + + default: break; + } + break; + + case CH34X_REQ_MODEM_CTRL: { + // set modem controls RTS/DTR request. Note: signals are inverted + uint16_t const modem_signal = ~value; + if (modem_signal & CH34X_BIT_RTS) { + p_cdc->line_state |= CDC_CONTROL_LINE_STATE_RTS; + } else { + p_cdc->line_state &= (uint8_t) ~CDC_CONTROL_LINE_STATE_RTS; + } + + if (modem_signal & CH34X_BIT_DTR) { + p_cdc->line_state |= CDC_CONTROL_LINE_STATE_DTR; + } else { + p_cdc->line_state &= (uint8_t) ~CDC_CONTROL_LINE_STATE_DTR; + } + break; + } + + default: break; + } + break; + #endif + + default: break; + } + } + + xfer->complete_cb = p_cdc->user_control_cb; + if (xfer->complete_cb) { + xfer->complete_cb(xfer); + } +} + +// internal control complete to update state such as line state, encoding +static void cdch_internal_control_complete(tuh_xfer_t* xfer) { + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + process_internal_control_complete(xfer, itf_num); +} + +bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); + cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid]; + + if (complete_cb) { + return driver->set_control_line_state(p_cdc, line_state, complete_cb, user_data); + } else { + // blocking + xfer_result_t result = XFER_RESULT_INVALID; + bool ret = driver->set_control_line_state(p_cdc, line_state, complete_cb, (uintptr_t) &result); + + if (user_data) { + // user_data is not NULL, return result via user_data + *((xfer_result_t*) user_data) = result; + } + + TU_VERIFY(ret && result == XFER_RESULT_SUCCESS); + p_cdc->line_state = (uint8_t) line_state; + return true; + } +} + +bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); + cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid]; + + if (complete_cb) { + return driver->set_baudrate(p_cdc, baudrate, complete_cb, user_data); + } else { + // blocking + xfer_result_t result = XFER_RESULT_INVALID; + bool ret = driver->set_baudrate(p_cdc, baudrate, complete_cb, (uintptr_t) &result); + + if (user_data) { + // user_data is not NULL, return result via user_data + *((xfer_result_t*) user_data) = result; + } + + TU_VERIFY(ret && result == XFER_RESULT_SUCCESS); + p_cdc->line_coding.bit_rate = baudrate; + return true; + } +} + +bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); + cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid]; + + if (complete_cb) { + return driver->set_data_format(p_cdc, stop_bits, parity, data_bits, complete_cb, user_data); + } else { + // blocking + xfer_result_t result = XFER_RESULT_INVALID; + bool ret = driver->set_data_format(p_cdc, stop_bits, parity, data_bits, complete_cb, (uintptr_t) &result); + + if (user_data) { + // user_data is not NULL, return result via user_data + *((xfer_result_t*) user_data) = result; + } + + TU_VERIFY(ret && result == XFER_RESULT_SUCCESS); + p_cdc->line_coding.stop_bits = stop_bits; + p_cdc->line_coding.parity = parity; + p_cdc->line_coding.data_bits = data_bits; + return true; + } +} + +bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); + cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid]; + + if ( complete_cb ) { + return driver->set_line_coding(p_cdc, line_coding, complete_cb, user_data); + } else { + // blocking + xfer_result_t result = XFER_RESULT_INVALID; + bool ret = driver->set_line_coding(p_cdc, line_coding, complete_cb, (uintptr_t) &result); + + if (user_data) { + // user_data is not NULL, return result via user_data + *((xfer_result_t*) user_data) = result; + } + + TU_VERIFY(ret && result == XFER_RESULT_SUCCESS); + p_cdc->line_coding = *line_coding; + return true; + } +} + +//--------------------------------------------------------------------+ +// CLASS-USBH API +//--------------------------------------------------------------------+ + +bool cdch_init(void) { + TU_LOG_DRV("sizeof(cdch_interface_t) = %u\r\n", sizeof(cdch_interface_t)); + tu_memclr(cdch_data, sizeof(cdch_data)); + for (size_t i = 0; i < CFG_TUH_CDC; i++) { + cdch_interface_t* p_cdc = &cdch_data[i]; + tu_edpt_stream_init(&p_cdc->stream.tx, true, true, false, + p_cdc->stream.tx_ff_buf, CFG_TUH_CDC_TX_BUFSIZE, + p_cdc->stream.tx_ep_buf, CFG_TUH_CDC_TX_EPSIZE); + + tu_edpt_stream_init(&p_cdc->stream.rx, true, false, false, + p_cdc->stream.rx_ff_buf, CFG_TUH_CDC_RX_BUFSIZE, + p_cdc->stream.rx_ep_buf, CFG_TUH_CDC_RX_EPSIZE); + } + + return true; +} + +bool cdch_deinit(void) { + for (size_t i = 0; i < CFG_TUH_CDC; i++) { + cdch_interface_t* p_cdc = &cdch_data[i]; + tu_edpt_stream_deinit(&p_cdc->stream.tx); + tu_edpt_stream_deinit(&p_cdc->stream.rx); + } + return true; +} + +void cdch_close(uint8_t daddr) { + for (uint8_t idx = 0; idx < CFG_TUH_CDC; idx++) { + cdch_interface_t* p_cdc = &cdch_data[idx]; + if (p_cdc->daddr == daddr) { + TU_LOG_DRV(" CDCh close addr = %u index = %u\r\n", daddr, idx); + + // Invoke application callback + if (tuh_cdc_umount_cb) tuh_cdc_umount_cb(idx); + + p_cdc->daddr = 0; + p_cdc->bInterfaceNumber = 0; + p_cdc->mounted = false; + tu_edpt_stream_close(&p_cdc->stream.tx); + tu_edpt_stream_close(&p_cdc->stream.rx); + } + } +} + +bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) { + // TODO handle stall response, retry failed transfer ... + TU_ASSERT(event == XFER_RESULT_SUCCESS); + + uint8_t const idx = get_idx_by_ep_addr(daddr, ep_addr); + cdch_interface_t * p_cdc = get_itf(idx); + TU_ASSERT(p_cdc); + + if ( ep_addr == p_cdc->stream.tx.ep_addr ) { + // invoke tx complete callback to possibly refill tx fifo + if (tuh_cdc_tx_complete_cb) tuh_cdc_tx_complete_cb(idx); + + if ( 0 == tu_edpt_stream_write_xfer(&p_cdc->stream.tx) ) { + // If there is no data left, a ZLP should be sent if: + // - xferred_bytes is multiple of EP Packet size and not zero + tu_edpt_stream_write_zlp_if_needed(&p_cdc->stream.tx, xferred_bytes); + } + } else if ( ep_addr == p_cdc->stream.rx.ep_addr ) { + #if CFG_TUH_CDC_FTDI + if (p_cdc->serial_drid == SERIAL_DRIVER_FTDI) { + // FTDI reserve 2 bytes for status + // uint8_t status[2] = {p_cdc->stream.rx.ep_buf[0], p_cdc->stream.rx.ep_buf[1]}; + tu_edpt_stream_read_xfer_complete_offset(&p_cdc->stream.rx, xferred_bytes, 2); + }else + #endif + { + tu_edpt_stream_read_xfer_complete(&p_cdc->stream.rx, xferred_bytes); + } + + // invoke receive callback + if (tuh_cdc_rx_cb) tuh_cdc_rx_cb(idx); + + // prepare for next transfer if needed + tu_edpt_stream_read_xfer(&p_cdc->stream.rx); + }else if ( ep_addr == p_cdc->ep_notif ) { + // TODO handle notification endpoint + }else { + TU_ASSERT(false); + } + + return true; +} + +//--------------------------------------------------------------------+ +// Enumeration +//--------------------------------------------------------------------+ + +static bool open_ep_stream_pair(cdch_interface_t* p_cdc, tusb_desc_endpoint_t const* desc_ep) { + for (size_t i = 0; i < 2; i++) { + TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && + TUSB_XFER_BULK == desc_ep->bmAttributes.xfer); + TU_ASSERT(tuh_edpt_open(p_cdc->daddr, desc_ep)); + + if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) { + tu_edpt_stream_open(&p_cdc->stream.rx, p_cdc->daddr, desc_ep); + } else { + tu_edpt_stream_open(&p_cdc->stream.tx, p_cdc->daddr, desc_ep); + } + + desc_ep = (tusb_desc_endpoint_t const*) tu_desc_next(desc_ep); + } + + return true; +} + +bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { + (void) rhport; + + // For CDC: only support ACM subclass + // Note: Protocol 0xFF can be RNDIS device + if (TUSB_CLASS_CDC == itf_desc->bInterfaceClass && + CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass) { + return acm_open(daddr, itf_desc, max_len); + } + else if (SERIAL_DRIVER_COUNT > 1 && + TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass) { + uint16_t vid, pid; + TU_VERIFY(tuh_vid_pid_get(daddr, &vid, &pid)); + + for (size_t dr = 1; dr < SERIAL_DRIVER_COUNT; dr++) { + cdch_serial_driver_t const* driver = &serial_drivers[dr]; + for (size_t i = 0; i < driver->vid_pid_count; i++) { + if (driver->vid_pid_list[i][0] == vid && driver->vid_pid_list[i][1] == pid) { + return driver->open(daddr, itf_desc, max_len); + } + } + } + } + + return false; +} + +static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t itf_num) { + TU_LOG_DRV("CDCh Set Configure complete\r\n"); + p_cdc->mounted = true; + if (tuh_cdc_mount_cb) tuh_cdc_mount_cb(idx); + + // Prepare for incoming data + tu_edpt_stream_read_xfer(&p_cdc->stream.rx); + + // notify usbh that driver enumeration is complete + usbh_driver_set_config_complete(p_cdc->daddr, itf_num); +} + +bool cdch_set_config(uint8_t daddr, uint8_t itf_num) { + tusb_control_request_t request; + request.wIndex = tu_htole16((uint16_t) itf_num); + + // fake transfer to kick-off process + tuh_xfer_t xfer; + xfer.daddr = daddr; + xfer.result = XFER_RESULT_SUCCESS; + xfer.setup = &request; + xfer.user_data = 0; // initial state + + uint8_t const idx = tuh_cdc_itf_get_index(daddr, itf_num); + cdch_interface_t * p_cdc = get_itf(idx); + TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); + + serial_drivers[p_cdc->serial_drid].process_set_config(&xfer); + return true; +} + +//--------------------------------------------------------------------+ +// ACM +//--------------------------------------------------------------------+ + +enum { + CONFIG_ACM_SET_CONTROL_LINE_STATE = 0, + CONFIG_ACM_SET_LINE_CODING, + CONFIG_ACM_COMPLETE, +}; + +static bool acm_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len) { + uint8_t const* p_desc_end = ((uint8_t const*) itf_desc) + max_len; + + cdch_interface_t* p_cdc = make_new_itf(daddr, itf_desc); + TU_VERIFY(p_cdc); + p_cdc->serial_drid = SERIAL_DRIVER_ACM; + + //------------- Control Interface -------------// + uint8_t const* p_desc = tu_desc_next(itf_desc); + + // Communication Functional Descriptors + while ((p_desc < p_desc_end) && (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc))) { + if (CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc)) { + // save ACM bmCapabilities + p_cdc->acm_capability = ((cdc_desc_func_acm_t const*) p_desc)->bmCapabilities; + } + + p_desc = tu_desc_next(p_desc); + } + + // Open notification endpoint of control interface if any + if (itf_desc->bNumEndpoints == 1) { + TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)); + tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) p_desc; + + TU_ASSERT(tuh_edpt_open(daddr, desc_ep)); + p_cdc->ep_notif = desc_ep->bEndpointAddress; + + p_desc = tu_desc_next(p_desc); + } + + //------------- Data Interface (if any) -------------// + if ((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && + (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const*) p_desc)->bInterfaceClass)) { + // next to endpoint descriptor + p_desc = tu_desc_next(p_desc); + + // data endpoints expected to be in pairs + TU_ASSERT(open_ep_stream_pair(p_cdc, (tusb_desc_endpoint_t const*) p_desc)); + } + + return true; +} + +static void acm_process_config(tuh_xfer_t* xfer) { + uintptr_t const state = xfer->user_data; + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); + cdch_interface_t* p_cdc = get_itf(idx); + TU_ASSERT(p_cdc,); + + switch (state) { + case CONFIG_ACM_SET_CONTROL_LINE_STATE: + #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM + if (p_cdc->acm_capability.support_line_request) { + TU_ASSERT(acm_set_control_line_state(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, acm_process_config, CONFIG_ACM_SET_LINE_CODING),); + break; + } + #endif + TU_ATTR_FALLTHROUGH; + + case CONFIG_ACM_SET_LINE_CODING: + #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM + if (p_cdc->acm_capability.support_line_request) { + cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; + TU_ASSERT(acm_set_line_coding(p_cdc, &line_coding, acm_process_config, CONFIG_ACM_COMPLETE),); + break; + } + #endif + TU_ATTR_FALLTHROUGH; + + case CONFIG_ACM_COMPLETE: + // itf_num+1 to account for data interface as well + set_config_complete(p_cdc, idx, itf_num + 1); + break; + + default: + break; + } +} + +static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + TU_VERIFY(p_cdc->acm_capability.support_line_request); + TU_LOG_DRV("CDC ACM Set Control Line State\r\n"); + + tusb_control_request_t const request = { + .bmRequestType_bit = { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_OUT + }, + .bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE, + .wValue = tu_htole16(line_state), + .wIndex = tu_htole16((uint16_t) p_cdc->bInterfaceNumber), + .wLength = 0 + }; + + p_cdc->user_control_cb = complete_cb; + + tuh_xfer_t xfer = { + .daddr = p_cdc->daddr, + .ep_addr = 0, + .setup = &request, + .buffer = NULL, + .complete_cb = complete_cb ? cdch_internal_control_complete : NULL, // complete_cb is NULL for sync call + .user_data = user_data + }; + + TU_ASSERT(tuh_control_xfer(&xfer)); + return true; +} + +static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + TU_LOG_DRV("CDC ACM Set Line Conding\r\n"); + + tusb_control_request_t const request = { + .bmRequestType_bit = { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_OUT + }, + .bRequest = CDC_REQUEST_SET_LINE_CODING, + .wValue = 0, + .wIndex = tu_htole16(p_cdc->bInterfaceNumber), + .wLength = tu_htole16(sizeof(cdc_line_coding_t)) + }; + + // use usbh enum buf to hold line coding since user line_coding variable does not live long enough + uint8_t* enum_buf = usbh_get_enum_buf(); + memcpy(enum_buf, line_coding, sizeof(cdc_line_coding_t)); + + p_cdc->user_control_cb = complete_cb; + tuh_xfer_t xfer = { + .daddr = p_cdc->daddr, + .ep_addr = 0, + .setup = &request, + .buffer = enum_buf, + .complete_cb = complete_cb ? cdch_internal_control_complete : NULL, // complete_cb is NULL for sync call + .user_data = user_data + }; + + TU_ASSERT(tuh_control_xfer(&xfer)); + return true; +} + +static bool acm_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + TU_LOG_DRV("CDC ACM Set Data Format\r\n"); + + cdc_line_coding_t line_coding; + line_coding.bit_rate = p_cdc->line_coding.bit_rate; + line_coding.stop_bits = stop_bits; + line_coding.parity = parity; + line_coding.data_bits = data_bits; + + return acm_set_line_coding(p_cdc, &line_coding, complete_cb, user_data); +} + +static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + TU_VERIFY(p_cdc->acm_capability.support_line_request); + cdc_line_coding_t line_coding = p_cdc->line_coding; + line_coding.bit_rate = baudrate; + return acm_set_line_coding(p_cdc, &line_coding, complete_cb, user_data); +} + +//--------------------------------------------------------------------+ +// FTDI +//--------------------------------------------------------------------+ +#if CFG_TUH_CDC_FTDI + +enum { + CONFIG_FTDI_RESET = 0, + CONFIG_FTDI_MODEM_CTRL, + CONFIG_FTDI_SET_BAUDRATE, + CONFIG_FTDI_SET_DATA, + CONFIG_FTDI_COMPLETE +}; + +static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) { + // FTDI Interface includes 1 vendor interface + 2 bulk endpoints + TU_VERIFY(itf_desc->bInterfaceSubClass == 0xff && itf_desc->bInterfaceProtocol == 0xff && itf_desc->bNumEndpoints == 2); + TU_VERIFY(sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t) <= max_len); + + cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc); + TU_VERIFY(p_cdc); + + TU_LOG_DRV("FTDI opened\r\n"); + p_cdc->serial_drid = SERIAL_DRIVER_FTDI; + + // endpoint pair + tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); + + // data endpoints expected to be in pairs + return open_ep_stream_pair(p_cdc, desc_ep); +} + +// set request without data +static bool ftdi_sio_set_request(cdch_interface_t* p_cdc, uint8_t command, uint16_t value, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + tusb_control_request_t const request = { + .bmRequestType_bit = { + .recipient = TUSB_REQ_RCPT_DEVICE, + .type = TUSB_REQ_TYPE_VENDOR, + .direction = TUSB_DIR_OUT + }, + .bRequest = command, + .wValue = tu_htole16(value), + .wIndex = 0, + .wLength = 0 + }; + + tuh_xfer_t xfer = { + .daddr = p_cdc->daddr, + .ep_addr = 0, + .setup = &request, + .buffer = NULL, + .complete_cb = complete_cb, + .user_data = user_data + }; + + return tuh_control_xfer(&xfer); +} + +static bool ftdi_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + return ftdi_sio_set_request(p_cdc, FTDI_SIO_RESET, FTDI_SIO_RESET_SIO, complete_cb, user_data); +} + +static bool ftdi_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + (void) p_cdc; + (void) stop_bits; + (void) parity; + (void) data_bits; + (void) complete_cb; + (void) user_data; + // TODO not implemented yet + return false; +} + +static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + (void) p_cdc; + (void) line_coding; + (void) complete_cb; + (void) user_data; + // TODO not implemented yet + return false; +} + +static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + TU_LOG_DRV("CDC FTDI Set Control Line State\r\n"); + p_cdc->user_control_cb = complete_cb; + TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_MODEM_CTRL, 0x0300 | line_state, + complete_cb ? cdch_internal_control_complete : NULL, user_data)); + return true; +} + +static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, uint32_t base) { + const uint8_t divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 }; + uint32_t divisor; + + /* divisor shifted 3 bits to the left */ + uint32_t divisor3 = base / (2 * baud); + divisor = (divisor3 >> 3); + divisor |= (uint32_t) divfrac[divisor3 & 0x7] << 14; + + /* Deal with special cases for highest baud rates. */ + if (divisor == 1) { /* 1.0 */ + divisor = 0; + } + else if (divisor == 0x4001) { /* 1.5 */ + divisor = 1; + } + + return divisor; +} + +static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud) { + return ftdi_232bm_baud_base_to_divisor(baud, 48000000u); +} + +static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + uint16_t const divisor = (uint16_t) ftdi_232bm_baud_to_divisor(baudrate); + TU_LOG_DRV("CDC FTDI Set BaudRate = %" PRIu32 ", divisor = 0x%04x\r\n", baudrate, divisor); + + p_cdc->user_control_cb = complete_cb; + p_cdc->requested_line_coding.bit_rate = baudrate; + TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_SET_BAUD_RATE, divisor, + complete_cb ? cdch_internal_control_complete : NULL, user_data)); + + return true; +} + +static void ftdi_process_config(tuh_xfer_t* xfer) { + uintptr_t const state = xfer->user_data; + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); + cdch_interface_t * p_cdc = get_itf(idx); + TU_ASSERT(p_cdc, ); + + switch(state) { + // Note may need to read FTDI eeprom + case CONFIG_FTDI_RESET: + TU_ASSERT(ftdi_sio_reset(p_cdc, ftdi_process_config, CONFIG_FTDI_MODEM_CTRL),); + break; + + case CONFIG_FTDI_MODEM_CTRL: + #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM + TU_ASSERT(ftdi_sio_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, ftdi_process_config, CONFIG_FTDI_SET_BAUDRATE),); + break; + #else + TU_ATTR_FALLTHROUGH; + #endif + + case CONFIG_FTDI_SET_BAUDRATE: { + #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM + cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; + TU_ASSERT(ftdi_sio_set_baudrate(p_cdc, line_coding.bit_rate, ftdi_process_config, CONFIG_FTDI_SET_DATA),); + break; + #else + TU_ATTR_FALLTHROUGH; + #endif + } + + case CONFIG_FTDI_SET_DATA: { + #if 0 // TODO set data format + #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM + cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; + TU_ASSERT(ftdi_sio_set_data(p_cdc, process_ftdi_config, CONFIG_FTDI_COMPLETE),); + break; + #endif + #endif + + TU_ATTR_FALLTHROUGH; + } + + case CONFIG_FTDI_COMPLETE: + set_config_complete(p_cdc, idx, itf_num); + break; + + default: + break; + } +} + +#endif + +//--------------------------------------------------------------------+ +// CP210x +//--------------------------------------------------------------------+ + +#if CFG_TUH_CDC_CP210X + +enum { + CONFIG_CP210X_IFC_ENABLE = 0, + CONFIG_CP210X_SET_BAUDRATE, + CONFIG_CP210X_SET_LINE_CTL, + CONFIG_CP210X_SET_DTR_RTS, + CONFIG_CP210X_COMPLETE +}; + +static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { + // CP210x Interface includes 1 vendor interface + 2 bulk endpoints + TU_VERIFY(itf_desc->bInterfaceSubClass == 0 && itf_desc->bInterfaceProtocol == 0 && itf_desc->bNumEndpoints == 2); + TU_VERIFY(sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t) <= max_len); + + cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc); + TU_VERIFY(p_cdc); + + TU_LOG_DRV("CP210x opened\r\n"); + p_cdc->serial_drid = SERIAL_DRIVER_CP210X; + + // endpoint pair + tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); + + // data endpoints expected to be in pairs + return open_ep_stream_pair(p_cdc, desc_ep); +} + +static bool cp210x_set_request(cdch_interface_t* p_cdc, uint8_t command, uint16_t value, uint8_t* buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + tusb_control_request_t const request = { + .bmRequestType_bit = { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_VENDOR, + .direction = TUSB_DIR_OUT + }, + .bRequest = command, + .wValue = tu_htole16(value), + .wIndex = p_cdc->bInterfaceNumber, + .wLength = tu_htole16(length) + }; + + // use usbh enum buf since application variable does not live long enough + uint8_t* enum_buf = NULL; + + if (buffer && length > 0) { + enum_buf = usbh_get_enum_buf(); + tu_memcpy_s(enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length); + } + + tuh_xfer_t xfer = { + .daddr = p_cdc->daddr, + .ep_addr = 0, + .setup = &request, + .buffer = enum_buf, + .complete_cb = complete_cb, + .user_data = user_data + }; + + return tuh_control_xfer(&xfer); +} + +static bool cp210x_ifc_enable(cdch_interface_t* p_cdc, uint16_t enabled, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + return cp210x_set_request(p_cdc, CP210X_IFC_ENABLE, enabled, NULL, 0, complete_cb, user_data); +} + +static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + // TODO implement later + (void) p_cdc; + (void) line_coding; + (void) complete_cb; + (void) user_data; + return false; +} + +static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + TU_LOG_DRV("CDC CP210x Set BaudRate = %" PRIu32 "\r\n", baudrate); + uint32_t baud_le = tu_htole32(baudrate); + p_cdc->user_control_cb = complete_cb; + return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baud_le, 4, + complete_cb ? cdch_internal_control_complete : NULL, user_data); +} + +static bool cp210x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + (void) p_cdc; + (void) stop_bits; + (void) parity; + (void) data_bits; + (void) complete_cb; + (void) user_data; + // TODO not implemented yet + return false; +} + +static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + TU_LOG_DRV("CDC CP210x Set Control Line State\r\n"); + p_cdc->user_control_cb = complete_cb; + return cp210x_set_request(p_cdc, CP210X_SET_MHS, 0x0300 | line_state, NULL, 0, + complete_cb ? cdch_internal_control_complete : NULL, user_data); +} + +static void cp210x_process_config(tuh_xfer_t* xfer) { + uintptr_t const state = xfer->user_data; + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); + cdch_interface_t *p_cdc = get_itf(idx); + TU_ASSERT(p_cdc,); + + switch (state) { + case CONFIG_CP210X_IFC_ENABLE: + TU_ASSERT(cp210x_ifc_enable(p_cdc, 1, cp210x_process_config, CONFIG_CP210X_SET_BAUDRATE),); + break; + + case CONFIG_CP210X_SET_BAUDRATE: { + #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM + cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; + TU_ASSERT(cp210x_set_baudrate(p_cdc, line_coding.bit_rate, cp210x_process_config, CONFIG_CP210X_SET_LINE_CTL),); + break; + #else + TU_ATTR_FALLTHROUGH; + #endif + } + + case CONFIG_CP210X_SET_LINE_CTL: { + #if defined(CFG_TUH_CDC_LINE_CODING_ON_ENUM) && 0 // skip for now + cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; + break; + #else + TU_ATTR_FALLTHROUGH; + #endif + } + + case CONFIG_CP210X_SET_DTR_RTS: + #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM + TU_ASSERT(cp210x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, cp210x_process_config, CONFIG_CP210X_COMPLETE),); + break; + #else + TU_ATTR_FALLTHROUGH; + #endif + + case CONFIG_CP210X_COMPLETE: + set_config_complete(p_cdc, idx, itf_num); + break; + + default: break; + } +} + +#endif + +//--------------------------------------------------------------------+ +// CH34x (CH340 & CH341) +//--------------------------------------------------------------------+ + +#if CFG_TUH_CDC_CH34X + +static uint8_t ch34x_get_lcr(uint8_t stop_bits, uint8_t parity, uint8_t data_bits); +static uint16_t ch34x_get_divisor_prescaler(uint32_t baval); + +//------------- control request -------------// + +static bool ch34x_set_request(cdch_interface_t* p_cdc, uint8_t direction, uint8_t request, uint16_t value, + uint16_t index, uint8_t* buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + tusb_control_request_t const request_setup = { + .bmRequestType_bit = { + .recipient = TUSB_REQ_RCPT_DEVICE, + .type = TUSB_REQ_TYPE_VENDOR, + .direction = direction & 0x01u + }, + .bRequest = request, + .wValue = tu_htole16 (value), + .wIndex = tu_htole16 (index), + .wLength = tu_htole16 (length) + }; + + // use usbh enum buf since application variable does not live long enough + uint8_t* enum_buf = NULL; + + if (buffer && length > 0) { + enum_buf = usbh_get_enum_buf(); + if (direction == TUSB_DIR_OUT) { + tu_memcpy_s(enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length); + } + } + + tuh_xfer_t xfer = { + .daddr = p_cdc->daddr, + .ep_addr = 0, + .setup = &request_setup, + .buffer = enum_buf, + .complete_cb = complete_cb, + .user_data = user_data + }; + + return tuh_control_xfer(&xfer); +} + +static inline bool ch34x_control_out(cdch_interface_t* p_cdc, uint8_t request, uint16_t value, uint16_t index, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + return ch34x_set_request(p_cdc, TUSB_DIR_OUT, request, value, index, NULL, 0, complete_cb, user_data); +} + +static inline bool ch34x_control_in(cdch_interface_t* p_cdc, uint8_t request, uint16_t value, uint16_t index, + uint8_t* buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + return ch34x_set_request(p_cdc, TUSB_DIR_IN, request, value, index, buffer, buffersize, + complete_cb, user_data); +} + +static inline bool ch34x_write_reg(cdch_interface_t* p_cdc, uint16_t reg, uint16_t reg_value, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + return ch34x_control_out(p_cdc, CH34X_REQ_WRITE_REG, reg, reg_value, complete_cb, user_data); +} + +//static bool ch34x_read_reg_request ( cdch_interface_t* p_cdc, uint16_t reg, +// uint8_t *buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data ) +//{ +// return ch34x_control_in ( p_cdc, CH34X_REQ_READ_REG, reg, 0, buffer, buffersize, complete_cb, user_data ); +//} + +static bool ch34x_write_reg_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + uint16_t const div_ps = ch34x_get_divisor_prescaler(baudrate); + TU_VERIFY(div_ps); + TU_ASSERT(ch34x_write_reg(p_cdc, CH34X_REG16_DIVISOR_PRESCALER, div_ps, + complete_cb, user_data)); + return true; +} + +//------------- Driver API -------------// + +// internal control complete to update state such as line state, encoding +static void ch34x_control_complete(tuh_xfer_t* xfer) { + // CH34x only has 1 interface and use wIndex as payload and not for bInterfaceNumber + process_internal_control_complete(xfer, 0); +} + +static bool ch34x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + p_cdc->requested_line_coding.stop_bits = stop_bits; + p_cdc->requested_line_coding.parity = parity; + p_cdc->requested_line_coding.data_bits = data_bits; + + uint8_t const lcr = ch34x_get_lcr(stop_bits, parity, data_bits); + TU_VERIFY(lcr); + TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_WRITE_REG, CH32X_REG16_LCR2_LCR, lcr, + complete_cb ? ch34x_control_complete : NULL, user_data)); + return true; +} + +static bool ch34x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + p_cdc->requested_line_coding.bit_rate = baudrate; + p_cdc->user_control_cb = complete_cb; + TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, baudrate, + complete_cb ? ch34x_control_complete : NULL, user_data)); + return true; +} + +static void ch34x_set_line_coding_stage1_complete(tuh_xfer_t* xfer) { + // CH34x only has 1 interface and use wIndex as payload and not for bInterfaceNumber + uint8_t const itf_num = 0; + uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); + cdch_interface_t* p_cdc = get_itf(idx); + TU_ASSERT(p_cdc, ); + + if (xfer->result == XFER_RESULT_SUCCESS) { + // stage 1 success, continue to stage 2 + p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate; + TU_ASSERT(ch34x_set_data_format(p_cdc, p_cdc->requested_line_coding.stop_bits, p_cdc->requested_line_coding.parity, + p_cdc->requested_line_coding.data_bits, ch34x_control_complete, xfer->user_data), ); + } else { + // stage 1 failed, notify user + xfer->complete_cb = p_cdc->user_control_cb; + if (xfer->complete_cb) { + xfer->complete_cb(xfer); + } + } +} + +// 2 stages: set baudrate (stage1) + set data format (stage2) +static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + p_cdc->requested_line_coding = *line_coding; + p_cdc->user_control_cb = complete_cb; + + if (complete_cb) { + // stage 1 set baudrate + TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, line_coding->bit_rate, + ch34x_set_line_coding_stage1_complete, user_data)); + } else { + // sync call + xfer_result_t result; + + // stage 1 set baudrate + TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, line_coding->bit_rate, NULL, (uintptr_t) &result)); + TU_VERIFY(result == XFER_RESULT_SUCCESS); + p_cdc->line_coding.bit_rate = line_coding->bit_rate; + + // stage 2 set data format + TU_ASSERT(ch34x_set_data_format(p_cdc, line_coding->stop_bits, line_coding->parity, line_coding->data_bits, + NULL, (uintptr_t) &result)); + TU_VERIFY(result == XFER_RESULT_SUCCESS); + p_cdc->line_coding.stop_bits = line_coding->stop_bits; + p_cdc->line_coding.parity = line_coding->parity; + p_cdc->line_coding.data_bits = line_coding->data_bits; + + // update transfer result, user_data is expected to point to xfer_result_t + if (user_data) { + *((xfer_result_t*) user_data) = result; + } + } + + return true; +} + +static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + uint8_t control = 0; + if (line_state & CDC_CONTROL_LINE_STATE_RTS) { + control |= CH34X_BIT_RTS; + } + if (line_state & CDC_CONTROL_LINE_STATE_DTR) { + control |= CH34X_BIT_DTR; + } + + // CH34x signals are inverted + control = ~control; + + p_cdc->user_control_cb = complete_cb; + TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_MODEM_CTRL, control, 0, + complete_cb ? ch34x_control_complete : NULL, user_data)); + return true; +} + +//------------- Enumeration -------------// +enum { + CONFIG_CH34X_READ_VERSION = 0, + CONFIG_CH34X_SERIAL_INIT, + CONFIG_CH34X_SPECIAL_REG_WRITE, + CONFIG_CH34X_FLOW_CONTROL, + CONFIG_CH34X_MODEM_CONTROL, + CONFIG_CH34X_COMPLETE +}; + +static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len) { + // CH34x Interface includes 1 vendor interface + 2 bulk + 1 interrupt endpoints + TU_VERIFY (itf_desc->bNumEndpoints == 3); + TU_VERIFY (sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t) <= max_len); + + cdch_interface_t* p_cdc = make_new_itf(daddr, itf_desc); + TU_VERIFY (p_cdc); + + TU_LOG_DRV ("CH34x opened\r\n"); + p_cdc->serial_drid = SERIAL_DRIVER_CH34X; + + tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) tu_desc_next(itf_desc); + + // data endpoints expected to be in pairs + TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep)); + desc_ep += 2; + + // Interrupt endpoint: not used for now + TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(desc_ep) && + TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer); + TU_ASSERT(tuh_edpt_open(daddr, desc_ep)); + p_cdc->ep_notif = desc_ep->bEndpointAddress; + + return true; +} + +static void ch34x_process_config(tuh_xfer_t* xfer) { + // CH34x only has 1 interface and use wIndex as payload and not for bInterfaceNumber + uint8_t const itf_num = 0; + uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); + cdch_interface_t* p_cdc = get_itf(idx); + uintptr_t const state = xfer->user_data; + uint8_t buffer[2]; // TODO remove + TU_ASSERT (p_cdc,); + TU_ASSERT (xfer->result == XFER_RESULT_SUCCESS,); + + switch (state) { + case CONFIG_CH34X_READ_VERSION: + TU_LOG_DRV("[%u] CDCh CH34x attempt to read Chip Version\r\n", p_cdc->daddr); + TU_ASSERT (ch34x_control_in(p_cdc, CH34X_REQ_READ_VERSION, 0, 0, buffer, 2, ch34x_process_config, CONFIG_CH34X_SERIAL_INIT),); + break; + + case CONFIG_CH34X_SERIAL_INIT: { + // handle version read data, set CH34x line coding (incl. baudrate) + uint8_t const version = xfer->buffer[0]; + TU_LOG_DRV("[%u] CDCh CH34x Chip Version = %02x\r\n", p_cdc->daddr, version); + // only versions >= 0x30 are tested, below 0x30 seems having other programming, see drivers from WCH vendor, Linux kernel and FreeBSD + TU_ASSERT (version >= 0x30,); + // init CH34x with line coding + cdc_line_coding_t const line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X; + uint16_t const div_ps = ch34x_get_divisor_prescaler(line_coding.bit_rate); + TU_ASSERT(div_ps, ); + uint8_t const lcr = ch34x_get_lcr(line_coding.stop_bits, line_coding.parity, line_coding.data_bits); + TU_ASSERT(lcr, ); + TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_SERIAL_INIT, tu_u16(lcr, 0x9c), div_ps, + ch34x_process_config, CONFIG_CH34X_SPECIAL_REG_WRITE),); + break; + } + + case CONFIG_CH34X_SPECIAL_REG_WRITE: + // overtake line coding and do special reg write, purpose unknown, overtaken from WCH driver + p_cdc->line_coding = ((cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X); + TU_ASSERT (ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x0F, CH341_REG_0x2C), 0x0007, ch34x_process_config, CONFIG_CH34X_FLOW_CONTROL),); + break; + + case CONFIG_CH34X_FLOW_CONTROL: + // no hardware flow control + TU_ASSERT (ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x27, CH341_REG_0x27), 0x0000, ch34x_process_config, CONFIG_CH34X_MODEM_CONTROL),); + break; + + case CONFIG_CH34X_MODEM_CONTROL: + // !always! set modem controls RTS/DTR (CH34x has no reset state after CH34X_REQ_SERIAL_INIT) + TU_ASSERT (ch34x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, ch34x_process_config, CONFIG_CH34X_COMPLETE),); + break; + + case CONFIG_CH34X_COMPLETE: + set_config_complete(p_cdc, idx, itf_num); + break; + + default: + TU_ASSERT (false,); + break; + } +} + +//------------- CH34x helper -------------// + +// calculate divisor and prescaler for baudrate, return it as 16-bit combined value +static uint16_t ch34x_get_divisor_prescaler(uint32_t baval) { + uint8_t a; + uint8_t b; + uint32_t c; + + TU_VERIFY(baval != 0 && baval <= 2000000, 0); + switch (baval) { + case 921600: + a = 0xf3; + b = 7; + break; + + case 307200: + a = 0xd9; + b = 7; + break; + + default: + if (baval > 6000000 / 255) { + b = 3; + c = 6000000; + } else if (baval > 750000 / 255) { + b = 2; + c = 750000; + } else if (baval > 93750 / 255) { + b = 1; + c = 93750; + } else { + b = 0; + c = 11719; + } + a = (uint8_t) (c / baval); + if (a == 0 || a == 0xFF) { + return 0; + } + if ((c / a - baval) > (baval - c / (a + 1))) { + a++; + } + a = (uint8_t) (256 - a); + break; + } + + // reg divisor = a, reg prescaler = b + // According to linux code we need to set bit 7 of UCHCOM_REG_BPS_PRE, + // otherwise the chip will buffer data. + return (uint16_t) ((uint16_t)a << 8 | 0x80 | b); +} + +// calculate lcr value from data coding +static uint8_t ch34x_get_lcr(uint8_t stop_bits, uint8_t parity, uint8_t data_bits) { + uint8_t lcr = CH34X_LCR_ENABLE_RX | CH34X_LCR_ENABLE_TX; + TU_VERIFY(data_bits >= 5 && data_bits <= 8, 0); + lcr |= (uint8_t) (data_bits - 5); + + switch(parity) { + case CDC_LINE_CODING_PARITY_NONE: + break; + + case CDC_LINE_CODING_PARITY_ODD: + lcr |= CH34X_LCR_ENABLE_PAR; + break; + + case CDC_LINE_CODING_PARITY_EVEN: + lcr |= CH34X_LCR_ENABLE_PAR | CH34X_LCR_PAR_EVEN; + break; + + case CDC_LINE_CODING_PARITY_MARK: + lcr |= CH34X_LCR_ENABLE_PAR | CH34X_LCR_MARK_SPACE; + break; + + case CDC_LINE_CODING_PARITY_SPACE: + lcr |= CH34X_LCR_ENABLE_PAR | CH34X_LCR_MARK_SPACE | CH34X_LCR_PAR_EVEN; + break; + + default: break; + } + + // 1.5 stop bits not supported + TU_VERIFY(stop_bits != CDC_LINE_CODING_STOP_BITS_1_5, 0); + if (stop_bits == CDC_LINE_CODING_STOP_BITS_2) { + lcr |= CH34X_LCR_STOP_BITS_2; + } + + return lcr; +} + + +#endif // CFG_TUH_CDC_CH34X + +#endif diff --git a/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc_host.h b/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc_host.h new file mode 100644 index 00000000..b63dd153 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc_host.h @@ -0,0 +1,206 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_CDC_HOST_H_ +#define _TUSB_CDC_HOST_H_ + +#include "cdc.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Class Driver Configuration +//--------------------------------------------------------------------+ + +// Set Line Control state on enumeration/mounted: DTR ( bit 0), RTS (bit 1) +#ifndef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM +#define CFG_TUH_CDC_LINE_CONTROL_ON_ENUM 0 +#endif + +// Set Line Coding on enumeration/mounted, value for cdc_line_coding_t +//#ifndef CFG_TUH_CDC_LINE_CODING_ON_ENUM +//#define CFG_TUH_CDC_LINE_CODING_ON_ENUM { 115200, CDC_LINE_CODING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 } +//#endif + +// RX FIFO size +#ifndef CFG_TUH_CDC_RX_BUFSIZE +#define CFG_TUH_CDC_RX_BUFSIZE USBH_EPSIZE_BULK_MAX +#endif + +// RX Endpoint size +#ifndef CFG_TUH_CDC_RX_EPSIZE +#define CFG_TUH_CDC_RX_EPSIZE USBH_EPSIZE_BULK_MAX +#endif + +// TX FIFO size +#ifndef CFG_TUH_CDC_TX_BUFSIZE +#define CFG_TUH_CDC_TX_BUFSIZE USBH_EPSIZE_BULK_MAX +#endif + +// TX Endpoint size +#ifndef CFG_TUH_CDC_TX_EPSIZE +#define CFG_TUH_CDC_TX_EPSIZE USBH_EPSIZE_BULK_MAX +#endif + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ + +// Get Interface index from device address + interface number +// return TUSB_INDEX_INVALID_8 (0xFF) if not found +uint8_t tuh_cdc_itf_get_index(uint8_t daddr, uint8_t itf_num); + +// Get Interface information +// return true if index is correct and interface is currently mounted +bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info); + +// Check if a interface is mounted +bool tuh_cdc_mounted(uint8_t idx); + +// Get current DTR status +bool tuh_cdc_get_dtr(uint8_t idx); + +// Get current RTS status +bool tuh_cdc_get_rts(uint8_t idx); + +// Check if interface is connected (DTR active) +TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_connected(uint8_t idx) +{ + return tuh_cdc_get_dtr(idx); +} + +// Get local (saved/cached) version of line coding. +// This function should return correct values if tuh_cdc_set_line_coding() / tuh_cdc_get_line_coding() +// are invoked previously or CFG_TUH_CDC_LINE_CODING_ON_ENUM is defined. +// NOTE: This function does not make any USB transfer request to device. +bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding); + +//--------------------------------------------------------------------+ +// Write API +//--------------------------------------------------------------------+ + +// Get the number of bytes available for writing +uint32_t tuh_cdc_write_available(uint8_t idx); + +// Write to cdc interface +uint32_t tuh_cdc_write(uint8_t idx, void const* buffer, uint32_t bufsize); + +// Force sending data if possible, return number of forced bytes +uint32_t tuh_cdc_write_flush(uint8_t idx); + +// Clear the transmit FIFO +bool tuh_cdc_write_clear(uint8_t idx); + +//--------------------------------------------------------------------+ +// Read API +//--------------------------------------------------------------------+ + +// Get the number of bytes available for reading +uint32_t tuh_cdc_read_available(uint8_t idx); + +// Read from cdc interface +uint32_t tuh_cdc_read (uint8_t idx, void* buffer, uint32_t bufsize); + +// Get a byte from RX FIFO without removing it +bool tuh_cdc_peek(uint8_t idx, uint8_t* ch); + +// Clear the received FIFO +bool tuh_cdc_read_clear (uint8_t idx); + +//--------------------------------------------------------------------+ +// Control Endpoint (Request) API +// Each Function will make a USB control transfer request to/from device +// - If complete_cb is provided, the function will return immediately and invoke +// the callback when request is complete. +// - If complete_cb is NULL, the function will block until request is complete. +// - In this case, user_data should be pointed to xfer_result_t to hold the transfer result. +// - The function will return true if transfer is successful, false otherwise. +//--------------------------------------------------------------------+ + +// Request to Set Control Line State: DTR (bit 0), RTS (bit 1) +bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + +// Request to set baudrate +bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + +// Request to set data format +bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + +// Request to Set Line Coding = baudrate + data format +// Note: only implemented by ACM and CH34x, not supported by FTDI and CP210x yet +bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + +// Request to Get Line Coding (ACM only) +// Should only use if tuh_cdc_set_line_coding() / tuh_cdc_get_line_coding() never got invoked and +// CFG_TUH_CDC_LINE_CODING_ON_ENUM is not defined +// bool tuh_cdc_get_line_coding(uint8_t idx, cdc_line_coding_t* coding); + +// Connect by set both DTR, RTS +TU_ATTR_ALWAYS_INLINE static inline +bool tuh_cdc_connect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + return tuh_cdc_set_control_line_state(idx, CDC_CONTROL_LINE_STATE_DTR | CDC_CONTROL_LINE_STATE_RTS, complete_cb, user_data); +} + +// Disconnect by clear both DTR, RTS +TU_ATTR_ALWAYS_INLINE static inline +bool tuh_cdc_disconnect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + return tuh_cdc_set_control_line_state(idx, 0x00, complete_cb, user_data); +} + +//--------------------------------------------------------------------+ +// CDC APPLICATION CALLBACKS +//--------------------------------------------------------------------+ + +// Invoked when a device with CDC interface is mounted +// idx is index of cdc interface in the internal pool. +TU_ATTR_WEAK extern void tuh_cdc_mount_cb(uint8_t idx); + +// Invoked when a device with CDC interface is unmounted +TU_ATTR_WEAK extern void tuh_cdc_umount_cb(uint8_t idx); + +// Invoked when received new data +TU_ATTR_WEAK extern void tuh_cdc_rx_cb(uint8_t idx); + +// Invoked when a TX is complete and therefore space becomes available in TX buffer +TU_ATTR_WEAK extern void tuh_cdc_tx_complete_cb(uint8_t idx); + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +bool cdch_init (void); +bool cdch_deinit (void); +bool cdch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len); +bool cdch_set_config (uint8_t dev_addr, uint8_t itf_num); +bool cdch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); +void cdch_close (uint8_t dev_addr); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CDC_HOST_H_ */ diff --git a/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc_rndis.h b/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc_rndis.h new file mode 100644 index 00000000..ad153e0a --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc_rndis.h @@ -0,0 +1,301 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +/** \ingroup ClassDriver_CDC Communication Device Class (CDC) + * \defgroup CDC_RNDIS Remote Network Driver Interface Specification (RNDIS) + * @{ + * \defgroup CDC_RNDIS_Common Common Definitions + * @{ */ + +#ifndef _TUSB_CDC_RNDIS_H_ +#define _TUSB_CDC_RNDIS_H_ + +#include "cdc.h" + +#ifdef __cplusplus + extern "C" { +#endif + +#ifdef __CC_ARM +#pragma diag_suppress 66 // Suppress Keil warnings #66-D: enumeration value is out of "int" range +#endif + +/// RNDIS Message Types +typedef enum +{ + RNDIS_MSG_PACKET = 0x00000001UL, ///< The host and device use this to send network data to one another. + + RNDIS_MSG_INITIALIZE = 0x00000002UL, ///< Sent by the host to initialize the device. + RNDIS_MSG_INITIALIZE_CMPLT = 0x80000002UL, ///< Device response to an initialize message. + + RNDIS_MSG_HALT = 0x00000003UL, ///< Sent by the host to halt the device. This does not have a response. It is optional for the device to send this message to the host. + + RNDIS_MSG_QUERY = 0x00000004UL, ///< Sent by the host to send a query OID. + RNDIS_MSG_QUERY_CMPLT = 0x80000004UL, ///< Device response to a query OID. + + RNDIS_MSG_SET = 0x00000005UL, ///< Sent by the host to send a set OID. + RNDIS_MSG_SET_CMPLT = 0x80000005UL, ///< Device response to a set OID. + + RNDIS_MSG_RESET = 0x00000006UL, ///< Sent by the host to perform a soft reset on the device. + RNDIS_MSG_RESET_CMPLT = 0x80000006UL, ///< Device response to reset message. + + RNDIS_MSG_INDICATE_STATUS = 0x00000007UL, ///< Sent by the device to indicate its status or an error when an unrecognized message is received. + + RNDIS_MSG_KEEP_ALIVE = 0x00000008UL, ///< During idle periods, sent every few seconds by the host to check that the device is still responsive. It is optional for the device to send this message to check if the host is active. + RNDIS_MSG_KEEP_ALIVE_CMPLT = 0x80000008UL ///< The device response to a keepalivemessage. The host can respond with this message to a keepalive message from the device when the device implements the optional KeepAliveTimer. +}rndis_msg_type_t; + +/// RNDIS Message Status Values +typedef enum +{ + RNDIS_STATUS_SUCCESS = 0x00000000UL, ///< Success + RNDIS_STATUS_FAILURE = 0xC0000001UL, ///< Unspecified error + RNDIS_STATUS_INVALID_DATA = 0xC0010015UL, ///< Invalid data error + RNDIS_STATUS_NOT_SUPPORTED = 0xC00000BBUL, ///< Unsupported request error + RNDIS_STATUS_MEDIA_CONNECT = 0x4001000BUL, ///< Device is connected to a network medium. + RNDIS_STATUS_MEDIA_DISCONNECT = 0x4001000CUL ///< Device is disconnected from the medium. +}rndis_msg_status_t; + +#ifdef __CC_ARM +#pragma diag_default 66 // return Keil 66 to normal severity +#endif + +//--------------------------------------------------------------------+ +// MESSAGE STRUCTURE +//--------------------------------------------------------------------+ + +//------------- Initialize -------------// +/// \brief Initialize Message +/// \details This message MUST be sent by the host to initialize the device. +typedef struct { + uint32_t type ; ///< Message type, must be \ref RNDIS_MSG_INITIALIZE + uint32_t length ; ///< Message length in bytes, must be 0x18 + uint32_t request_id ; ///< A 32-bit integer value, generated by the host, used to match the host's sent request to the response from the device. + uint32_t major_version ; ///< The major version of the RNDIS Protocol implemented by the host. + uint32_t minor_version ; ///< The minor version of the RNDIS Protocol implemented by the host + uint32_t max_xfer_size ; ///< The maximum size, in bytes, of any single bus data transfer that the host expects to receive from the device. +}rndis_msg_initialize_t; + +/// \brief Initialize Complete Message +/// \details This message MUST be sent by the device in response to an initialize message. +typedef struct { + uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_INITIALIZE_CMPLT + uint32_t length ; ///< Message length in bytes, must be 0x30 + uint32_t request_id ; ///< A 32-bit integer value from \a request_id field of the \ref rndis_msg_initialize_t to which this message is a response. + uint32_t status ; ///< The initialization status of the device, has value from \ref rndis_msg_status_t + uint32_t major_version ; ///< the highest-numbered RNDIS Protocol version supported by the device. + uint32_t minor_version ; ///< the highest-numbered RNDIS Protocol version supported by the device. + uint32_t device_flags ; ///< MUST be set to 0x000000010. Other values are reserved for future use. + uint32_t medium ; ///< is 0x00 for RNDIS_MEDIUM_802_3 + uint32_t max_packet_per_xfer ; ///< The maximum number of concatenated \ref RNDIS_MSG_PACKET messages that the device can handle in a single bus transfer to it. This value MUST be at least 1. + uint32_t max_xfer_size ; ///< The maximum size, in bytes, of any single bus data transfer that the device expects to receive from the host. + uint32_t packet_alignment_factor ; ///< The byte alignment the device expects for each RNDIS message that is part of a multimessage transfer to it. The value is specified as an exponent of 2; for example, the host uses 2{PacketAlignmentFactor} as the alignment value. + uint32_t reserved[2] ; +} rndis_msg_initialize_cmplt_t; + +//------------- Query -------------// +/// \brief Query Message +/// \details This message MUST be sent by the host to query an OID. +typedef struct { + uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_QUERY + uint32_t length ; ///< Message length in bytes, including the header and the \a oid_buffer + uint32_t request_id ; ///< A 32-bit integer value, generated by the host, used to match the host's sent request to the response from the device. + uint32_t oid ; ///< The integer value of the host operating system-defined identifier, for the parameter of the device being queried for. + uint32_t buffer_length ; ///< The length, in bytes, of the input data required for the OID query. This MUST be set to 0 when there is no input data associated with the OID. + uint32_t buffer_offset ; ///< The offset, in bytes, from the beginning of \a request_id field where the input data for the query is located in the message. This value MUST be set to 0 when there is no input data associated with the OID. + uint32_t reserved ; + uint8_t oid_buffer[] ; ///< Flexible array contains the input data supplied by the host, required for the OID query request processing by the device, as per the host NDIS specification. +} rndis_msg_query_t, rndis_msg_set_t; + +TU_VERIFY_STATIC(sizeof(rndis_msg_query_t) == 28, "Make sure flexible array member does not affect layout"); + +/// \brief Query Complete Message +/// \details This message MUST be sent by the device in response to a query OID message. +typedef struct { + uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_QUERY_CMPLT + uint32_t length ; ///< Message length in bytes, including the header and the \a oid_buffer + uint32_t request_id ; ///< A 32-bit integer value from \a request_id field of the \ref rndis_msg_query_t to which this message is a response. + uint32_t status ; ///< The status of processing for the query request, has value from \ref rndis_msg_status_t. + uint32_t buffer_length ; ///< The length, in bytes, of the data in the response to the query. This MUST be set to 0 when there is no OIDInputBuffer. + uint32_t buffer_offset ; ///< The offset, in bytes, from the beginning of \a request_id field where the response data for the query is located in the message. This MUST be set to 0 when there is no \ref oid_buffer. + uint8_t oid_buffer[] ; ///< Flexible array member contains the response data to the OID query request as specified by the host. +} rndis_msg_query_cmplt_t; + +TU_VERIFY_STATIC(sizeof(rndis_msg_query_cmplt_t) == 24, "Make sure flexible array member does not affect layout"); + +//------------- Reset -------------// +/// \brief Reset Message +/// \details This message MUST be sent by the host to perform a soft reset on the device. +typedef struct { + uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_RESET + uint32_t length ; ///< Message length in bytes, MUST be 0x06 + uint32_t reserved ; +} rndis_msg_reset_t; + +/// \brief Reset Complete Message +/// \details This message MUST be sent by the device in response to a reset message. +typedef struct { + uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_RESET_CMPLT + uint32_t length ; ///< Message length in bytes, MUST be 0x10 + uint32_t status ; ///< The status of processing for the \ref rndis_msg_reset_t, has value from \ref rndis_msg_status_t. + uint32_t addressing_reset ; ///< This field indicates whether the addressing information, which is the multicast address list or packet filter, has been lost during the reset operation. This MUST be set to 0x00000001 if the device requires that the host to resend addressing information or MUST be set to zero otherwise. +} rndis_msg_reset_cmplt_t; + +//typedef struct { +// uint32_t type; +// uint32_t length; +// uint32_t status; +// uint32_t buffer_length; +// uint32_t buffer_offset; +// uint32_t diagnostic_status; // optional +// uint32_t diagnostic_error_offset; // optional +// uint32_t status_buffer[0]; // optional +//} rndis_msg_indicate_status_t; + +/// \brief Keep Alive Message +/// \details This message MUST be sent by the host to check that device is still responsive. It is optional for the device to send this message to check if the host is active +typedef struct { + uint32_t type ; ///< Message Type + uint32_t length ; ///< Message length in bytes, MUST be 0x10 + uint32_t request_id ; +} rndis_msg_keep_alive_t, rndis_msg_halt_t; + +/// \brief Set Complete Message +/// \brief This message MUST be sent in response to a the request message +typedef struct { + uint32_t type ; ///< Message Type + uint32_t length ; ///< Message length in bytes, MUST be 0x10 + uint32_t request_id ; ///< must be the same as requesting message + uint32_t status ; ///< The status of processing for the request message request by the device to which this message is the response. +} rndis_msg_set_cmplt_t, rndis_msg_keep_alive_cmplt_t; + +/// \brief Packet Data Message +/// \brief This message MUST be used by the host and the device to send network data to one another. +typedef struct { + uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_PACKET + uint32_t length ; ///< Message length in bytes, The total length of this RNDIS message including the header, payload, and padding. + uint32_t data_offset ; ///< Specifies the offset, in bytes, from the start of this \a data_offset field of this message to the start of the data. This MUST be an integer multiple of 4. + uint32_t data_length ; ///< Specifies the number of bytes in the payload of this message. + uint32_t out_of_band_data_offet ; ///< Specifies the offset, in bytes, of the first out-of-band data record from the start of the DataOffset field in this message. MUST be an integer multiple of 4 when out-of-band data is present or set to 0 otherwise. When there are multiple out-ofband data records, each subsequent record MUST immediately follow the previous out-of-band data record. + uint32_t out_of_band_data_length ; ///< Specifies, in bytes, the total length of the out-of-band data. + uint32_t num_out_of_band_data_elements ; ///< Specifies the number of out-of-band records in this message. + uint32_t per_packet_info_offset ; ///< Specifies the offset, in bytes, of the start of per-packet-info data record from the start of the \a data_offset field in this message. MUST be an integer multiple of 4 when per-packet-info data record is present or MUST be set to 0 otherwise. When there are multiple per-packet-info data records, each subsequent record MUST immediately follow the previous record. + uint32_t per_packet_info_length ; ///< Specifies, in bytes, the total length of per-packetinformation contained in this message. + uint32_t reserved[2] ; + uint32_t payload[0] ; ///< Network data contained in this message. + + // uint8_t padding[0] + // Additional bytes of zeros added at the end of the message to comply with + // the internal and external padding requirements. Internal padding SHOULD be as per the + // specification of the out-of-band data record and per-packet-info data record. The external + //padding size SHOULD be determined based on the PacketAlignmentFactor field specification + //in REMOTE_NDIS_INITIALIZE_CMPLT message by the device, when multiple + //REMOTE_NDIS_PACKET_MSG messages are bundled together in a single bus-native message. + //In this case, all but the very last REMOTE_NDIS_PACKET_MSG MUST respect the + //PacketAlignmentFactor field. + + // rndis_msg_packet_t [0] : (optional) more packet if multiple packet per bus transaction is supported +} rndis_msg_packet_t; + + +typedef struct { + uint32_t size ; ///< Length, in bytes, of this header and appended data and padding. This value MUST be an integer multiple of 4. + uint32_t type ; ///< MUST be as per host operating system specification. + uint32_t offset ; ///< The byte offset from the beginning of this record to the beginning of data. + uint32_t data[0] ; ///< Flexible array contains data +} rndis_msg_out_of_band_data_t, rndis_msg_per_packet_info_t; + +//--------------------------------------------------------------------+ +// NDIS Object ID +//--------------------------------------------------------------------+ + +/// NDIS Object ID +typedef enum +{ + //------------- General Required OIDs -------------// + RNDIS_OID_GEN_SUPPORTED_LIST = 0x00010101, ///< List of supported OIDs + RNDIS_OID_GEN_HARDWARE_STATUS = 0x00010102, ///< Hardware status + RNDIS_OID_GEN_MEDIA_SUPPORTED = 0x00010103, ///< Media types supported (encoded) + RNDIS_OID_GEN_MEDIA_IN_USE = 0x00010104, ///< Media types in use (encoded) + RNDIS_OID_GEN_MAXIMUM_LOOKAHEAD = 0x00010105, ///< + RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE = 0x00010106, ///< Maximum frame size in bytes + RNDIS_OID_GEN_LINK_SPEED = 0x00010107, ///< Link speed in units of 100 bps + RNDIS_OID_GEN_TRANSMIT_BUFFER_SPACE = 0x00010108, ///< Transmit buffer space + RNDIS_OID_GEN_RECEIVE_BUFFER_SPACE = 0x00010109, ///< Receive buffer space + RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE = 0x0001010A, ///< Minimum amount of storage, in bytes, that a single packet occupies in the transmit buffer space of the NIC + RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE = 0x0001010B, ///< Amount of storage, in bytes, that a single packet occupies in the receive buffer space of the NIC + RNDIS_OID_GEN_VENDOR_ID = 0x0001010C, ///< Vendor NIC code + RNDIS_OID_GEN_VENDOR_DESCRIPTION = 0x0001010D, ///< Vendor network card description + RNDIS_OID_GEN_CURRENT_PACKET_FILTER = 0x0001010E, ///< Current packet filter (encoded) + RNDIS_OID_GEN_CURRENT_LOOKAHEAD = 0x0001010F, ///< Current lookahead size in bytes + RNDIS_OID_GEN_DRIVER_VERSION = 0x00010110, ///< NDIS version number used by the driver + RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE = 0x00010111, ///< Maximum total packet length in bytes + RNDIS_OID_GEN_PROTOCOL_OPTIONS = 0x00010112, ///< Optional protocol flags (encoded) + RNDIS_OID_GEN_MAC_OPTIONS = 0x00010113, ///< Optional NIC flags (encoded) + RNDIS_OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114, ///< Whether the NIC is connected to the network + RNDIS_OID_GEN_MAXIMUM_SEND_PACKETS = 0x00010115, ///< The maximum number of send packets the driver can accept per call to its MiniportSendPacketsfunction + + //------------- General Optional OIDs -------------// + RNDIS_OID_GEN_VENDOR_DRIVER_VERSION = 0x00010116, ///< Vendor-assigned version number of the driver + RNDIS_OID_GEN_SUPPORTED_GUIDS = 0x00010117, ///< The custom GUIDs (Globally Unique Identifier) supported by the miniport driver + RNDIS_OID_GEN_NETWORK_LAYER_ADDRESSES = 0x00010118, ///< List of network-layer addresses associated with the binding between a transport and the driver + RNDIS_OID_GEN_TRANSPORT_HEADER_OFFSET = 0x00010119, ///< Size of packets' additional headers + RNDIS_OID_GEN_MEDIA_CAPABILITIES = 0x00010201, ///< + RNDIS_OID_GEN_PHYSICAL_MEDIUM = 0x00010202, ///< Physical media supported by the miniport driver (encoded) + + //------------- 802.3 Objects (Ethernet) -------------// + RNDIS_OID_802_3_PERMANENT_ADDRESS = 0x01010101, ///< Permanent station address + RNDIS_OID_802_3_CURRENT_ADDRESS = 0x01010102, ///< Current station address + RNDIS_OID_802_3_MULTICAST_LIST = 0x01010103, ///< Current multicast address list + RNDIS_OID_802_3_MAXIMUM_LIST_SIZE = 0x01010104, ///< Maximum size of multicast address list +} rndis_oid_type_t; + +/// RNDIS Packet Filter Bits \ref RNDIS_OID_GEN_CURRENT_PACKET_FILTER. +typedef enum +{ + RNDIS_PACKET_TYPE_DIRECTED = 0x00000001, ///< Directed packets. Directed packets contain a destination address equal to the station address of the NIC. + RNDIS_PACKET_TYPE_MULTICAST = 0x00000002, ///< Multicast address packets sent to addresses in the multicast address list. + RNDIS_PACKET_TYPE_ALL_MULTICAST = 0x00000004, ///< All multicast address packets, not just the ones enumerated in the multicast address list. + RNDIS_PACKET_TYPE_BROADCAST = 0x00000008, ///< Broadcast packets. + RNDIS_PACKET_TYPE_SOURCE_ROUTING = 0x00000010, ///< All source routing packets. If the protocol driver sets this bit, the NDIS library attempts to act as a source routing bridge. + RNDIS_PACKET_TYPE_PROMISCUOUS = 0x00000020, ///< Specifies all packets regardless of whether VLAN filtering is enabled or not and whether the VLAN identifier matches or not. + RNDIS_PACKET_TYPE_SMT = 0x00000040, ///< SMT packets that an FDDI NIC receives. + RNDIS_PACKET_TYPE_ALL_LOCAL = 0x00000080, ///< All packets sent by installed protocols and all packets indicated by the NIC that is identified by a given NdisBindingHandle. + RNDIS_PACKET_TYPE_GROUP = 0x00001000, ///< Packets sent to the current group address. + RNDIS_PACKET_TYPE_ALL_FUNCTIONAL = 0x00002000, ///< All functional address packets, not just the ones in the current functional address. + RNDIS_PACKET_TYPE_FUNCTIONAL = 0x00004000, ///< Functional address packets sent to addresses included in the current functional address. + RNDIS_PACKET_TYPE_MAC_FRAME = 0x00008000, ///< NIC driver frames that a Token Ring NIC receives. + RNDIS_PACKET_TYPE_NO_LOCAL = 0x00010000, +} rndis_packet_filter_type_t; + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CDC_RNDIS_H_ */ + +/** @} */ +/** @} */ diff --git a/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc_rndis_host.c b/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc_rndis_host.c new file mode 100644 index 00000000..11a5355a --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc_rndis_host.c @@ -0,0 +1,289 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if (CFG_TUH_ENABLED && CFG_TUH_CDC && CFG_TUH_CDC_RNDIS) + +//--------------------------------------------------------------------+ +// INCLUDE +//--------------------------------------------------------------------+ +#include "common/tusb_common.h" +#include "cdc_host.h" +#include "cdc_rndis_host.h" + +#if 0 // TODO remove subtask related macros later +// Sub Task +#define OSAL_SUBTASK_BEGIN +#define OSAL_SUBTASK_END return TUSB_ERROR_NONE; + +#define STASK_RETURN(_error) return _error; +#define STASK_INVOKE(_subtask, _status) (_status) = _subtask +#define STASK_ASSERT(_cond) TU_VERIFY(_cond, TUSB_ERROR_OSAL_TASK_FAILED) +#endif + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +#define RNDIS_MSG_PAYLOAD_MAX (1024*4) + +CFG_TUH_MEM_SECTION static uint8_t msg_notification[CFG_TUH_DEVICE_MAX][8]; +CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN static uint8_t msg_payload[RNDIS_MSG_PAYLOAD_MAX]; + +static rndish_data_t rndish_data[CFG_TUH_DEVICE_MAX]; + +// TODO Microsoft requires message length for any get command must be at least 4096 bytes + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ +static tusb_error_t rndis_body_subtask(void); +static tusb_error_t send_message_get_response_subtask( uint8_t dev_addr, cdch_data_t *p_cdc, + uint8_t * p_mess, uint32_t mess_length, + uint8_t *p_response ); + +//--------------------------------------------------------------------+ +// APPLICATION API +//--------------------------------------------------------------------+ +tusb_error_t tusbh_cdc_rndis_get_mac_addr(uint8_t dev_addr, uint8_t mac_address[6]) +{ + TU_ASSERT( tusbh_cdc_rndis_is_mounted(dev_addr), TUSB_ERROR_CDCH_DEVICE_NOT_MOUNTED); + TU_VERIFY( mac_address, TUSB_ERROR_INVALID_PARA); + + memcpy(mac_address, rndish_data[dev_addr-1].mac_address, 6); + + return TUSB_ERROR_NONE; +} + +//--------------------------------------------------------------------+ +// IMPLEMENTATION +//--------------------------------------------------------------------+ + +// To enable the TASK_ASSERT style (quick return on false condition) in a real RTOS, a task must act as a wrapper +// and is used mainly to call subtasks. Within a subtask return statement can be called freely, the task with +// forever loop cannot have any return at all. +OSAL_TASK_FUNCTION(cdch_rndis_task) (void* param;) +{ + OSAL_TASK_BEGIN + rndis_body_subtask(); + OSAL_TASK_END +} + +static tusb_error_t rndis_body_subtask(void) +{ + static uint8_t relative_addr; + + OSAL_SUBTASK_BEGIN + + for (relative_addr = 0; relative_addr < CFG_TUH_DEVICE_MAX; relative_addr++) + { + + } + + osal_task_delay(100); + + OSAL_SUBTASK_END +} + +//--------------------------------------------------------------------+ +// RNDIS-CDC Driver API +//--------------------------------------------------------------------+ +void rndish_init(void) +{ + tu_memclr(rndish_data, sizeof(rndish_data_t)*CFG_TUH_DEVICE_MAX); + + //------------- Task creation -------------// + + //------------- semaphore creation for notification pipe -------------// + for(uint8_t i=0; itype == RNDIS_MSG_INITIALIZE_CMPLT && p_init_cmpt->status == RNDIS_STATUS_SUCCESS && + p_init_cmpt->max_packet_per_xfer == 1 && p_init_cmpt->max_xfer_size <= RNDIS_MSG_PAYLOAD_MAX); + rndish_data[dev_addr-1].max_xfer_size = p_init_cmpt->max_xfer_size; + + //------------- Message Query 802.3 Permanent Address -------------// + memcpy(msg_payload, &msg_query_permanent_addr, sizeof(rndis_msg_query_t)); + tu_memclr(msg_payload + sizeof(rndis_msg_query_t), 6); // 6 bytes for MAC address + + STASK_INVOKE( + send_message_get_response_subtask( dev_addr, p_cdc, + msg_payload, sizeof(rndis_msg_query_t) + 6, + msg_payload), + error + ); + if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error); + + rndis_msg_query_cmplt_t * const p_query_cmpt = (rndis_msg_query_cmplt_t *) msg_payload; + STASK_ASSERT(p_query_cmpt->type == RNDIS_MSG_QUERY_CMPLT && p_query_cmpt->status == RNDIS_STATUS_SUCCESS); + memcpy(rndish_data[dev_addr-1].mac_address, msg_payload + 8 + p_query_cmpt->buffer_offset, 6); + + //------------- Set OID_GEN_CURRENT_PACKET_FILTER to (DIRECTED | MULTICAST | BROADCAST) -------------// + memcpy(msg_payload, &msg_set_packet_filter, sizeof(rndis_msg_set_t)); + tu_memclr(msg_payload + sizeof(rndis_msg_set_t), 4); // 4 bytes for filter flags + ((rndis_msg_set_t*) msg_payload)->oid_buffer[0] = (RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_MULTICAST | RNDIS_PACKET_TYPE_BROADCAST); + + STASK_INVOKE( + send_message_get_response_subtask( dev_addr, p_cdc, + msg_payload, sizeof(rndis_msg_set_t) + 4, + msg_payload), + error + ); + if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error); + + rndis_msg_set_cmplt_t * const p_set_cmpt = (rndis_msg_set_cmplt_t *) msg_payload; + STASK_ASSERT(p_set_cmpt->type == RNDIS_MSG_SET_CMPLT && p_set_cmpt->status == RNDIS_STATUS_SUCCESS); + + tusbh_cdc_rndis_mounted_cb(dev_addr); + + OSAL_SUBTASK_END +} + +void rndish_xfer_isr(cdch_data_t *p_cdc, pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes) +{ + if ( pipehandle_is_equal(pipe_hdl, p_cdc->pipe_notification) ) + { + osal_semaphore_post( rndish_data[pipe_hdl.dev_addr-1].sem_notification_hdl ); + } +} + +//--------------------------------------------------------------------+ +// INTERNAL & HELPER +//--------------------------------------------------------------------+ +static tusb_error_t send_message_get_response_subtask( uint8_t dev_addr, cdch_data_t *p_cdc, + uint8_t * p_mess, uint32_t mess_length, + uint8_t *p_response) +{ + tusb_error_t error; + + OSAL_SUBTASK_BEGIN + + //------------- Send RNDIS Control Message -------------// + STASK_INVOKE( + usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_OUT, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_INTERFACE), + CDC_REQUEST_SEND_ENCAPSULATED_COMMAND, 0, p_cdc->interface_number, + mess_length, p_mess), + error + ); + if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error); + + //------------- waiting for Response Available notification -------------// + (void) usbh_edpt_xfer(p_cdc->pipe_notification, msg_notification[dev_addr-1], 8); + osal_semaphore_wait(rndish_data[dev_addr-1].sem_notification_hdl, OSAL_TIMEOUT_NORMAL, &error); + if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error); + STASK_ASSERT(msg_notification[dev_addr-1][0] == 1); + + //------------- Get RNDIS Message Initialize Complete -------------// + STASK_INVOKE( + usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_IN, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_INTERFACE), + CDC_REQUEST_GET_ENCAPSULATED_RESPONSE, 0, p_cdc->interface_number, + RNDIS_MSG_PAYLOAD_MAX, p_response), + error + ); + if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error); + + OSAL_SUBTASK_END +} + +//static tusb_error_t send_process_msg_initialize_subtask(uint8_t dev_addr, cdch_data_t *p_cdc) +//{ +// tusb_error_t error; +// +// OSAL_SUBTASK_BEGIN +// +// *((rndis_msg_initialize_t*) msg_payload) = (rndis_msg_initialize_t) +// { +// .type = RNDIS_MSG_INITIALIZE, +// .length = sizeof(rndis_msg_initialize_t), +// .request_id = 1, // TODO should use some magic number +// .major_version = 1, +// .minor_version = 0, +// .max_xfer_size = 0x4000 // TODO mimic windows +// }; +// +// +// +// OSAL_SUBTASK_END +//} +#endif diff --git a/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc_rndis_host.h b/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc_rndis_host.h new file mode 100644 index 00000000..bb431ec1 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/class/cdc/cdc_rndis_host.h @@ -0,0 +1,63 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +/** \ingroup CDC_RNDIS + * \defgroup CDC_RNSID_Host Host + * @{ */ + +#ifndef _TUSB_CDC_RNDIS_HOST_H_ +#define _TUSB_CDC_RNDIS_HOST_H_ + +#include "common/tusb_common.h" +#include "host/usbh.h" +#include "cdc_rndis.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// INTERNAL RNDIS-CDC Driver API +//--------------------------------------------------------------------+ +typedef struct { + OSAL_SEM_DEF(semaphore_notification); + osal_semaphore_handle_t sem_notification_hdl; // used to wait on notification pipe + uint32_t max_xfer_size; // got from device's msg initialize complete + uint8_t mac_address[6]; +}rndish_data_t; + +void rndish_init(void); +bool rndish_open_subtask(uint8_t dev_addr, cdch_data_t *p_cdc); +void rndish_xfer_isr(cdch_data_t *p_cdc, pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes); +void rndish_close(uint8_t dev_addr); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CDC_RNDIS_HOST_H_ */ + +/** @} */ diff --git a/test-devices/composite-stm32/lib/tinyusb/class/cdc/serial/ch34x.h b/test-devices/composite-stm32/lib/tinyusb/class/cdc/serial/ch34x.h new file mode 100644 index 00000000..c18066f5 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/class/cdc/serial/ch34x.h @@ -0,0 +1,84 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2023 Heiko Kuester + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _CH34X_H_ +#define _CH34X_H_ + +// There is no official documentation for the CH34x (CH340, CH341) chips. Reference can be found +// - https://github.com/WCHSoftGroup/ch341ser_linux +// - https://github.com/torvalds/linux/blob/master/drivers/usb/serial/ch341.c +// - https://github.com/freebsd/freebsd-src/blob/main/sys/dev/usb/serial/uchcom.c + +// set line_coding @ enumeration +#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM +#define CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X CFG_TUH_CDC_LINE_CODING_ON_ENUM +#else // this default is necessary to work properly +#define CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X { 9600, CDC_LINE_CONDING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 } +#endif + +// USB requests +#define CH34X_REQ_READ_VERSION 0x5F // dec 95 +#define CH34X_REQ_WRITE_REG 0x9A // dec 154 +#define CH34X_REQ_READ_REG 0x95 // dec 149 +#define CH34X_REQ_SERIAL_INIT 0xA1 // dec 161 +#define CH34X_REQ_MODEM_CTRL 0xA4 // dev 164 + +// registers +#define CH34X_REG_BREAK 0x05 +#define CH34X_REG_PRESCALER 0x12 +#define CH34X_REG_DIVISOR 0x13 +#define CH34X_REG_LCR 0x18 +#define CH34X_REG_LCR2 0x25 +#define CH34X_REG_MCR_MSR 0x06 +#define CH34X_REG_MCR_MSR2 0x07 +#define CH34X_NBREAK_BITS 0x01 + +#define CH341_REG_0x0F 0x0F // undocumented register +#define CH341_REG_0x2C 0x2C // undocumented register +#define CH341_REG_0x27 0x27 // hardware flow control (cts/rts) + +#define CH34X_REG16_DIVISOR_PRESCALER TU_U16(CH34X_REG_DIVISOR, CH34X_REG_PRESCALER) +#define CH32X_REG16_LCR2_LCR TU_U16(CH34X_REG_LCR2, CH34X_REG_LCR) + +// modem control bits +#define CH34X_BIT_RTS ( 1 << 6 ) +#define CH34X_BIT_DTR ( 1 << 5 ) + +// line control bits +#define CH34X_LCR_ENABLE_RX 0x80 +#define CH34X_LCR_ENABLE_TX 0x40 +#define CH34X_LCR_MARK_SPACE 0x20 +#define CH34X_LCR_PAR_EVEN 0x10 +#define CH34X_LCR_ENABLE_PAR 0x08 +#define CH34X_LCR_PAR_MASK 0x38 // all parity bits +#define CH34X_LCR_STOP_BITS_2 0x04 +#define CH34X_LCR_CS8 0x03 +#define CH34X_LCR_CS7 0x02 +#define CH34X_LCR_CS6 0x01 +#define CH34X_LCR_CS5 0x00 +#define CH34X_LCR_CS_MASK 0x03 // all CSx bits + +#endif /* _CH34X_H_ */ diff --git a/test-devices/composite-stm32/lib/tinyusb/class/cdc/serial/cp210x.h b/test-devices/composite-stm32/lib/tinyusb/class/cdc/serial/cp210x.h new file mode 100644 index 00000000..2c749f52 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/class/cdc/serial/cp210x.h @@ -0,0 +1,62 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2023 Ha Thach (thach@tinyusb.org) for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef TUSB_CP210X_H +#define TUSB_CP210X_H + +// Protocol details can be found at AN571: CP210x Virtual COM Port Interface +// https://www.silabs.com/documents/public/application-notes/AN571.pdf + +#define TU_CP210X_VID 0x10C4 + +/* Config request codes */ +#define CP210X_IFC_ENABLE 0x00 +#define CP210X_SET_BAUDDIV 0x01 +#define CP210X_GET_BAUDDIV 0x02 +#define CP210X_SET_LINE_CTL 0x03 // Set parity, data bits, stop bits +#define CP210X_GET_LINE_CTL 0x04 +#define CP210X_SET_BREAK 0x05 +#define CP210X_IMM_CHAR 0x06 +#define CP210X_SET_MHS 0x07 // Set DTR, RTS +#define CP210X_GET_MDMSTS 0x08 // Get modem status (DTR, RTS, CTS, DSR, RI, DCD) +#define CP210X_SET_XON 0x09 +#define CP210X_SET_XOFF 0x0A +#define CP210X_SET_EVENTMASK 0x0B +#define CP210X_GET_EVENTMASK 0x0C +#define CP210X_SET_CHAR 0x0D +#define CP210X_GET_CHARS 0x0E +#define CP210X_GET_PROPS 0x0F +#define CP210X_GET_COMM_STATUS 0x10 +#define CP210X_RESET 0x11 +#define CP210X_PURGE 0x12 +#define CP210X_SET_FLOW 0x13 +#define CP210X_GET_FLOW 0x14 +#define CP210X_EMBED_EVENTS 0x15 +#define CP210X_GET_EVENTSTATE 0x16 +#define CP210X_SET_CHARS 0x19 +#define CP210X_GET_BAUDRATE 0x1D +#define CP210X_SET_BAUDRATE 0x1E +#define CP210X_VENDOR_SPECIFIC 0xFF // GPIO, Recipient must be Device + +#endif //TUSB_CP210X_H diff --git a/test-devices/composite-stm32/lib/tinyusb/class/cdc/serial/ftdi_sio.h b/test-devices/composite-stm32/lib/tinyusb/class/cdc/serial/ftdi_sio.h new file mode 100644 index 00000000..0825f071 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/class/cdc/serial/ftdi_sio.h @@ -0,0 +1,246 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2023 Ha Thach (thach@tinyusb.org) for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef TUSB_FTDI_SIO_H +#define TUSB_FTDI_SIO_H + +// VID for matching FTDI devices +#define TU_FTDI_VID 0x0403 + +// Commands +#define FTDI_SIO_RESET 0 /* Reset the port */ +#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ +#define FTDI_SIO_SET_FLOW_CTRL 2 /* Set flow control register */ +#define FTDI_SIO_SET_BAUD_RATE 3 /* Set baud rate */ +#define FTDI_SIO_SET_DATA 4 /* Set the data characteristics of the port */ +#define FTDI_SIO_GET_MODEM_STATUS 5 /* Retrieve current value of modem status register */ +#define FTDI_SIO_SET_EVENT_CHAR 6 /* Set the event character */ +#define FTDI_SIO_SET_ERROR_CHAR 7 /* Set the error character */ +#define FTDI_SIO_SET_LATENCY_TIMER 9 /* Set the latency timer */ +#define FTDI_SIO_GET_LATENCY_TIMER 0x0a /* Get the latency timer */ +#define FTDI_SIO_SET_BITMODE 0x0b /* Set bitbang mode */ +#define FTDI_SIO_READ_PINS 0x0c /* Read immediate value of pins */ +#define FTDI_SIO_READ_EEPROM 0x90 /* Read EEPROM */ + +/* FTDI_SIO_RESET */ +#define FTDI_SIO_RESET_SIO 0 +#define FTDI_SIO_RESET_PURGE_RX 1 +#define FTDI_SIO_RESET_PURGE_TX 2 + +/* + * BmRequestType: 0100 0000B + * bRequest: FTDI_SIO_RESET + * wValue: Control Value + * 0 = Reset SIO + * 1 = Purge RX buffer + * 2 = Purge TX buffer + * wIndex: Port + * wLength: 0 + * Data: None + * + * The Reset SIO command has this effect: + * + * Sets flow control set to 'none' + * Event char = $0D + * Event trigger = disabled + * Purge RX buffer + * Purge TX buffer + * Clear DTR + * Clear RTS + * baud and data format not reset + * + * The Purge RX and TX buffer commands affect nothing except the buffers + * + */ + +/* FTDI_SIO_MODEM_CTRL */ +/* + * BmRequestType: 0100 0000B + * bRequest: FTDI_SIO_MODEM_CTRL + * wValue: ControlValue (see below) + * wIndex: Port + * wLength: 0 + * Data: None + * + * NOTE: If the device is in RTS/CTS flow control, the RTS set by this + * command will be IGNORED without an error being returned + * Also - you can not set DTR and RTS with one control message + */ + +#define FTDI_SIO_SET_DTR_MASK 0x1 +#define FTDI_SIO_SET_DTR_HIGH ((FTDI_SIO_SET_DTR_MASK << 8) | 1) +#define FTDI_SIO_SET_DTR_LOW ((FTDI_SIO_SET_DTR_MASK << 8) | 0) +#define FTDI_SIO_SET_RTS_MASK 0x2 +#define FTDI_SIO_SET_RTS_HIGH ((FTDI_SIO_SET_RTS_MASK << 8) | 2) +#define FTDI_SIO_SET_RTS_LOW ((FTDI_SIO_SET_RTS_MASK << 8) | 0) + +/* + * ControlValue + * B0 DTR state + * 0 = reset + * 1 = set + * B1 RTS state + * 0 = reset + * 1 = set + * B2..7 Reserved + * B8 DTR state enable + * 0 = ignore + * 1 = use DTR state + * B9 RTS state enable + * 0 = ignore + * 1 = use RTS state + * B10..15 Reserved + */ + +/* FTDI_SIO_SET_FLOW_CTRL */ +#define FTDI_SIO_DISABLE_FLOW_CTRL 0x0 +#define FTDI_SIO_RTS_CTS_HS (0x1 << 8) +#define FTDI_SIO_DTR_DSR_HS (0x2 << 8) +#define FTDI_SIO_XON_XOFF_HS (0x4 << 8) + +/* + * BmRequestType: 0100 0000b + * bRequest: FTDI_SIO_SET_FLOW_CTRL + * wValue: Xoff/Xon + * wIndex: Protocol/Port - hIndex is protocol / lIndex is port + * wLength: 0 + * Data: None + * + * hIndex protocol is: + * B0 Output handshaking using RTS/CTS + * 0 = disabled + * 1 = enabled + * B1 Output handshaking using DTR/DSR + * 0 = disabled + * 1 = enabled + * B2 Xon/Xoff handshaking + * 0 = disabled + * 1 = enabled + * + * A value of zero in the hIndex field disables handshaking + * + * If Xon/Xoff handshaking is specified, the hValue field should contain the + * XOFF character and the lValue field contains the XON character. + */ + +/* FTDI_SIO_SET_BAUD_RATE */ +/* + * BmRequestType: 0100 0000B + * bRequest: FTDI_SIO_SET_BAUDRATE + * wValue: BaudDivisor value - see below + * wIndex: Port + * wLength: 0 + * Data: None + * The BaudDivisor values are calculated as follows (too complicated): + */ + +/* FTDI_SIO_SET_DATA */ +#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8) +#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8) +#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8) +#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8) +#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8) +#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11) +#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11) +#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11) +#define FTDI_SIO_SET_BREAK (0x1 << 14) + +/* + * BmRequestType: 0100 0000B + * bRequest: FTDI_SIO_SET_DATA + * wValue: Data characteristics (see below) + * wIndex: Port + * wLength: 0 + * Data: No + * + * Data characteristics + * + * B0..7 Number of data bits + * B8..10 Parity + * 0 = None + * 1 = Odd + * 2 = Even + * 3 = Mark + * 4 = Space + * B11..13 Stop Bits + * 0 = 1 + * 1 = 1.5 + * 2 = 2 + * B14 + * 1 = TX ON (break) + * 0 = TX OFF (normal state) + * B15 Reserved + * + */ + +/* +* DATA FORMAT +* +* IN Endpoint +* +* The device reserves the first two bytes of data on this endpoint to contain +* the current values of the modem and line status registers. In the absence of +* data, the device generates a message consisting of these two status bytes + * every 40 ms + * + * Byte 0: Modem Status +* +* Offset Description +* B0 Reserved - must be 1 +* B1 Reserved - must be 0 +* B2 Reserved - must be 0 +* B3 Reserved - must be 0 +* B4 Clear to Send (CTS) +* B5 Data Set Ready (DSR) +* B6 Ring Indicator (RI) +* B7 Receive Line Signal Detect (RLSD) +* +* Byte 1: Line Status +* +* Offset Description +* B0 Data Ready (DR) +* B1 Overrun Error (OE) +* B2 Parity Error (PE) +* B3 Framing Error (FE) +* B4 Break Interrupt (BI) +* B5 Transmitter Holding Register (THRE) +* B6 Transmitter Empty (TEMT) +* B7 Error in RCVR FIFO +* +*/ +#define FTDI_RS0_CTS (1 << 4) +#define FTDI_RS0_DSR (1 << 5) +#define FTDI_RS0_RI (1 << 6) +#define FTDI_RS0_RLSD (1 << 7) + +#define FTDI_RS_DR 1 +#define FTDI_RS_OE (1<<1) +#define FTDI_RS_PE (1<<2) +#define FTDI_RS_FE (1<<3) +#define FTDI_RS_BI (1<<4) +#define FTDI_RS_THRE (1<<5) +#define FTDI_RS_TEMT (1<<6) +#define FTDI_RS_FIFO (1<<7) + +#endif //TUSB_FTDI_SIO_H diff --git a/test-devices/composite-stm32/lib/tinyusb/common/tusb_common.h b/test-devices/composite-stm32/lib/tinyusb/common/tusb_common.h new file mode 100644 index 00000000..0d4082c0 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/common/tusb_common.h @@ -0,0 +1,316 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_COMMON_H_ +#define _TUSB_COMMON_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Macros Helper +//--------------------------------------------------------------------+ +#define TU_ARRAY_SIZE(_arr) ( sizeof(_arr) / sizeof(_arr[0]) ) +#define TU_MIN(_x, _y) ( ( (_x) < (_y) ) ? (_x) : (_y) ) +#define TU_MAX(_x, _y) ( ( (_x) > (_y) ) ? (_x) : (_y) ) +#define TU_DIV_CEIL(n, d) (((n) + (d) - 1) / (d)) + +#define TU_U16(_high, _low) ((uint16_t) (((_high) << 8) | (_low))) +#define TU_U16_HIGH(_u16) ((uint8_t) (((_u16) >> 8) & 0x00ff)) +#define TU_U16_LOW(_u16) ((uint8_t) ((_u16) & 0x00ff)) +#define U16_TO_U8S_BE(_u16) TU_U16_HIGH(_u16), TU_U16_LOW(_u16) +#define U16_TO_U8S_LE(_u16) TU_U16_LOW(_u16), TU_U16_HIGH(_u16) + +#define TU_U32_BYTE3(_u32) ((uint8_t) ((((uint32_t) _u32) >> 24) & 0x000000ff)) // MSB +#define TU_U32_BYTE2(_u32) ((uint8_t) ((((uint32_t) _u32) >> 16) & 0x000000ff)) +#define TU_U32_BYTE1(_u32) ((uint8_t) ((((uint32_t) _u32) >> 8) & 0x000000ff)) +#define TU_U32_BYTE0(_u32) ((uint8_t) (((uint32_t) _u32) & 0x000000ff)) // LSB + +#define U32_TO_U8S_BE(_u32) TU_U32_BYTE3(_u32), TU_U32_BYTE2(_u32), TU_U32_BYTE1(_u32), TU_U32_BYTE0(_u32) +#define U32_TO_U8S_LE(_u32) TU_U32_BYTE0(_u32), TU_U32_BYTE1(_u32), TU_U32_BYTE2(_u32), TU_U32_BYTE3(_u32) + +#define TU_BIT(n) (1UL << (n)) + +// Generate a mask with bit from high (31) to low (0) set, e.g TU_GENMASK(3, 0) = 0b1111 +#define TU_GENMASK(h, l) ( (UINT32_MAX << (l)) & (UINT32_MAX >> (31 - (h))) ) + +//--------------------------------------------------------------------+ +// Includes +//--------------------------------------------------------------------+ + +// Standard Headers +#include +#include +#include +#include +#include +#include + +// Tinyusb Common Headers +#include "tusb_option.h" +#include "tusb_compiler.h" +#include "tusb_verify.h" +#include "tusb_types.h" +#include "tusb_debug.h" + +//--------------------------------------------------------------------+ +// Optional API implemented by application if needed +// TODO move to a more ovious place/file +//--------------------------------------------------------------------+ + +// flush data cache +TU_ATTR_WEAK extern void tusb_app_dcache_flush(uintptr_t addr, uint32_t data_size); + +// invalidate data cache +TU_ATTR_WEAK extern void tusb_app_dcache_invalidate(uintptr_t addr, uint32_t data_size); + +// Optional physical <-> virtual address translation +TU_ATTR_WEAK extern void* tusb_app_virt_to_phys(void *virt_addr); +TU_ATTR_WEAK extern void* tusb_app_phys_to_virt(void *phys_addr); + +//--------------------------------------------------------------------+ +// Internal Inline Functions +//--------------------------------------------------------------------+ + +//------------- Mem -------------// +#define tu_memclr(buffer, size) memset((buffer), 0, (size)) +#define tu_varclr(_var) tu_memclr(_var, sizeof(*(_var))) + +// This is a backport of memset_s from c11 +TU_ATTR_ALWAYS_INLINE static inline int tu_memset_s(void *dest, size_t destsz, int ch, size_t count) { + // TODO may check if desst and src is not NULL + if ( count > destsz ) { + return -1; + } + memset(dest, ch, count); + return 0; +} + +// This is a backport of memcpy_s from c11 +TU_ATTR_ALWAYS_INLINE static inline int tu_memcpy_s(void *dest, size_t destsz, const void *src, size_t count) { + // TODO may check if desst and src is not NULL + if ( count > destsz ) { + return -1; + } + memcpy(dest, src, count); + return 0; +} + + +//------------- Bytes -------------// +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_u32(uint8_t b3, uint8_t b2, uint8_t b1, uint8_t b0) { + return ( ((uint32_t) b3) << 24) | ( ((uint32_t) b2) << 16) | ( ((uint32_t) b1) << 8) | b0; +} + +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_u16(uint8_t high, uint8_t low) { + return (uint16_t) ((((uint16_t) high) << 8) | low); +} + +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte3(uint32_t ui32) { return TU_U32_BYTE3(ui32); } +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte2(uint32_t ui32) { return TU_U32_BYTE2(ui32); } +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte1(uint32_t ui32) { return TU_U32_BYTE1(ui32); } +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte0(uint32_t ui32) { return TU_U32_BYTE0(ui32); } + +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_u32_high16(uint32_t ui32) { return (uint16_t) (ui32 >> 16); } +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_u32_low16 (uint32_t ui32) { return (uint16_t) (ui32 & 0x0000ffffu); } + +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u16_high(uint16_t ui16) { return TU_U16_HIGH(ui16); } +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u16_low (uint16_t ui16) { return TU_U16_LOW(ui16); } + +//------------- Bits -------------// +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_bit_set (uint32_t value, uint8_t pos) { return value | TU_BIT(pos); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_bit_clear(uint32_t value, uint8_t pos) { return value & (~TU_BIT(pos)); } +TU_ATTR_ALWAYS_INLINE static inline bool tu_bit_test (uint32_t value, uint8_t pos) { return (value & TU_BIT(pos)) ? true : false; } + +//------------- Min -------------// +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_min8 (uint8_t x, uint8_t y ) { return (x < y) ? x : y; } +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_min16 (uint16_t x, uint16_t y) { return (x < y) ? x : y; } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_min32 (uint32_t x, uint32_t y) { return (x < y) ? x : y; } + +//------------- Max -------------// +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_max8 (uint8_t x, uint8_t y ) { return (x > y) ? x : y; } +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_max16 (uint16_t x, uint16_t y) { return (x > y) ? x : y; } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_max32 (uint32_t x, uint32_t y) { return (x > y) ? x : y; } + +//------------- Align -------------// +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align(uint32_t value, uint32_t alignment) { + return value & ((uint32_t) ~(alignment-1)); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align4 (uint32_t value) { return (value & 0xFFFFFFFCUL); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align8 (uint32_t value) { return (value & 0xFFFFFFF8UL); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align16 (uint32_t value) { return (value & 0xFFFFFFF0UL); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align32 (uint32_t value) { return (value & 0xFFFFFFE0UL); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align4k (uint32_t value) { return (value & 0xFFFFF000UL); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_offset4k(uint32_t value) { return (value & 0xFFFUL); } + +TU_ATTR_ALWAYS_INLINE static inline bool tu_is_aligned32(uint32_t value) { return (value & 0x1FUL) == 0; } +TU_ATTR_ALWAYS_INLINE static inline bool tu_is_aligned64(uint64_t value) { return (value & 0x3FUL) == 0; } + +//------------- Mathematics -------------// +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_div_ceil(uint32_t v, uint32_t d) { return (v + d -1)/d; } + +// log2 of a value is its MSB's position +// TODO use clz TODO remove +static inline uint8_t tu_log2(uint32_t value) +{ + uint8_t result = 0; + while (value >>= 1) { result++; } + return result; +} + +//static inline uint8_t tu_log2(uint32_t value) +//{ +// return sizeof(uint32_t) * CHAR_BIT - __builtin_clz(x) - 1; +//} + +static inline bool tu_is_power_of_two(uint32_t value) +{ + return (value != 0) && ((value & (value - 1)) == 0); +} + +//------------- Unaligned Access -------------// +#if TUP_ARCH_STRICT_ALIGN + +// Rely on compiler to generate correct code for unaligned access +typedef struct { uint16_t val; } TU_ATTR_PACKED tu_unaligned_uint16_t; +typedef struct { uint32_t val; } TU_ATTR_PACKED tu_unaligned_uint32_t; + +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_unaligned_read32(const void* mem) +{ + tu_unaligned_uint32_t const* ua32 = (tu_unaligned_uint32_t const*) mem; + return ua32->val; +} + +TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write32(void* mem, uint32_t value) +{ + tu_unaligned_uint32_t* ua32 = (tu_unaligned_uint32_t*) mem; + ua32->val = value; +} + +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_unaligned_read16(const void* mem) +{ + tu_unaligned_uint16_t const* ua16 = (tu_unaligned_uint16_t const*) mem; + return ua16->val; +} + +TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16(void* mem, uint16_t value) +{ + tu_unaligned_uint16_t* ua16 = (tu_unaligned_uint16_t*) mem; + ua16->val = value; +} + +#elif TUP_MCU_STRICT_ALIGN + +// MCU such as LPC_IP3511 Highspeed cannot access unaligned memory on USB_RAM although it is ARM M4. +// We have to manually pick up bytes since tu_unaligned_uint32_t will still generate unaligned code +// NOTE: volatile cast to memory to prevent compiler to optimize and generate unaligned code +// TODO Big Endian may need minor changes +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_unaligned_read32(const void* mem) +{ + volatile uint8_t const* buf8 = (uint8_t const*) mem; + return tu_u32(buf8[3], buf8[2], buf8[1], buf8[0]); +} + +TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write32(void* mem, uint32_t value) +{ + volatile uint8_t* buf8 = (uint8_t*) mem; + buf8[0] = tu_u32_byte0(value); + buf8[1] = tu_u32_byte1(value); + buf8[2] = tu_u32_byte2(value); + buf8[3] = tu_u32_byte3(value); +} + +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_unaligned_read16(const void* mem) +{ + volatile uint8_t const* buf8 = (uint8_t const*) mem; + return tu_u16(buf8[1], buf8[0]); +} + +TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16(void* mem, uint16_t value) +{ + volatile uint8_t* buf8 = (uint8_t*) mem; + buf8[0] = tu_u16_low(value); + buf8[1] = tu_u16_high(value); +} + + +#else + +// MCU that could access unaligned memory natively +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_unaligned_read32(const void *mem) { + return *((uint32_t const *) mem); +} + +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_unaligned_read16(const void *mem) { + return *((uint16_t const *) mem); +} + +TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write32(void *mem, uint32_t value) { + *((uint32_t *) mem) = value; +} + +TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16(void *mem, uint16_t value) { + *((uint16_t *) mem) = value; +} + +#endif + +// To be removed +//------------- Binary constant -------------// +#if defined(__GNUC__) && !defined(__CC_ARM) + +#define TU_BIN8(x) ((uint8_t) (0b##x)) +#define TU_BIN16(b1, b2) ((uint16_t) (0b##b1##b2)) +#define TU_BIN32(b1, b2, b3, b4) ((uint32_t) (0b##b1##b2##b3##b4)) + +#else + +// internal macro of B8, B16, B32 +#define _B8__(x) (((x&0x0000000FUL)?1:0) \ + +((x&0x000000F0UL)?2:0) \ + +((x&0x00000F00UL)?4:0) \ + +((x&0x0000F000UL)?8:0) \ + +((x&0x000F0000UL)?16:0) \ + +((x&0x00F00000UL)?32:0) \ + +((x&0x0F000000UL)?64:0) \ + +((x&0xF0000000UL)?128:0)) + +#define TU_BIN8(d) ((uint8_t) _B8__(0x##d##UL)) +#define TU_BIN16(dmsb,dlsb) (((uint16_t)TU_BIN8(dmsb)<<8) + TU_BIN8(dlsb)) +#define TU_BIN32(dmsb,db2,db3,dlsb) \ + (((uint32_t)TU_BIN8(dmsb)<<24) \ + + ((uint32_t)TU_BIN8(db2)<<16) \ + + ((uint32_t)TU_BIN8(db3)<<8) \ + + TU_BIN8(dlsb)) +#endif + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_COMMON_H_ */ diff --git a/test-devices/composite-stm32/lib/tinyusb/common/tusb_compiler.h b/test-devices/composite-stm32/lib/tinyusb/common/tusb_compiler.h new file mode 100644 index 00000000..0d5570b1 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/common/tusb_compiler.h @@ -0,0 +1,298 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +/** \ingroup Group_Common + * \defgroup Group_Compiler Compiler + * \brief Group_Compiler brief + * @{ */ + +#ifndef _TUSB_COMPILER_H_ +#define _TUSB_COMPILER_H_ + +#define TU_TOKEN(x) x +#define TU_STRING(x) #x ///< stringify without expand +#define TU_XSTRING(x) TU_STRING(x) ///< expand then stringify + +#define TU_STRCAT(a, b) a##b ///< concat without expand +#define TU_STRCAT3(a, b, c) a##b##c ///< concat without expand + +#define TU_XSTRCAT(a, b) TU_STRCAT(a, b) ///< expand then concat +#define TU_XSTRCAT3(a, b, c) TU_STRCAT3(a, b, c) ///< expand then concat 3 tokens + +#define TU_INCLUDE_PATH(_dir,_file) TU_XSTRING( TU_TOKEN(_dir)TU_TOKEN(_file) ) + +#if defined __COUNTER__ && __COUNTER__ != __COUNTER__ + #define _TU_COUNTER_ __COUNTER__ +#else + #define _TU_COUNTER_ __LINE__ +#endif + +// Compile-time Assert +#if defined (__cplusplus) && __cplusplus >= 201103L + #define TU_VERIFY_STATIC static_assert +#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define TU_VERIFY_STATIC _Static_assert +#elif defined(__CCRX__) + #define TU_VERIFY_STATIC(const_expr, _mess) typedef char TU_XSTRCAT(_verify_static_, _TU_COUNTER_)[(const_expr) ? 1 : 0]; +#else + #define TU_VERIFY_STATIC(const_expr, _mess) enum { TU_XSTRCAT(_verify_static_, _TU_COUNTER_) = 1/(!!(const_expr)) } +#endif + +/* --------------------- Fuzzing types -------------------------------------- */ +#ifdef _FUZZ + #define tu_static static __thread +#else + #define tu_static static +#endif + +// for declaration of reserved field, make use of _TU_COUNTER_ +#define TU_RESERVED TU_XSTRCAT(reserved, _TU_COUNTER_) + +#define TU_LITTLE_ENDIAN (0x12u) +#define TU_BIG_ENDIAN (0x21u) + +/*------------------------------------------------------------------*/ +/* Count number of arguments of __VA_ARGS__ + * - reference https://stackoverflow.com/questions/2124339/c-preprocessor-va-args-number-of-arguments + * - _GET_NTH_ARG() takes args >= N (64) but only expand to Nth one (64th) + * - _RSEQ_N() is reverse sequential to N to add padding to have + * Nth position is the same as the number of arguments + * - ##__VA_ARGS__ is used to deal with 0 paramerter (swallows comma) + *------------------------------------------------------------------*/ +#if !defined(__CCRX__) +#define TU_ARGS_NUM(...) _TU_NARG(_0, ##__VA_ARGS__, _RSEQ_N()) +#else +#define TU_ARGS_NUM(...) _TU_NARG(_0, __VA_ARGS__, _RSEQ_N()) +#endif + +#define _TU_NARG(...) _GET_NTH_ARG(__VA_ARGS__) +#define _GET_NTH_ARG( \ + _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,_34,_35,_36,_37,_38,_39,_40, \ + _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ + _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ + _61,_62,_63,N,...) N +#define _RSEQ_N() \ + 62,61,60, \ + 59,58,57,56,55,54,53,52,51,50, \ + 49,48,47,46,45,44,43,42,41,40, \ + 39,38,37,36,35,34,33,32,31,30, \ + 29,28,27,26,25,24,23,22,21,20, \ + 19,18,17,16,15,14,13,12,11,10, \ + 9,8,7,6,5,4,3,2,1,0 + +// Apply an macro X to each of the arguments with an separated of choice +#define TU_ARGS_APPLY(_X, _s, ...) TU_XSTRCAT(_TU_ARGS_APPLY_, TU_ARGS_NUM(__VA_ARGS__))(_X, _s, __VA_ARGS__) + +#define _TU_ARGS_APPLY_1(_X, _s, _a1) _X(_a1) +#define _TU_ARGS_APPLY_2(_X, _s, _a1, _a2) _X(_a1) _s _X(_a2) +#define _TU_ARGS_APPLY_3(_X, _s, _a1, _a2, _a3) _X(_a1) _s _TU_ARGS_APPLY_2(_X, _s, _a2, _a3) +#define _TU_ARGS_APPLY_4(_X, _s, _a1, _a2, _a3, _a4) _X(_a1) _s _TU_ARGS_APPLY_3(_X, _s, _a2, _a3, _a4) +#define _TU_ARGS_APPLY_5(_X, _s, _a1, _a2, _a3, _a4, _a5) _X(_a1) _s _TU_ARGS_APPLY_4(_X, _s, _a2, _a3, _a4, _a5) +#define _TU_ARGS_APPLY_6(_X, _s, _a1, _a2, _a3, _a4, _a5, _a6) _X(_a1) _s _TU_ARGS_APPLY_5(_X, _s, _a2, _a3, _a4, _a5, _a6) +#define _TU_ARGS_APPLY_7(_X, _s, _a1, _a2, _a3, _a4, _a5, _a6, _a7) _X(_a1) _s _TU_ARGS_APPLY_6(_X, _s, _a2, _a3, _a4, _a5, _a6, _a7) +#define _TU_ARGS_APPLY_8(_X, _s, _a1, _a2, _a3, _a4, _a5, _a6, _a7, _a8) _X(_a1) _s _TU_ARGS_APPLY_7(_X, _s, _a2, _a3, _a4, _a5, _a6, _a7, _a8) + +//--------------------------------------------------------------------+ +// Compiler porting with Attribute and Endian +//--------------------------------------------------------------------+ + +// TODO refactor since __attribute__ is supported across many compiler +#if defined(__GNUC__) + #define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes))) + #define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name))) + #define TU_ATTR_PACKED __attribute__ ((packed)) + #define TU_ATTR_WEAK __attribute__ ((weak)) + #ifndef TU_ATTR_ALWAYS_INLINE // allow to override for debug + #define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline)) + #endif + #define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used + #define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused + #define TU_ATTR_USED __attribute__ ((used)) // Function/Variable is meant to be used + + #define TU_ATTR_PACKED_BEGIN + #define TU_ATTR_PACKED_END + #define TU_ATTR_BIT_FIELD_ORDER_BEGIN + #define TU_ATTR_BIT_FIELD_ORDER_END + + #if __GNUC__ < 5 + #define TU_ATTR_FALLTHROUGH do {} while (0) /* fallthrough */ + #else + #if __has_attribute(__fallthrough__) + #define TU_ATTR_FALLTHROUGH __attribute__((fallthrough)) + #else + #define TU_ATTR_FALLTHROUGH do {} while (0) /* fallthrough */ + #endif + #endif + + // Endian conversion use well-known host to network (big endian) naming + #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + #define TU_BYTE_ORDER TU_LITTLE_ENDIAN + #else + #define TU_BYTE_ORDER TU_BIG_ENDIAN + #endif + + // Unfortunately XC16 doesn't provide builtins for 32bit endian conversion + #if defined(__XC16) + #define TU_BSWAP16(u16) (__builtin_swap(u16)) + #define TU_BSWAP32(u32) ((((u32) & 0xff000000) >> 24) | \ + (((u32) & 0x00ff0000) >> 8) | \ + (((u32) & 0x0000ff00) << 8) | \ + (((u32) & 0x000000ff) << 24)) + #else + #define TU_BSWAP16(u16) (__builtin_bswap16(u16)) + #define TU_BSWAP32(u32) (__builtin_bswap32(u32)) + #endif + + #ifndef __ARMCC_VERSION + // List of obsolete callback function that is renamed and should not be defined. + // Put it here since only gcc support this pragma + #pragma GCC poison tud_vendor_control_request_cb + #endif + +#elif defined(__TI_COMPILER_VERSION__) + #define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes))) + #define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name))) + #define TU_ATTR_PACKED __attribute__ ((packed)) + #define TU_ATTR_WEAK __attribute__ ((weak)) + #define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline)) + #define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used + #define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused + #define TU_ATTR_USED __attribute__ ((used)) + #define TU_ATTR_FALLTHROUGH __attribute__((fallthrough)) + + #define TU_ATTR_PACKED_BEGIN + #define TU_ATTR_PACKED_END + #define TU_ATTR_BIT_FIELD_ORDER_BEGIN + #define TU_ATTR_BIT_FIELD_ORDER_END + + // __BYTE_ORDER is defined in the TI ARM compiler, but not MSP430 (which is little endian) + #if ((__BYTE_ORDER__) == (__ORDER_LITTLE_ENDIAN__)) || defined(__MSP430__) + #define TU_BYTE_ORDER TU_LITTLE_ENDIAN + #else + #define TU_BYTE_ORDER TU_BIG_ENDIAN + #endif + + #define TU_BSWAP16(u16) (__builtin_bswap16(u16)) + #define TU_BSWAP32(u32) (__builtin_bswap32(u32)) + +#elif defined(__ICCARM__) + #include + #define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes))) + #define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name))) + #define TU_ATTR_PACKED __attribute__ ((packed)) + #define TU_ATTR_WEAK __attribute__ ((weak)) + #ifndef TU_ATTR_ALWAYS_INLINE // allow to override for debug + #define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline)) + #endif + #define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used + #define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused + #define TU_ATTR_USED __attribute__ ((used)) // Function/Variable is meant to be used + #define TU_ATTR_FALLTHROUGH do {} while (0) /* fallthrough */ + + #define TU_ATTR_PACKED_BEGIN + #define TU_ATTR_PACKED_END + #define TU_ATTR_BIT_FIELD_ORDER_BEGIN + #define TU_ATTR_BIT_FIELD_ORDER_END + + // Endian conversion use well-known host to network (big endian) naming + #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + #define TU_BYTE_ORDER TU_LITTLE_ENDIAN + #else + #define TU_BYTE_ORDER TU_BIG_ENDIAN + #endif + + #define TU_BSWAP16(u16) (__iar_builtin_REV16(u16)) + #define TU_BSWAP32(u32) (__iar_builtin_REV(u32)) + +#elif defined(__CCRX__) + #define TU_ATTR_ALIGNED(Bytes) + #define TU_ATTR_SECTION(sec_name) + #define TU_ATTR_PACKED + #define TU_ATTR_WEAK + #define TU_ATTR_ALWAYS_INLINE + #define TU_ATTR_DEPRECATED(mess) + #define TU_ATTR_UNUSED + #define TU_ATTR_USED + #define TU_ATTR_FALLTHROUGH do {} while (0) /* fallthrough */ + + #define TU_ATTR_PACKED_BEGIN _Pragma("pack") + #define TU_ATTR_PACKED_END _Pragma("packoption") + #define TU_ATTR_BIT_FIELD_ORDER_BEGIN _Pragma("bit_order right") + #define TU_ATTR_BIT_FIELD_ORDER_END _Pragma("bit_order") + + // Endian conversion use well-known host to network (big endian) naming + #if defined(__LIT) + #define TU_BYTE_ORDER TU_LITTLE_ENDIAN + #else + #define TU_BYTE_ORDER TU_BIG_ENDIAN + #endif + + #define TU_BSWAP16(u16) ((unsigned short)_builtin_revw((unsigned long)u16)) + #define TU_BSWAP32(u32) (_builtin_revl(u32)) + +#else + #error "Compiler attribute porting is required" +#endif + + +#if (TU_BYTE_ORDER == TU_LITTLE_ENDIAN) + + #define tu_htons(u16) (TU_BSWAP16(u16)) + #define tu_ntohs(u16) (TU_BSWAP16(u16)) + + #define tu_htonl(u32) (TU_BSWAP32(u32)) + #define tu_ntohl(u32) (TU_BSWAP32(u32)) + + #define tu_htole16(u16) (u16) + #define tu_le16toh(u16) (u16) + + #define tu_htole32(u32) (u32) + #define tu_le32toh(u32) (u32) + +#elif (TU_BYTE_ORDER == TU_BIG_ENDIAN) + + #define tu_htons(u16) (u16) + #define tu_ntohs(u16) (u16) + + #define tu_htonl(u32) (u32) + #define tu_ntohl(u32) (u32) + + #define tu_htole16(u16) (TU_BSWAP16(u16)) + #define tu_le16toh(u16) (TU_BSWAP16(u16)) + + #define tu_htole32(u32) (TU_BSWAP32(u32)) + #define tu_le32toh(u32) (TU_BSWAP32(u32)) + +#else + #error Byte order is undefined +#endif + +#endif /* _TUSB_COMPILER_H_ */ + +/// @} diff --git a/test-devices/composite-stm32/lib/tinyusb/common/tusb_debug.h b/test-devices/composite-stm32/lib/tinyusb/common/tusb_debug.h new file mode 100644 index 00000000..2e9f1d9c --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/common/tusb_debug.h @@ -0,0 +1,171 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022, Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_DEBUG_H_ +#define _TUSB_DEBUG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Debug +//--------------------------------------------------------------------+ + +// CFG_TUSB_DEBUG for debugging +// 0 : no debug +// 1 : print error +// 2 : print warning +// 3 : print info +#if CFG_TUSB_DEBUG + +// Enum to String for debugging purposes +#if CFG_TUSB_DEBUG >= CFG_TUH_LOG_LEVEL || CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL +extern char const* const tu_str_speed[]; +extern char const* const tu_str_std_request[]; +extern char const* const tu_str_xfer_result[]; +#endif + +void tu_print_mem(void const *buf, uint32_t count, uint8_t indent); + +#ifdef CFG_TUSB_DEBUG_PRINTF + extern int CFG_TUSB_DEBUG_PRINTF(const char *format, ...); + #define tu_printf CFG_TUSB_DEBUG_PRINTF +#else + #define tu_printf printf +#endif + +static inline void tu_print_buf(uint8_t const* buf, uint32_t bufsize) { + for(uint32_t i=0; i= 2 + #define TU_LOG2 TU_LOG1 + #define TU_LOG2_MEM TU_LOG1_MEM + #define TU_LOG2_BUF TU_LOG1_BUF + #define TU_LOG2_INT TU_LOG1_INT + #define TU_LOG2_HEX TU_LOG1_HEX +#endif + +// Log Level 3: Info +#if CFG_TUSB_DEBUG >= 3 + #define TU_LOG3 TU_LOG1 + #define TU_LOG3_MEM TU_LOG1_MEM + #define TU_LOG3_BUF TU_LOG1_BUF + #define TU_LOG3_INT TU_LOG1_INT + #define TU_LOG3_HEX TU_LOG1_HEX +#endif + +typedef struct { + uint32_t key; + const char* data; +} tu_lookup_entry_t; + +typedef struct { + uint16_t count; + tu_lookup_entry_t const* items; +} tu_lookup_table_t; + +static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint32_t key) { + tu_static char not_found[11]; + + for(uint16_t i=0; icount; i++) { + if (p_table->items[i].key == key) return p_table->items[i].data; + } + + // not found return the key value in hex + snprintf(not_found, sizeof(not_found), "0x%08lX", (unsigned long) key); + + return not_found; +} + +#endif // CFG_TUSB_DEBUG + +#ifndef TU_LOG + #define TU_LOG(n, ...) + #define TU_LOG_MEM(n, ...) + #define TU_LOG_BUF(n, ...) + #define TU_LOG_INT(n, ...) + #define TU_LOG_HEX(n, ...) + #define TU_LOG_LOCATION() + #define TU_LOG_FAILED() +#endif + +// TODO replace all TU_LOGn with TU_LOG(n) + +#define TU_LOG0(...) +#define TU_LOG0_MEM(...) +#define TU_LOG0_BUF(...) +#define TU_LOG0_INT(...) +#define TU_LOG0_HEX(...) + +#ifndef TU_LOG1 + #define TU_LOG1(...) + #define TU_LOG1_MEM(...) + #define TU_LOG1_BUF(...) + #define TU_LOG1_INT(...) + #define TU_LOG1_HEX(...) +#endif + +#ifndef TU_LOG2 + #define TU_LOG2(...) + #define TU_LOG2_MEM(...) + #define TU_LOG2_BUF(...) + #define TU_LOG2_INT(...) + #define TU_LOG2_HEX(...) +#endif + +#ifndef TU_LOG3 + #define TU_LOG3(...) + #define TU_LOG3_MEM(...) + #define TU_LOG3_BUF(...) + #define TU_LOG3_INT(...) + #define TU_LOG3_HEX(...) +#endif + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_DEBUG_H_ */ diff --git a/test-devices/composite-stm32/lib/tinyusb/common/tusb_fifo.c b/test-devices/composite-stm32/lib/tinyusb/common/tusb_fifo.c new file mode 100644 index 00000000..76696396 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/common/tusb_fifo.c @@ -0,0 +1,1066 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * Copyright (c) 2020 Reinhard Panhuber - rework to unmasked pointers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "osal/osal.h" +#include "tusb_fifo.h" + +#define TU_FIFO_DBG 0 + +// Suppress IAR warning +// Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this statement +#if defined(__ICCARM__) +#pragma diag_suppress = Pa082 +#endif + +#if OSAL_MUTEX_REQUIRED + +TU_ATTR_ALWAYS_INLINE static inline void _ff_lock(osal_mutex_t mutex) +{ + if (mutex) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER); +} + +TU_ATTR_ALWAYS_INLINE static inline void _ff_unlock(osal_mutex_t mutex) +{ + if (mutex) osal_mutex_unlock(mutex); +} + +#else + +#define _ff_lock(_mutex) +#define _ff_unlock(_mutex) + +#endif + +/** \enum tu_fifo_copy_mode_t + * \brief Write modes intended to allow special read and write functions to be able to + * copy data to and from USB hardware FIFOs as needed for e.g. STM32s and others + */ +typedef enum +{ + TU_FIFO_COPY_INC, ///< Copy from/to an increasing source/destination address - default mode + TU_FIFO_COPY_CST_FULL_WORDS, ///< Copy from/to a constant source/destination address - required for e.g. STM32 to write into USB hardware FIFO +} tu_fifo_copy_mode_t; + +bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable) +{ + // Limit index space to 2*depth - this allows for a fast "modulo" calculation + // but limits the maximum depth to 2^16/2 = 2^15 and buffer overflows are detectable + // only if overflow happens once (important for unsupervised DMA applications) + if (depth > 0x8000) return false; + + _ff_lock(f->mutex_wr); + _ff_lock(f->mutex_rd); + + f->buffer = (uint8_t*) buffer; + f->depth = depth; + f->item_size = (uint16_t) (item_size & 0x7FFF); + f->overwritable = overwritable; + f->rd_idx = 0; + f->wr_idx = 0; + + _ff_unlock(f->mutex_wr); + _ff_unlock(f->mutex_rd); + + return true; +} + +//--------------------------------------------------------------------+ +// Pull & Push +//--------------------------------------------------------------------+ + +// Intended to be used to read from hardware USB FIFO in e.g. STM32 where all data is read from a constant address +// Code adapted from dcd_synopsys.c +// TODO generalize with configurable 1 byte or 4 byte each read +static void _ff_push_const_addr(uint8_t * ff_buf, const void * app_buf, uint16_t len) +{ + volatile const uint32_t * reg_rx = (volatile const uint32_t *) app_buf; + + // Reading full available 32 bit words from const app address + uint16_t full_words = len >> 2; + while(full_words--) + { + tu_unaligned_write32(ff_buf, *reg_rx); + ff_buf += 4; + } + + // Read the remaining 1-3 bytes from const app address + uint8_t const bytes_rem = len & 0x03; + if ( bytes_rem ) + { + uint32_t tmp32 = *reg_rx; + memcpy(ff_buf, &tmp32, bytes_rem); + } +} + +// Intended to be used to write to hardware USB FIFO in e.g. STM32 +// where all data is written to a constant address in full word copies +static void _ff_pull_const_addr(void * app_buf, const uint8_t * ff_buf, uint16_t len) +{ + volatile uint32_t * reg_tx = (volatile uint32_t *) app_buf; + + // Write full available 32 bit words to const address + uint16_t full_words = len >> 2; + while(full_words--) + { + *reg_tx = tu_unaligned_read32(ff_buf); + ff_buf += 4; + } + + // Write the remaining 1-3 bytes into const address + uint8_t const bytes_rem = len & 0x03; + if ( bytes_rem ) + { + uint32_t tmp32 = 0; + memcpy(&tmp32, ff_buf, bytes_rem); + + *reg_tx = tmp32; + } +} + +// send one item to fifo WITHOUT updating write pointer +static inline void _ff_push(tu_fifo_t* f, void const * app_buf, uint16_t rel) +{ + memcpy(f->buffer + (rel * f->item_size), app_buf, f->item_size); +} + +// send n items to fifo WITHOUT updating write pointer +static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t wr_ptr, tu_fifo_copy_mode_t copy_mode) +{ + uint16_t const lin_count = f->depth - wr_ptr; + uint16_t const wrap_count = n - lin_count; + + uint16_t lin_bytes = lin_count * f->item_size; + uint16_t wrap_bytes = wrap_count * f->item_size; + + // current buffer of fifo + uint8_t* ff_buf = f->buffer + (wr_ptr * f->item_size); + + switch (copy_mode) + { + case TU_FIFO_COPY_INC: + if(n <= lin_count) + { + // Linear only + memcpy(ff_buf, app_buf, n*f->item_size); + } + else + { + // Wrap around + + // Write data to linear part of buffer + memcpy(ff_buf, app_buf, lin_bytes); + + // Write data wrapped around + // TU_ASSERT(nWrap_bytes <= f->depth, ); + memcpy(f->buffer, ((uint8_t const*) app_buf) + lin_bytes, wrap_bytes); + } + break; + + case TU_FIFO_COPY_CST_FULL_WORDS: + // Intended for hardware buffers from which it can be read word by word only + if(n <= lin_count) + { + // Linear only + _ff_push_const_addr(ff_buf, app_buf, n*f->item_size); + } + else + { + // Wrap around case + + // Write full words to linear part of buffer + uint16_t nLin_4n_bytes = lin_bytes & 0xFFFC; + _ff_push_const_addr(ff_buf, app_buf, nLin_4n_bytes); + ff_buf += nLin_4n_bytes; + + // There could be odd 1-3 bytes before the wrap-around boundary + uint8_t rem = lin_bytes & 0x03; + if (rem > 0) + { + volatile const uint32_t * rx_fifo = (volatile const uint32_t *) app_buf; + + uint8_t remrem = (uint8_t) tu_min16(wrap_bytes, 4-rem); + wrap_bytes -= remrem; + + uint32_t tmp32 = *rx_fifo; + uint8_t * src_u8 = ((uint8_t *) &tmp32); + + // Write 1-3 bytes before wrapped boundary + while(rem--) *ff_buf++ = *src_u8++; + + // Read more bytes to beginning to complete a word + ff_buf = f->buffer; + while(remrem--) *ff_buf++ = *src_u8++; + } + else + { + ff_buf = f->buffer; // wrap around to beginning + } + + // Write data wrapped part + if (wrap_bytes > 0) _ff_push_const_addr(ff_buf, app_buf, wrap_bytes); + } + break; + default: break; + } +} + +// get one item from fifo WITHOUT updating read pointer +static inline void _ff_pull(tu_fifo_t* f, void * app_buf, uint16_t rel) +{ + memcpy(app_buf, f->buffer + (rel * f->item_size), f->item_size); +} + +// get n items from fifo WITHOUT updating read pointer +static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rd_ptr, tu_fifo_copy_mode_t copy_mode) +{ + uint16_t const lin_count = f->depth - rd_ptr; + uint16_t const wrap_count = n - lin_count; // only used if wrapped + + uint16_t lin_bytes = lin_count * f->item_size; + uint16_t wrap_bytes = wrap_count * f->item_size; + + // current buffer of fifo + uint8_t* ff_buf = f->buffer + (rd_ptr * f->item_size); + + switch (copy_mode) + { + case TU_FIFO_COPY_INC: + if ( n <= lin_count ) + { + // Linear only + memcpy(app_buf, ff_buf, n*f->item_size); + } + else + { + // Wrap around + + // Read data from linear part of buffer + memcpy(app_buf, ff_buf, lin_bytes); + + // Read data wrapped part + memcpy((uint8_t*) app_buf + lin_bytes, f->buffer, wrap_bytes); + } + break; + + case TU_FIFO_COPY_CST_FULL_WORDS: + if ( n <= lin_count ) + { + // Linear only + _ff_pull_const_addr(app_buf, ff_buf, n*f->item_size); + } + else + { + // Wrap around case + + // Read full words from linear part of buffer + uint16_t lin_4n_bytes = lin_bytes & 0xFFFC; + _ff_pull_const_addr(app_buf, ff_buf, lin_4n_bytes); + ff_buf += lin_4n_bytes; + + // There could be odd 1-3 bytes before the wrap-around boundary + uint8_t rem = lin_bytes & 0x03; + if (rem > 0) + { + volatile uint32_t * reg_tx = (volatile uint32_t *) app_buf; + + uint8_t remrem = (uint8_t) tu_min16(wrap_bytes, 4-rem); + wrap_bytes -= remrem; + + uint32_t tmp32=0; + uint8_t * dst_u8 = (uint8_t *)&tmp32; + + // Read 1-3 bytes before wrapped boundary + while(rem--) *dst_u8++ = *ff_buf++; + + // Read more bytes from beginning to complete a word + ff_buf = f->buffer; + while(remrem--) *dst_u8++ = *ff_buf++; + + *reg_tx = tmp32; + } + else + { + ff_buf = f->buffer; // wrap around to beginning + } + + // Read data wrapped part + if (wrap_bytes > 0) _ff_pull_const_addr(app_buf, ff_buf, wrap_bytes); + } + break; + + default: break; + } +} + +//--------------------------------------------------------------------+ +// Helper +//--------------------------------------------------------------------+ + +// return only the index difference and as such can be used to determine an overflow i.e overflowable count +TU_ATTR_ALWAYS_INLINE static inline +uint16_t _ff_count(uint16_t depth, uint16_t wr_idx, uint16_t rd_idx) +{ + // In case we have non-power of two depth we need a further modification + if (wr_idx >= rd_idx) + { + return (uint16_t) (wr_idx - rd_idx); + } else + { + return (uint16_t) (2*depth - (rd_idx - wr_idx)); + } +} + +// return remaining slot in fifo +TU_ATTR_ALWAYS_INLINE static inline +uint16_t _ff_remaining(uint16_t depth, uint16_t wr_idx, uint16_t rd_idx) +{ + uint16_t const count = _ff_count(depth, wr_idx, rd_idx); + return (depth > count) ? (depth - count) : 0; +} + +//--------------------------------------------------------------------+ +// Index Helper +//--------------------------------------------------------------------+ + +// Advance an absolute index +// "absolute" index is only in the range of [0..2*depth) +static uint16_t advance_index(uint16_t depth, uint16_t idx, uint16_t offset) +{ + // We limit the index space of p such that a correct wrap around happens + // Check for a wrap around or if we are in unused index space - This has to be checked first!! + // We are exploiting the wrap around to the correct index + uint16_t new_idx = (uint16_t) (idx + offset); + if ( (idx > new_idx) || (new_idx >= 2*depth) ) + { + uint16_t const non_used_index_space = (uint16_t) (UINT16_MAX - (2*depth-1)); + new_idx = (uint16_t) (new_idx + non_used_index_space); + } + + return new_idx; +} + +#if 0 // not used but +// Backward an absolute index +static uint16_t backward_index(uint16_t depth, uint16_t idx, uint16_t offset) +{ + // We limit the index space of p such that a correct wrap around happens + // Check for a wrap around or if we are in unused index space - This has to be checked first!! + // We are exploiting the wrap around to the correct index + uint16_t new_idx = (uint16_t) (idx - offset); + if ( (idx < new_idx) || (new_idx >= 2*depth) ) + { + uint16_t const non_used_index_space = (uint16_t) (UINT16_MAX - (2*depth-1)); + new_idx = (uint16_t) (new_idx - non_used_index_space); + } + + return new_idx; +} +#endif + +// index to pointer, simply an modulo with minus. +TU_ATTR_ALWAYS_INLINE static inline +uint16_t idx2ptr(uint16_t depth, uint16_t idx) +{ + // Only run at most 3 times since index is limit in the range of [0..2*depth) + while ( idx >= depth ) idx -= depth; + return idx; +} + +// Works on local copies of w +// When an overwritable fifo is overflowed, rd_idx will be re-index so that it forms +// an full fifo i.e _ff_count() = depth +TU_ATTR_ALWAYS_INLINE static inline +uint16_t _ff_correct_read_index(tu_fifo_t* f, uint16_t wr_idx) +{ + uint16_t rd_idx; + if ( wr_idx >= f->depth ) + { + rd_idx = wr_idx - f->depth; + }else + { + rd_idx = wr_idx + f->depth; + } + + f->rd_idx = rd_idx; + + return rd_idx; +} + +// Works on local copies of w and r +// Must be protected by mutexes since in case of an overflow read pointer gets modified +static bool _tu_fifo_peek(tu_fifo_t* f, void * p_buffer, uint16_t wr_idx, uint16_t rd_idx) +{ + uint16_t cnt = _ff_count(f->depth, wr_idx, rd_idx); + + // nothing to peek + if ( cnt == 0 ) return false; + + // Check overflow and correct if required + if ( cnt > f->depth ) + { + rd_idx = _ff_correct_read_index(f, wr_idx); + cnt = f->depth; + } + + uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); + + // Peek data + _ff_pull(f, p_buffer, rd_ptr); + + return true; +} + +// Works on local copies of w and r +// Must be protected by mutexes since in case of an overflow read pointer gets modified +static uint16_t _tu_fifo_peek_n(tu_fifo_t* f, void * p_buffer, uint16_t n, uint16_t wr_idx, uint16_t rd_idx, tu_fifo_copy_mode_t copy_mode) +{ + uint16_t cnt = _ff_count(f->depth, wr_idx, rd_idx); + + // nothing to peek + if ( cnt == 0 ) return 0; + + // Check overflow and correct if required + if ( cnt > f->depth ) + { + rd_idx = _ff_correct_read_index(f, wr_idx); + cnt = f->depth; + } + + // Check if we can read something at and after offset - if too less is available we read what remains + if ( cnt < n ) n = cnt; + + uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); + + // Peek data + _ff_pull_n(f, p_buffer, n, rd_ptr, copy_mode); + + return n; +} + +static uint16_t _tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n, tu_fifo_copy_mode_t copy_mode) +{ + if ( n == 0 ) return 0; + + _ff_lock(f->mutex_wr); + + uint16_t wr_idx = f->wr_idx; + uint16_t rd_idx = f->rd_idx; + + uint8_t const* buf8 = (uint8_t const*) data; + + TU_LOG(TU_FIFO_DBG, "rd = %3u, wr = %3u, count = %3u, remain = %3u, n = %3u: ", + rd_idx, wr_idx, _ff_count(f->depth, wr_idx, rd_idx), _ff_remaining(f->depth, wr_idx, rd_idx), n); + + if ( !f->overwritable ) + { + // limit up to full + uint16_t const remain = _ff_remaining(f->depth, wr_idx, rd_idx); + n = tu_min16(n, remain); + } + else + { + // In over-writable mode, fifo_write() is allowed even when fifo is full. In such case, + // oldest data in fifo i.e at read pointer data will be overwritten + // Note: we can modify read buffer contents but we must not modify the read index itself within a write function! + // Since it would end up in a race condition with read functions! + if ( n >= f->depth ) + { + // Only copy last part + if ( copy_mode == TU_FIFO_COPY_INC ) + { + buf8 += (n - f->depth) * f->item_size; + }else + { + // TODO should read from hw fifo to discard data, however reading an odd number could + // accidentally discard data. + } + + n = f->depth; + + // We start writing at the read pointer's position since we fill the whole buffer + wr_idx = rd_idx; + } + else + { + uint16_t const overflowable_count = _ff_count(f->depth, wr_idx, rd_idx); + if (overflowable_count + n >= 2*f->depth) + { + // Double overflowed + // Index is bigger than the allowed range [0,2*depth) + // re-position write index to have a full fifo after pushed + wr_idx = advance_index(f->depth, rd_idx, f->depth - n); + + // TODO we should also shift out n bytes from read index since we avoid changing rd index !! + // However memmove() is expensive due to actual copying + wrapping consideration. + // Also race condition could happen anyway if read() is invoke while moving result in corrupted memory + // currently deliberately not implemented --> result in incorrect data read back + }else + { + // normal + single overflowed: + // Index is in the range of [0,2*depth) and thus detect and recoverable. Recovering is handled in read() + // Therefore we just increase write index + // we will correct (re-position) read index later on in fifo_read() function + } + } + } + + if (n) + { + uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); + + TU_LOG(TU_FIFO_DBG, "actual_n = %u, wr_ptr = %u", n, wr_ptr); + + // Write data + _ff_push_n(f, buf8, n, wr_ptr, copy_mode); + + // Advance index + f->wr_idx = advance_index(f->depth, wr_idx, n); + + TU_LOG(TU_FIFO_DBG, "\tnew_wr = %u\r\n", f->wr_idx); + } + + _ff_unlock(f->mutex_wr); + + return n; +} + +static uint16_t _tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n, tu_fifo_copy_mode_t copy_mode) +{ + _ff_lock(f->mutex_rd); + + // Peek the data + // f->rd_idx might get modified in case of an overflow so we can not use a local variable + n = _tu_fifo_peek_n(f, buffer, n, f->wr_idx, f->rd_idx, copy_mode); + + // Advance read pointer + f->rd_idx = advance_index(f->depth, f->rd_idx, n); + + _ff_unlock(f->mutex_rd); + return n; +} + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ + +/******************************************************************************/ +/*! + @brief Get number of items in FIFO. + + As this function only reads the read and write pointers once, this function is + reentrant and thus thread and ISR save without any mutexes. In case an + overflow occurred, this function return f.depth at maximum. Overflows are + checked and corrected for in the read functions! + + @param[in] f + Pointer to the FIFO buffer to manipulate + + @returns Number of items in FIFO + */ +/******************************************************************************/ +uint16_t tu_fifo_count(tu_fifo_t* f) +{ + return tu_min16(_ff_count(f->depth, f->wr_idx, f->rd_idx), f->depth); +} + +/******************************************************************************/ +/*! + @brief Check if FIFO is empty. + + As this function only reads the read and write pointers once, this function is + reentrant and thus thread and ISR save without any mutexes. + + @param[in] f + Pointer to the FIFO buffer to manipulate + + @returns Number of items in FIFO + */ +/******************************************************************************/ +bool tu_fifo_empty(tu_fifo_t* f) +{ + return f->wr_idx == f->rd_idx; +} + +/******************************************************************************/ +/*! + @brief Check if FIFO is full. + + As this function only reads the read and write pointers once, this function is + reentrant and thus thread and ISR save without any mutexes. + + @param[in] f + Pointer to the FIFO buffer to manipulate + + @returns Number of items in FIFO + */ +/******************************************************************************/ +bool tu_fifo_full(tu_fifo_t* f) +{ + return _ff_count(f->depth, f->wr_idx, f->rd_idx) >= f->depth; +} + +/******************************************************************************/ +/*! + @brief Get remaining space in FIFO. + + As this function only reads the read and write pointers once, this function is + reentrant and thus thread and ISR save without any mutexes. + + @param[in] f + Pointer to the FIFO buffer to manipulate + + @returns Number of items in FIFO + */ +/******************************************************************************/ +uint16_t tu_fifo_remaining(tu_fifo_t* f) +{ + return _ff_remaining(f->depth, f->wr_idx, f->rd_idx); +} + +/******************************************************************************/ +/*! + @brief Check if overflow happened. + + BE AWARE - THIS FUNCTION MIGHT NOT GIVE A CORRECT ANSWERE IN CASE WRITE POINTER "OVERFLOWS" + Only one overflow is allowed for this function to work e.g. if depth = 100, you must not + write more than 2*depth-1 items in one rush without updating write pointer. Otherwise + write pointer wraps and your pointer states are messed up. This can only happen if you + use DMAs, write functions do not allow such an error. Avoid such nasty things! + + All reading functions (read, peek) check for overflows and correct read pointer on their own such + that latest items are read. + If required (e.g. for DMA use) you can also correct the read pointer by + tu_fifo_correct_read_pointer(). + + @param[in] f + Pointer to the FIFO buffer to manipulate + + @returns True if overflow happened + */ +/******************************************************************************/ +bool tu_fifo_overflowed(tu_fifo_t* f) +{ + return _ff_count(f->depth, f->wr_idx, f->rd_idx) > f->depth; +} + +// Only use in case tu_fifo_overflow() returned true! +void tu_fifo_correct_read_pointer(tu_fifo_t* f) +{ + _ff_lock(f->mutex_rd); + _ff_correct_read_index(f, f->wr_idx); + _ff_unlock(f->mutex_rd); +} + +/******************************************************************************/ +/*! + @brief Read one element out of the buffer. + + This function will return the element located at the array index of the + read pointer, and then increment the read pointer index. + This function checks for an overflow and corrects read pointer if required. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] buffer + Pointer to the place holder for data read from the buffer + + @returns TRUE if the queue is not empty + */ +/******************************************************************************/ +bool tu_fifo_read(tu_fifo_t* f, void * buffer) +{ + _ff_lock(f->mutex_rd); + + // Peek the data + // f->rd_idx might get modified in case of an overflow so we can not use a local variable + bool ret = _tu_fifo_peek(f, buffer, f->wr_idx, f->rd_idx); + + // Advance pointer + f->rd_idx = advance_index(f->depth, f->rd_idx, ret); + + _ff_unlock(f->mutex_rd); + return ret; +} + +/******************************************************************************/ +/*! + @brief This function will read n elements from the array index specified by + the read pointer and increment the read index. + This function checks for an overflow and corrects read pointer if required. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] buffer + The pointer to data location + @param[in] n + Number of element that buffer can afford + + @returns number of items read from the FIFO + */ +/******************************************************************************/ +uint16_t tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n) +{ + return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_INC); +} + +uint16_t tu_fifo_read_n_const_addr_full_words(tu_fifo_t* f, void * buffer, uint16_t n) +{ + return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_CST_FULL_WORDS); +} + +/******************************************************************************/ +/*! + @brief Read one item without removing it from the FIFO. + This function checks for an overflow and corrects read pointer if required. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] p_buffer + Pointer to the place holder for data read from the buffer + + @returns TRUE if the queue is not empty + */ +/******************************************************************************/ +bool tu_fifo_peek(tu_fifo_t* f, void * p_buffer) +{ + _ff_lock(f->mutex_rd); + bool ret = _tu_fifo_peek(f, p_buffer, f->wr_idx, f->rd_idx); + _ff_unlock(f->mutex_rd); + return ret; +} + +/******************************************************************************/ +/*! + @brief Read n items without removing it from the FIFO + This function checks for an overflow and corrects read pointer if required. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] p_buffer + Pointer to the place holder for data read from the buffer + @param[in] n + Number of items to peek + + @returns Number of bytes written to p_buffer + */ +/******************************************************************************/ +uint16_t tu_fifo_peek_n(tu_fifo_t* f, void * p_buffer, uint16_t n) +{ + _ff_lock(f->mutex_rd); + uint16_t ret = _tu_fifo_peek_n(f, p_buffer, n, f->wr_idx, f->rd_idx, TU_FIFO_COPY_INC); + _ff_unlock(f->mutex_rd); + return ret; +} + +/******************************************************************************/ +/*! + @brief Write one element into the buffer. + + This function will write one element into the array index specified by + the write pointer and increment the write index. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] data + The byte to add to the FIFO + + @returns TRUE if the data was written to the FIFO (overwrittable + FIFO will always return TRUE) + */ +/******************************************************************************/ +bool tu_fifo_write(tu_fifo_t* f, const void * data) +{ + _ff_lock(f->mutex_wr); + + bool ret; + uint16_t const wr_idx = f->wr_idx; + + if ( tu_fifo_full(f) && !f->overwritable ) + { + ret = false; + }else + { + uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); + + // Write data + _ff_push(f, data, wr_ptr); + + // Advance pointer + f->wr_idx = advance_index(f->depth, wr_idx, 1); + + ret = true; + } + + _ff_unlock(f->mutex_wr); + + return ret; +} + +/******************************************************************************/ +/*! + @brief This function will write n elements into the array index specified by + the write pointer and increment the write index. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] data + The pointer to data to add to the FIFO + @param[in] count + Number of element + @return Number of written elements + */ +/******************************************************************************/ +uint16_t tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n) +{ + return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_INC); +} + +/******************************************************************************/ +/*! + @brief This function will write n elements into the array index specified by + the write pointer and increment the write index. The source address will + not be incremented which is useful for reading from registers. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] data + The pointer to data to add to the FIFO + @param[in] count + Number of element + @return Number of written elements + */ +/******************************************************************************/ +uint16_t tu_fifo_write_n_const_addr_full_words(tu_fifo_t* f, const void * data, uint16_t n) +{ + return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_CST_FULL_WORDS); +} + +/******************************************************************************/ +/*! + @brief Clear the fifo read and write pointers + + @param[in] f + Pointer to the FIFO buffer to manipulate + */ +/******************************************************************************/ +bool tu_fifo_clear(tu_fifo_t *f) +{ + _ff_lock(f->mutex_wr); + _ff_lock(f->mutex_rd); + + f->rd_idx = 0; + f->wr_idx = 0; + + _ff_unlock(f->mutex_wr); + _ff_unlock(f->mutex_rd); + return true; +} + +/******************************************************************************/ +/*! + @brief Change the fifo mode to overwritable or not overwritable + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] overwritable + Overwritable mode the fifo is set to + */ +/******************************************************************************/ +bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable) +{ + _ff_lock(f->mutex_wr); + _ff_lock(f->mutex_rd); + + f->overwritable = overwritable; + + _ff_unlock(f->mutex_wr); + _ff_unlock(f->mutex_rd); + + return true; +} + +/******************************************************************************/ +/*! + @brief Advance write pointer - intended to be used in combination with DMA. + It is possible to fill the FIFO by use of a DMA in circular mode. Within + DMA ISRs you may update the write pointer to be able to read from the FIFO. + As long as the DMA is the only process writing into the FIFO this is safe + to use. + + USE WITH CARE - WE DO NOT CONDUCT SAFETY CHECKS HERE! + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] n + Number of items the write pointer moves forward + */ +/******************************************************************************/ +void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n) +{ + f->wr_idx = advance_index(f->depth, f->wr_idx, n); +} + +/******************************************************************************/ +/*! + @brief Advance read pointer - intended to be used in combination with DMA. + It is possible to read from the FIFO by use of a DMA in linear mode. Within + DMA ISRs you may update the read pointer to be able to again write into the + FIFO. As long as the DMA is the only process reading from the FIFO this is + safe to use. + + USE WITH CARE - WE DO NOT CONDUCT SAFETY CHECKS HERE! + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] n + Number of items the read pointer moves forward + */ +/******************************************************************************/ +void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n) +{ + f->rd_idx = advance_index(f->depth, f->rd_idx, n); +} + +/******************************************************************************/ +/*! + @brief Get read info + + Returns the length and pointer from which bytes can be read in a linear manner. + This is of major interest for DMA transmissions. If returned length is zero the + corresponding pointer is invalid. + The read pointer does NOT get advanced, use tu_fifo_advance_read_pointer() to + do so! + @param[in] f + Pointer to FIFO + @param[out] *info + Pointer to struct which holds the desired infos + */ +/******************************************************************************/ +void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) +{ + // Operate on temporary values in case they change in between + uint16_t wr_idx = f->wr_idx; + uint16_t rd_idx = f->rd_idx; + + uint16_t cnt = _ff_count(f->depth, wr_idx, rd_idx); + + // Check overflow and correct if required - may happen in case a DMA wrote too fast + if (cnt > f->depth) + { + _ff_lock(f->mutex_rd); + rd_idx = _ff_correct_read_index(f, wr_idx); + _ff_unlock(f->mutex_rd); + + cnt = f->depth; + } + + // Check if fifo is empty + if (cnt == 0) + { + info->len_lin = 0; + info->len_wrap = 0; + info->ptr_lin = NULL; + info->ptr_wrap = NULL; + return; + } + + // Get relative pointers + uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); + uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); + + // Copy pointer to buffer to start reading from + info->ptr_lin = &f->buffer[rd_ptr]; + + // Check if there is a wrap around necessary + if (wr_ptr > rd_ptr) + { + // Non wrapping case + info->len_lin = cnt; + + info->len_wrap = 0; + info->ptr_wrap = NULL; + } + else + { + info->len_lin = f->depth - rd_ptr; // Also the case if FIFO was full + + info->len_wrap = cnt - info->len_lin; + info->ptr_wrap = f->buffer; + } +} + +/******************************************************************************/ +/*! + @brief Get linear write info + + Returns the length and pointer to which bytes can be written into FIFO in a linear manner. + This is of major interest for DMA transmissions not using circular mode. If a returned length is zero the + corresponding pointer is invalid. The returned lengths summed up are the currently free space in the FIFO. + The write pointer does NOT get advanced, use tu_fifo_advance_write_pointer() to do so! + TAKE CARE TO NOT OVERFLOW THE BUFFER MORE THAN TWO TIMES THE FIFO DEPTH - IT CAN NOT RECOVERE OTHERWISE! + @param[in] f + Pointer to FIFO + @param[out] *info + Pointer to struct which holds the desired infos + */ +/******************************************************************************/ +void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) +{ + uint16_t wr_idx = f->wr_idx; + uint16_t rd_idx = f->rd_idx; + uint16_t remain = _ff_remaining(f->depth, wr_idx, rd_idx); + + if (remain == 0) + { + info->len_lin = 0; + info->len_wrap = 0; + info->ptr_lin = NULL; + info->ptr_wrap = NULL; + return; + } + + // Get relative pointers + uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); + uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); + + // Copy pointer to buffer to start writing to + info->ptr_lin = &f->buffer[wr_ptr]; + + if (wr_ptr < rd_ptr) + { + // Non wrapping case + info->len_lin = rd_ptr-wr_ptr; + info->len_wrap = 0; + info->ptr_wrap = NULL; + } + else + { + info->len_lin = f->depth - wr_ptr; + info->len_wrap = remain - info->len_lin; // Remaining length - n already was limited to remain or FIFO depth + info->ptr_wrap = f->buffer; // Always start of buffer + } +} diff --git a/test-devices/composite-stm32/lib/tinyusb/common/tusb_fifo.h b/test-devices/composite-stm32/lib/tinyusb/common/tusb_fifo.h new file mode 100644 index 00000000..2d9f5e66 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/common/tusb_fifo.h @@ -0,0 +1,195 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * Copyright (c) 2020 Reinhard Panhuber - rework to unmasked pointers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_FIFO_H_ +#define _TUSB_FIFO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// Due to the use of unmasked pointers, this FIFO does not suffer from losing +// one item slice. Furthermore, write and read operations are completely +// decoupled as write and read functions do not modify a common state. Henceforth, +// writing or reading from the FIFO within an ISR is safe as long as no other +// process (thread or ISR) interferes. +// Also, this FIFO is ready to be used in combination with a DMA as the write and +// read pointers can be updated from within a DMA ISR. Overflows are detectable +// within a certain number (see tu_fifo_overflow()). + +#include "common/tusb_common.h" +#include "osal/osal.h" + +// mutex is only needed for RTOS +// for OS None, we don't get preempted +#define CFG_FIFO_MUTEX OSAL_MUTEX_REQUIRED + +/* Write/Read index is always in the range of: + * 0 .. 2*depth-1 + * The extra window allow us to determine the fifo state of empty or full with only 2 indices + * Following are examples with depth = 3 + * + * - empty: W = R + * | + * ------------------------- + * | 0 | RW| 2 | 3 | 4 | 5 | + * + * - full 1: W > R + * | + * ------------------------- + * | 0 | R | 2 | 3 | W | 5 | + * + * - full 2: W < R + * | + * ------------------------- + * | 0 | 1 | W | 3 | 4 | R | + * + * - Number of items in the fifo can be determined in either cases: + * - case W >= R: Count = W - R + * - case W < R: Count = 2*depth - (R - W) + * + * In non-overwritable mode, computed Count (in above 2 cases) is at most equal to depth. + * However, in over-writable mode, write index can be repeatedly increased and count can be + * temporarily larger than depth (overflowed condition) e.g + * + * - Overflowed 1: write(3), write(1) + * In this case we will adjust Read index when read()/peek() is called so that count = depth. + * | + * ------------------------- + * | R | 1 | 2 | 3 | W | 5 | + * + * - Double Overflowed i.e index is out of allowed range [0,2*depth) + * This occurs when we continue to write after 1st overflowed to 2nd overflowed. e.g: + * write(3), write(1), write(2) + * This must be prevented since it will cause unrecoverable state, in above example + * if not handled the fifo will be empty instead of continue-to-be full. Since we must not modify + * read index in write() function, which cause race condition. We will re-position write index so that + * after data is written it is a full fifo i.e W = depth - R + * + * re-position W = 1 before write(2) + * Note: we should also move data from mem[3] to read index as well, but deliberately skipped here + * since it is an expensive operation !!! + * | + * ------------------------- + * | R | W | 2 | 3 | 4 | 5 | + * + * perform write(2), result is still a full fifo. + * + * | + * ------------------------- + * | R | 1 | 2 | W | 4 | 5 | + */ +typedef struct { + uint8_t* buffer ; // buffer pointer + uint16_t depth ; // max items + + struct TU_ATTR_PACKED { + uint16_t item_size : 15; // size of each item + bool overwritable : 1 ; // ovwerwritable when full + }; + + volatile uint16_t wr_idx ; // write index + volatile uint16_t rd_idx ; // read index + +#if OSAL_MUTEX_REQUIRED + osal_mutex_t mutex_wr; + osal_mutex_t mutex_rd; +#endif + +} tu_fifo_t; + +typedef struct { + uint16_t len_lin ; ///< linear length in item size + uint16_t len_wrap ; ///< wrapped length in item size + void * ptr_lin ; ///< linear part start pointer + void * ptr_wrap ; ///< wrapped part start pointer +} tu_fifo_buffer_info_t; + +#define TU_FIFO_INIT(_buffer, _depth, _type, _overwritable){\ + .buffer = _buffer, \ + .depth = _depth, \ + .item_size = sizeof(_type), \ + .overwritable = _overwritable, \ +} + +#define TU_FIFO_DEF(_name, _depth, _type, _overwritable) \ + uint8_t _name##_buf[_depth*sizeof(_type)]; \ + tu_fifo_t _name = TU_FIFO_INIT(_name##_buf, _depth, _type, _overwritable) + +bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable); +bool tu_fifo_clear(tu_fifo_t *f); +bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable); + +#if OSAL_MUTEX_REQUIRED + TU_ATTR_ALWAYS_INLINE static inline + void tu_fifo_config_mutex(tu_fifo_t *f, osal_mutex_t wr_mutex, osal_mutex_t rd_mutex) { + f->mutex_wr = wr_mutex; + f->mutex_rd = rd_mutex; + } +#else + #define tu_fifo_config_mutex(_f, _wr_mutex, _rd_mutex) +#endif + +bool tu_fifo_write (tu_fifo_t* f, void const * p_data); +uint16_t tu_fifo_write_n (tu_fifo_t* f, void const * p_data, uint16_t n); +uint16_t tu_fifo_write_n_const_addr_full_words (tu_fifo_t* f, const void * data, uint16_t n); + +bool tu_fifo_read (tu_fifo_t* f, void * p_buffer); +uint16_t tu_fifo_read_n (tu_fifo_t* f, void * p_buffer, uint16_t n); +uint16_t tu_fifo_read_n_const_addr_full_words (tu_fifo_t* f, void * buffer, uint16_t n); + +bool tu_fifo_peek (tu_fifo_t* f, void * p_buffer); +uint16_t tu_fifo_peek_n (tu_fifo_t* f, void * p_buffer, uint16_t n); + +uint16_t tu_fifo_count (tu_fifo_t* f); +uint16_t tu_fifo_remaining (tu_fifo_t* f); +bool tu_fifo_empty (tu_fifo_t* f); +bool tu_fifo_full (tu_fifo_t* f); +bool tu_fifo_overflowed (tu_fifo_t* f); +void tu_fifo_correct_read_pointer (tu_fifo_t* f); + +TU_ATTR_ALWAYS_INLINE static inline +uint16_t tu_fifo_depth(tu_fifo_t* f) { + return f->depth; +} + +// Pointer modifications intended to be used in combinations with DMAs. +// USE WITH CARE - NO SAFETY CHECKS CONDUCTED HERE! NOT MUTEX PROTECTED! +void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n); +void tu_fifo_advance_read_pointer (tu_fifo_t *f, uint16_t n); + +// If you want to read/write from/to the FIFO by use of a DMA, you may need to conduct two copies +// to handle a possible wrapping part. These functions deliver a pointer to start +// reading/writing from/to and a valid linear length along which no wrap occurs. +void tu_fifo_get_read_info (tu_fifo_t *f, tu_fifo_buffer_info_t *info); +void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info); + +#ifdef __cplusplus +} +#endif + +#endif /* _TUSB_FIFO_H_ */ diff --git a/test-devices/composite-stm32/lib/tinyusb/common/tusb_mcu.h b/test-devices/composite-stm32/lib/tinyusb/common/tusb_mcu.h new file mode 100644 index 00000000..5a567f2d --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/common/tusb_mcu.h @@ -0,0 +1,451 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021, Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef TUSB_MCU_H_ +#define TUSB_MCU_H_ + +//--------------------------------------------------------------------+ +// Port/Platform Specific +// TUP stand for TinyUSB Port/Platform (can be renamed) +//--------------------------------------------------------------------+ + +//------------- Unaligned Memory Access -------------// + +#ifdef __ARM_ARCH + // ARM Architecture set __ARM_FEATURE_UNALIGNED to 1 for mcu supports unaligned access + #if defined(__ARM_FEATURE_UNALIGNED) && __ARM_FEATURE_UNALIGNED == 1 + #define TUP_ARCH_STRICT_ALIGN 0 + #else + #define TUP_ARCH_STRICT_ALIGN 1 + #endif +#else + // TODO default to strict align for others + // Should investigate other architecture such as risv, xtensa, mips for optimal setting + #define TUP_ARCH_STRICT_ALIGN 1 +#endif + +/* USB Controller Attributes for Device, Host or MCU (both) + * - ENDPOINT_MAX: max (logical) number of endpoint + * - ENDPOINT_EXCLUSIVE_NUMBER: endpoint number with different direction IN and OUT aren't allowed, + * e.g EP1 OUT & EP1 IN cannot exist together + * - RHPORT_HIGHSPEED: support highspeed with on-chip PHY + */ + +//--------------------------------------------------------------------+ +// NXP +//--------------------------------------------------------------------+ +#if TU_CHECK_MCU(OPT_MCU_LPC11UXX, OPT_MCU_LPC13XX, OPT_MCU_LPC15XX) + #define TUP_USBIP_IP3511 + #define TUP_DCD_ENDPOINT_MAX 5 + +#elif TU_CHECK_MCU(OPT_MCU_LPC175X_6X, OPT_MCU_LPC177X_8X, OPT_MCU_LPC40XX) + #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_USBIP_OHCI + #define TUP_OHCI_RHPORTS 2 + +#elif TU_CHECK_MCU(OPT_MCU_LPC51UXX) + #define TUP_USBIP_IP3511 + #define TUP_DCD_ENDPOINT_MAX 5 + +#elif TU_CHECK_MCU(OPT_MCU_LPC54) + // TODO USB0 has 5, USB1 has 6 + #define TUP_USBIP_IP3511 + #define TUP_DCD_ENDPOINT_MAX 6 + +#elif TU_CHECK_MCU(OPT_MCU_LPC55) + // TODO USB0 has 5, USB1 has 6 + #define TUP_USBIP_IP3511 + #define TUP_DCD_ENDPOINT_MAX 6 + +#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX) + // USB0 has 6 with HS PHY, USB1 has 4 only FS + #define TUP_USBIP_CHIPIDEA_HS + #define TUP_USBIP_EHCI + + #define TUP_DCD_ENDPOINT_MAX 6 + #define TUP_RHPORT_HIGHSPEED 1 + +#elif TU_CHECK_MCU(OPT_MCU_MCXN9) + // USB0 is chipidea FS + #define TUP_USBIP_CHIPIDEA_FS + #define TUP_USBIP_CHIPIDEA_FS_MCX + + // USB1 is chipidea HS + #define TUP_USBIP_CHIPIDEA_HS + #define TUP_USBIP_EHCI + + #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_RHPORT_HIGHSPEED 1 + +#elif TU_CHECK_MCU(OPT_MCU_MCXA15) + // USB0 is chipidea FS + #define TUP_USBIP_CHIPIDEA_FS + #define TUP_USBIP_CHIPIDEA_FS_MCX + + #define TUP_DCD_ENDPOINT_MAX 16 + +#elif TU_CHECK_MCU(OPT_MCU_MIMXRT1XXX) + #define TUP_USBIP_CHIPIDEA_HS + #define TUP_USBIP_EHCI + + #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_RHPORT_HIGHSPEED 1 + +#elif TU_CHECK_MCU(OPT_MCU_KINETIS_KL, OPT_MCU_KINETIS_K32L, OPT_MCU_KINETIS_K) + #define TUP_USBIP_CHIPIDEA_FS + #define TUP_USBIP_CHIPIDEA_FS_KINETIS + #define TUP_DCD_ENDPOINT_MAX 16 + +#elif TU_CHECK_MCU(OPT_MCU_MM32F327X) + #define TUP_DCD_ENDPOINT_MAX 16 + +//--------------------------------------------------------------------+ +// Nordic +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_NRF5X) + // 8 CBI + 1 ISO + #define TUP_DCD_ENDPOINT_MAX 9 + +//--------------------------------------------------------------------+ +// Microchip +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_SAMD21, OPT_MCU_SAMD51, OPT_MCU_SAME5X) || \ + TU_CHECK_MCU(OPT_MCU_SAMD11, OPT_MCU_SAML21, OPT_MCU_SAML22) + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_SAMG) + #define TUP_DCD_ENDPOINT_MAX 6 + #define TUP_DCD_ENDPOINT_EXCLUSIVE_NUMBER + +#elif TU_CHECK_MCU(OPT_MCU_SAMX7X) + #define TUP_DCD_ENDPOINT_MAX 10 + #define TUP_RHPORT_HIGHSPEED 1 + #define TUP_DCD_ENDPOINT_EXCLUSIVE_NUMBER + +#elif TU_CHECK_MCU(OPT_MCU_PIC32MZ) + #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_EXCLUSIVE_NUMBER + +#elif TU_CHECK_MCU(OPT_MCU_PIC32MX, OPT_MCU_PIC32MM, OPT_MCU_PIC32MK) || \ + TU_CHECK_MCU(OPT_MCU_PIC24, OPT_MCU_DSPIC33) + #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_DCD_ENDPOINT_EXCLUSIVE_NUMBER + +//--------------------------------------------------------------------+ +// ST +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_STM32F0) + #define TUP_USBIP_FSDEV + #define TUP_USBIP_FSDEV_STM32 + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_STM32F1) + // - F102, F103 use fsdev + // - F105, F107 use dwc2 + #if defined (STM32F105x8) || defined (STM32F105xB) || defined (STM32F105xC) || \ + defined (STM32F107xB) || defined (STM32F107xC) + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_STM32 + + #define TUP_DCD_ENDPOINT_MAX 4 + #elif defined(STM32F102x6) || defined(STM32F102xB) || \ + defined(STM32F103x6) || defined(STM32F103xB) || defined(STM32F103xE) || defined(STM32F103xG) + #define TUP_USBIP_FSDEV + #define TUP_USBIP_FSDEV_STM32 + #define TUP_DCD_ENDPOINT_MAX 8 + #else + #error "Unsupported STM32F1 mcu" + #endif + +#elif TU_CHECK_MCU(OPT_MCU_STM32F2) + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_STM32 + + // FS has 4 ep, HS has 5 ep + #define TUP_DCD_ENDPOINT_MAX 6 + +#elif TU_CHECK_MCU(OPT_MCU_STM32F3) + #define TUP_USBIP_FSDEV + #define TUP_USBIP_FSDEV_STM32 + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_STM32F4) + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_STM32 + + // For most mcu, FS has 4, HS has 6. TODO 446/469/479 HS has 9 + #define TUP_DCD_ENDPOINT_MAX 6 + +#elif TU_CHECK_MCU(OPT_MCU_STM32F7) + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_STM32 + + // FS has 6, HS has 9 + #define TUP_DCD_ENDPOINT_MAX 9 + + // MCU with on-chip HS Phy + #if defined(STM32F723xx) || defined(STM32F730xx) || defined(STM32F733xx) + #define TUP_RHPORT_HIGHSPEED 1 // Port0: FS, Port1: HS + #endif + +#elif TU_CHECK_MCU(OPT_MCU_STM32H7) + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_STM32 + + #define TUP_DCD_ENDPOINT_MAX 9 + +#elif TU_CHECK_MCU(OPT_MCU_STM32H5) + #define TUP_USBIP_FSDEV + #define TUP_USBIP_FSDEV_STM32 + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_STM32G4) + // Device controller + #define TUP_USBIP_FSDEV + #define TUP_USBIP_FSDEV_STM32 + + // TypeC controller + #define TUP_USBIP_TYPEC_STM32 + #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_TYPEC_RHPORTS_NUM 1 + +#elif TU_CHECK_MCU(OPT_MCU_STM32G0) + #define TUP_USBIP_FSDEV + #define TUP_USBIP_FSDEV_STM32 + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_STM32L0, OPT_MCU_STM32L1) + #define TUP_USBIP_FSDEV + #define TUP_USBIP_FSDEV_STM32 + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_STM32L4) + // - L4x2, L4x3 use fsdev + // - L4x4, L4x6, L4x7, L4x9 use dwc2 + #if defined (STM32L475xx) || defined (STM32L476xx) || \ + defined (STM32L485xx) || defined (STM32L486xx) || defined (STM32L496xx) || \ + defined (STM32L4A6xx) || defined (STM32L4P5xx) || defined (STM32L4Q5xx) || \ + defined (STM32L4R5xx) || defined (STM32L4R7xx) || defined (STM32L4R9xx) || \ + defined (STM32L4S5xx) || defined (STM32L4S7xx) || defined (STM32L4S9xx) + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_STM32 + + #define TUP_DCD_ENDPOINT_MAX 6 + #elif defined(STM32L412xx) || defined(STM32L422xx) || defined(STM32L432xx) || defined(STM32L433xx) || \ + defined(STM32L442xx) || defined(STM32L443xx) || defined(STM32L452xx) || defined(STM32L462xx) + #define TUP_USBIP_FSDEV + #define TUP_USBIP_FSDEV_STM32 + #define TUP_DCD_ENDPOINT_MAX 8 + #else + #error "Unsupported STM32L4 mcu" + #endif + +#elif TU_CHECK_MCU(OPT_MCU_STM32WB) + #define TUP_USBIP_FSDEV + #define TUP_USBIP_FSDEV_STM32 + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_STM32U5) + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_STM32 + + // U59x/5Ax/5Fx/5Gx are highspeed with built-in HS PHY + #if defined(STM32U595xx) || defined(STM32U599xx) || defined(STM32U5A5xx) || defined(STM32U5A9xx) || \ + defined(STM32U5F7xx) || defined(STM32U5F9xx) || defined(STM32U5G7xx) || defined(STM32U5G9xx) + #define TUP_DCD_ENDPOINT_MAX 9 + #define TUP_RHPORT_HIGHSPEED 1 + #else + #define TUP_DCD_ENDPOINT_MAX 6 + #endif + +#elif TU_CHECK_MCU(OPT_MCU_STM32L5) + #define TUP_USBIP_FSDEV + #define TUP_USBIP_FSDEV_STM32 + #define TUP_DCD_ENDPOINT_MAX 8 + +//--------------------------------------------------------------------+ +// Sony +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_CXD56) + #define TUP_DCD_ENDPOINT_MAX 7 + #define TUP_RHPORT_HIGHSPEED 1 + #define TUP_DCD_ENDPOINT_EXCLUSIVE_NUMBER + +//--------------------------------------------------------------------+ +// TI +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_MSP430x5xx) + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_MSP432E4, OPT_MCU_TM4C123, OPT_MCU_TM4C129) + #define TUP_DCD_ENDPOINT_MAX 8 + +//--------------------------------------------------------------------+ +// ValentyUSB (Litex) +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_VALENTYUSB_EPTRI) + #define TUP_DCD_ENDPOINT_MAX 16 + +//--------------------------------------------------------------------+ +// Nuvoton +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_NUC121, OPT_MCU_NUC126) + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_NUC120) + #define TUP_DCD_ENDPOINT_MAX 6 + +#elif TU_CHECK_MCU(OPT_MCU_NUC505) + #define TUP_DCD_ENDPOINT_MAX 12 + #define TUP_RHPORT_HIGHSPEED 1 + +//--------------------------------------------------------------------+ +// Espressif +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) + #define TUP_USBIP_DWC2 + #define TUP_DCD_ENDPOINT_MAX 6 + +#elif TU_CHECK_MCU(OPT_MCU_ESP32) && (CFG_TUD_ENABLED || !(defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421)) + #error "MCUs are only supported with CFG_TUH_MAX3421 enabled" + +//--------------------------------------------------------------------+ +// Dialog +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_DA1469X) + #define TUP_DCD_ENDPOINT_MAX 4 + +//--------------------------------------------------------------------+ +// Raspberry Pi +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_RP2040) + #define TUP_DCD_ENDPOINT_MAX 16 + + #define TU_ATTR_FAST_FUNC __attribute__((section(".time_critical.tinyusb"))) + +//--------------------------------------------------------------------+ +// Silabs +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_EFM32GG) + #define TUP_USBIP_DWC2 + #define TUP_DCD_ENDPOINT_MAX 7 + +//--------------------------------------------------------------------+ +// Renesas +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_RX63X, OPT_MCU_RX65X, OPT_MCU_RX72N, OPT_MCU_RAXXX) + #define TUP_USBIP_RUSB2 + #define TUP_DCD_ENDPOINT_MAX 10 + +//--------------------------------------------------------------------+ +// GigaDevice +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_GD32VF103) + #define TUP_USBIP_DWC2 + #define TUP_DCD_ENDPOINT_MAX 4 + +//--------------------------------------------------------------------+ +// Broadcom +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_BCM2711, OPT_MCU_BCM2835, OPT_MCU_BCM2837) + #define TUP_USBIP_DWC2 + #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_RHPORT_HIGHSPEED 1 + +//--------------------------------------------------------------------+ +// Infineon +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_XMC4000) + #define TUP_USBIP_DWC2 + #define TUP_DCD_ENDPOINT_MAX 8 + +//--------------------------------------------------------------------+ +// BridgeTek +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_FT90X) + #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_RHPORT_HIGHSPEED 1 + +#elif TU_CHECK_MCU(OPT_MCU_FT93X) + #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_RHPORT_HIGHSPEED 1 + +//--------------------------------------------------------------------+ +// Allwinner +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_F1C100S) + #define TUP_DCD_ENDPOINT_MAX 4 + +//------------- WCH -------------// +#elif TU_CHECK_MCU(OPT_MCU_CH32V307) + #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_RHPORT_HIGHSPEED 1 + +#elif TU_CHECK_MCU(OPT_MCU_CH32F20X) + #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_RHPORT_HIGHSPEED 1 +#endif + + +//--------------------------------------------------------------------+ +// External USB controller +//--------------------------------------------------------------------+ + +#if defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421 + #ifndef CFG_TUH_MAX3421_ENDPOINT_TOTAL + #define CFG_TUH_MAX3421_ENDPOINT_TOTAL (8 + 4*(CFG_TUH_DEVICE_MAX-1)) + #endif +#endif + + +//--------------------------------------------------------------------+ +// Default Values +//--------------------------------------------------------------------+ + +#ifndef TUP_MCU_MULTIPLE_CORE +#define TUP_MCU_MULTIPLE_CORE 0 +#endif + +#if !defined(TUP_DCD_ENDPOINT_MAX) && defined(CFG_TUD_ENABLED) && CFG_TUD_ENABLED +#warning "TUP_DCD_ENDPOINT_MAX is not defined for this MCU, default to 8" + #define TUP_DCD_ENDPOINT_MAX 8 +#endif + +// Default to fullspeed if not defined +#ifndef TUP_RHPORT_HIGHSPEED + #define TUP_RHPORT_HIGHSPEED 0 +#endif + +// fast function, normally mean placing function in SRAM +#ifndef TU_ATTR_FAST_FUNC + #define TU_ATTR_FAST_FUNC +#endif + +#if defined(TUP_USBIP_DWC2) || defined(TUP_USBIP_FSDEV) + #define TUP_DCD_EDPT_ISO_ALLOC +#endif + +#endif diff --git a/test-devices/composite-stm32/lib/tinyusb/common/tusb_private.h b/test-devices/composite-stm32/lib/tinyusb/common/tusb_private.h new file mode 100644 index 00000000..373a5025 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/common/tusb_private.h @@ -0,0 +1,177 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022, Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + + +#ifndef _TUSB_PRIVATE_H_ +#define _TUSB_PRIVATE_H_ + +// Internal Helper used by Host and Device Stack + +#ifdef __cplusplus + extern "C" { +#endif + +typedef struct TU_ATTR_PACKED +{ + volatile uint8_t busy : 1; + volatile uint8_t stalled : 1; + volatile uint8_t claimed : 1; +}tu_edpt_state_t; + +typedef struct { + bool is_host; // host or device most + union { + uint8_t daddr; + uint8_t rhport; + uint8_t hwid; + }; + uint8_t ep_addr; + uint8_t ep_speed; + + uint16_t ep_packetsize; + uint16_t ep_bufsize; + + // TODO xfer_fifo can skip this buffer + uint8_t* ep_buf; + + tu_fifo_t ff; + + // mutex: read if ep rx, write if e tx + OSAL_MUTEX_DEF(ff_mutexdef); + +}tu_edpt_stream_t; + +//--------------------------------------------------------------------+ +// Endpoint +//--------------------------------------------------------------------+ + +// Check if endpoint descriptor is valid per USB specs +bool tu_edpt_validate(tusb_desc_endpoint_t const * desc_ep, tusb_speed_t speed); + +// Bind all endpoint of a interface descriptor to class driver +void tu_edpt_bind_driver(uint8_t ep2drv[][2], tusb_desc_interface_t const* p_desc, uint16_t desc_len, uint8_t driver_id); + +// Calculate total length of n interfaces (depending on IAD) +uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const* desc_itf, uint8_t itf_count, uint16_t max_len); + +// Claim an endpoint with provided mutex +bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex); + +// Release an endpoint with provided mutex +bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex); + +//--------------------------------------------------------------------+ +// Endpoint Stream +//--------------------------------------------------------------------+ + +// Init an endpoint stream +bool tu_edpt_stream_init(tu_edpt_stream_t* s, bool is_host, bool is_tx, bool overwritable, + void* ff_buf, uint16_t ff_bufsize, uint8_t* ep_buf, uint16_t ep_bufsize); + +// Deinit an endpoint stream +bool tu_edpt_stream_deinit(tu_edpt_stream_t* s); + +// Open an stream for an endpoint +// hwid is either device address (host mode) or rhport (device mode) +TU_ATTR_ALWAYS_INLINE static inline +void tu_edpt_stream_open(tu_edpt_stream_t* s, uint8_t hwid, tusb_desc_endpoint_t const *desc_ep) { + tu_fifo_clear(&s->ff); + s->hwid = hwid; + s->ep_addr = desc_ep->bEndpointAddress; + s->ep_packetsize = tu_edpt_packet_size(desc_ep); +} + +TU_ATTR_ALWAYS_INLINE static inline +void tu_edpt_stream_close(tu_edpt_stream_t* s) { + s->hwid = 0; + s->ep_addr = 0; +} + +// Clear fifo +TU_ATTR_ALWAYS_INLINE static inline +bool tu_edpt_stream_clear(tu_edpt_stream_t* s) { + return tu_fifo_clear(&s->ff); +} + +//--------------------------------------------------------------------+ +// Stream Write +//--------------------------------------------------------------------+ + +// Write to stream +uint32_t tu_edpt_stream_write(tu_edpt_stream_t* s, void const *buffer, uint32_t bufsize); + +// Start an usb transfer if endpoint is not busy +uint32_t tu_edpt_stream_write_xfer(tu_edpt_stream_t* s); + +// Start an zero-length packet if needed +bool tu_edpt_stream_write_zlp_if_needed(tu_edpt_stream_t* s, uint32_t last_xferred_bytes); + +// Get the number of bytes available for writing +TU_ATTR_ALWAYS_INLINE static inline +uint32_t tu_edpt_stream_write_available(tu_edpt_stream_t* s) { + return (uint32_t) tu_fifo_remaining(&s->ff); +} + +//--------------------------------------------------------------------+ +// Stream Read +//--------------------------------------------------------------------+ + +// Read from stream +uint32_t tu_edpt_stream_read(tu_edpt_stream_t* s, void* buffer, uint32_t bufsize); + +// Start an usb transfer if endpoint is not busy +uint32_t tu_edpt_stream_read_xfer(tu_edpt_stream_t* s); + +// Must be called in the transfer complete callback +TU_ATTR_ALWAYS_INLINE static inline +void tu_edpt_stream_read_xfer_complete(tu_edpt_stream_t* s, uint32_t xferred_bytes) { + tu_fifo_write_n(&s->ff, s->ep_buf, (uint16_t) xferred_bytes); +} + +// Same as tu_edpt_stream_read_xfer_complete but skip the first n bytes +TU_ATTR_ALWAYS_INLINE static inline +void tu_edpt_stream_read_xfer_complete_offset(tu_edpt_stream_t* s, uint32_t xferred_bytes, uint32_t skip_offset) { + if (skip_offset < xferred_bytes) { + tu_fifo_write_n(&s->ff, s->ep_buf + skip_offset, (uint16_t) (xferred_bytes - skip_offset)); + } +} + +// Get the number of bytes available for reading +TU_ATTR_ALWAYS_INLINE static inline +uint32_t tu_edpt_stream_read_available(tu_edpt_stream_t* s) { + return (uint32_t) tu_fifo_count(&s->ff); +} + +TU_ATTR_ALWAYS_INLINE static inline +bool tu_edpt_stream_peek(tu_edpt_stream_t* s, uint8_t* ch) { + return tu_fifo_peek(&s->ff, ch); +} + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_PRIVATE_H_ */ diff --git a/test-devices/composite-stm32/lib/tinyusb/common/tusb_types.h b/test-devices/composite-stm32/lib/tinyusb/common/tusb_types.h new file mode 100644 index 00000000..b571f9b7 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/common/tusb_types.h @@ -0,0 +1,535 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef TUSB_TYPES_H_ +#define TUSB_TYPES_H_ + +#include +#include +#include "tusb_compiler.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/*------------------------------------------------------------------*/ +/* CONSTANTS + *------------------------------------------------------------------*/ + +/// defined base on EHCI specs value for Endpoint Speed +typedef enum { + TUSB_SPEED_FULL = 0, + TUSB_SPEED_LOW = 1, + TUSB_SPEED_HIGH = 2, + TUSB_SPEED_INVALID = 0xff, +} tusb_speed_t; + +/// defined base on USB Specs Endpoint's bmAttributes +typedef enum { + TUSB_XFER_CONTROL = 0 , + TUSB_XFER_ISOCHRONOUS , + TUSB_XFER_BULK , + TUSB_XFER_INTERRUPT +} tusb_xfer_type_t; + +typedef enum { + TUSB_DIR_OUT = 0, + TUSB_DIR_IN = 1, + + TUSB_DIR_IN_MASK = 0x80 +} tusb_dir_t; + +enum { + TUSB_EPSIZE_BULK_FS = 64, + TUSB_EPSIZE_BULK_HS = 512, + + TUSB_EPSIZE_ISO_FS_MAX = 1023, + TUSB_EPSIZE_ISO_HS_MAX = 1024, +}; + +/// Isochronous Endpoint Attributes +typedef enum { + TUSB_ISO_EP_ATT_NO_SYNC = 0x00, + TUSB_ISO_EP_ATT_ASYNCHRONOUS = 0x04, + TUSB_ISO_EP_ATT_ADAPTIVE = 0x08, + TUSB_ISO_EP_ATT_SYNCHRONOUS = 0x0C, + TUSB_ISO_EP_ATT_DATA = 0x00, ///< Data End Point + TUSB_ISO_EP_ATT_EXPLICIT_FB = 0x10, ///< Feedback End Point + TUSB_ISO_EP_ATT_IMPLICIT_FB = 0x20, ///< Data endpoint that also serves as an implicit feedback +} tusb_iso_ep_attribute_t; + +/// USB Descriptor Types +typedef enum { + TUSB_DESC_DEVICE = 0x01, + TUSB_DESC_CONFIGURATION = 0x02, + TUSB_DESC_STRING = 0x03, + TUSB_DESC_INTERFACE = 0x04, + TUSB_DESC_ENDPOINT = 0x05, + TUSB_DESC_DEVICE_QUALIFIER = 0x06, + TUSB_DESC_OTHER_SPEED_CONFIG = 0x07, + TUSB_DESC_INTERFACE_POWER = 0x08, + TUSB_DESC_OTG = 0x09, + TUSB_DESC_DEBUG = 0x0A, + TUSB_DESC_INTERFACE_ASSOCIATION = 0x0B, + + TUSB_DESC_BOS = 0x0F, + TUSB_DESC_DEVICE_CAPABILITY = 0x10, + + TUSB_DESC_FUNCTIONAL = 0x21, + + // Class Specific Descriptor + TUSB_DESC_CS_DEVICE = 0x21, + TUSB_DESC_CS_CONFIGURATION = 0x22, + TUSB_DESC_CS_STRING = 0x23, + TUSB_DESC_CS_INTERFACE = 0x24, + TUSB_DESC_CS_ENDPOINT = 0x25, + + TUSB_DESC_SUPERSPEED_ENDPOINT_COMPANION = 0x30, + TUSB_DESC_SUPERSPEED_ISO_ENDPOINT_COMPANION = 0x31 +} tusb_desc_type_t; + +typedef enum { + TUSB_REQ_GET_STATUS = 0 , + TUSB_REQ_CLEAR_FEATURE = 1 , + TUSB_REQ_RESERVED = 2 , + TUSB_REQ_SET_FEATURE = 3 , + TUSB_REQ_RESERVED2 = 4 , + TUSB_REQ_SET_ADDRESS = 5 , + TUSB_REQ_GET_DESCRIPTOR = 6 , + TUSB_REQ_SET_DESCRIPTOR = 7 , + TUSB_REQ_GET_CONFIGURATION = 8 , + TUSB_REQ_SET_CONFIGURATION = 9 , + TUSB_REQ_GET_INTERFACE = 10 , + TUSB_REQ_SET_INTERFACE = 11 , + TUSB_REQ_SYNCH_FRAME = 12 +} tusb_request_code_t; + +typedef enum { + TUSB_REQ_FEATURE_EDPT_HALT = 0, + TUSB_REQ_FEATURE_REMOTE_WAKEUP = 1, + TUSB_REQ_FEATURE_TEST_MODE = 2 +} tusb_request_feature_selector_t; + +typedef enum { + TUSB_REQ_TYPE_STANDARD = 0, + TUSB_REQ_TYPE_CLASS, + TUSB_REQ_TYPE_VENDOR, + TUSB_REQ_TYPE_INVALID +} tusb_request_type_t; + +typedef enum { + TUSB_REQ_RCPT_DEVICE =0, + TUSB_REQ_RCPT_INTERFACE, + TUSB_REQ_RCPT_ENDPOINT, + TUSB_REQ_RCPT_OTHER +} tusb_request_recipient_t; + +// https://www.usb.org/defined-class-codes +typedef enum { + TUSB_CLASS_UNSPECIFIED = 0 , + TUSB_CLASS_AUDIO = 1 , + TUSB_CLASS_CDC = 2 , + TUSB_CLASS_HID = 3 , + TUSB_CLASS_RESERVED_4 = 4 , + TUSB_CLASS_PHYSICAL = 5 , + TUSB_CLASS_IMAGE = 6 , + TUSB_CLASS_PRINTER = 7 , + TUSB_CLASS_MSC = 8 , + TUSB_CLASS_HUB = 9 , + TUSB_CLASS_CDC_DATA = 10 , + TUSB_CLASS_SMART_CARD = 11 , + TUSB_CLASS_RESERVED_12 = 12 , + TUSB_CLASS_CONTENT_SECURITY = 13 , + TUSB_CLASS_VIDEO = 14 , + TUSB_CLASS_PERSONAL_HEALTHCARE = 15 , + TUSB_CLASS_AUDIO_VIDEO = 16 , + + TUSB_CLASS_DIAGNOSTIC = 0xDC , + TUSB_CLASS_WIRELESS_CONTROLLER = 0xE0 , + TUSB_CLASS_MISC = 0xEF , + TUSB_CLASS_APPLICATION_SPECIFIC = 0xFE , + TUSB_CLASS_VENDOR_SPECIFIC = 0xFF +} tusb_class_code_t; + +typedef enum +{ + MISC_SUBCLASS_COMMON = 2 +}misc_subclass_type_t; + +typedef enum { + MISC_PROTOCOL_IAD = 1 +} misc_protocol_type_t; + +typedef enum { + APP_SUBCLASS_USBTMC = 0x03, + APP_SUBCLASS_DFU_RUNTIME = 0x01 +} app_subclass_type_t; + +typedef enum { + DEVICE_CAPABILITY_WIRELESS_USB = 0x01, + DEVICE_CAPABILITY_USB20_EXTENSION = 0x02, + DEVICE_CAPABILITY_SUPERSPEED_USB = 0x03, + DEVICE_CAPABILITY_CONTAINER_id = 0x04, + DEVICE_CAPABILITY_PLATFORM = 0x05, + DEVICE_CAPABILITY_POWER_DELIVERY = 0x06, + DEVICE_CAPABILITY_BATTERY_INFO = 0x07, + DEVICE_CAPABILITY_PD_CONSUMER_PORT = 0x08, + DEVICE_CAPABILITY_PD_PROVIDER_PORT = 0x09, + DEVICE_CAPABILITY_SUPERSPEED_PLUS = 0x0A, + DEVICE_CAPABILITY_PRECESION_TIME_MEASUREMENT = 0x0B, + DEVICE_CAPABILITY_WIRELESS_USB_EXT = 0x0C, + DEVICE_CAPABILITY_BILLBOARD = 0x0D, + DEVICE_CAPABILITY_AUTHENTICATION = 0x0E, + DEVICE_CAPABILITY_BILLBOARD_EX = 0x0F, + DEVICE_CAPABILITY_CONFIGURATION_SUMMARY = 0x10 +} device_capability_type_t; + +enum { + TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP = 1u << 5, + TUSB_DESC_CONFIG_ATT_SELF_POWERED = 1u << 6, +}; + +#define TUSB_DESC_CONFIG_POWER_MA(x) ((x)/2) + +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ +typedef enum { + XFER_RESULT_SUCCESS = 0, + XFER_RESULT_FAILED, + XFER_RESULT_STALLED, + XFER_RESULT_TIMEOUT, + XFER_RESULT_INVALID +} xfer_result_t; + +// TODO remove +enum { + DESC_OFFSET_LEN = 0, + DESC_OFFSET_TYPE = 1 +}; + +enum { + INTERFACE_INVALID_NUMBER = 0xff +}; + +typedef enum { + MS_OS_20_SET_HEADER_DESCRIPTOR = 0x00, + MS_OS_20_SUBSET_HEADER_CONFIGURATION = 0x01, + MS_OS_20_SUBSET_HEADER_FUNCTION = 0x02, + MS_OS_20_FEATURE_COMPATBLE_ID = 0x03, + MS_OS_20_FEATURE_REG_PROPERTY = 0x04, + MS_OS_20_FEATURE_MIN_RESUME_TIME = 0x05, + MS_OS_20_FEATURE_MODEL_ID = 0x06, + MS_OS_20_FEATURE_CCGP_DEVICE = 0x07, + MS_OS_20_FEATURE_VENDOR_REVISION = 0x08 +} microsoft_os_20_type_t; + +enum { + CONTROL_STAGE_IDLE, + CONTROL_STAGE_SETUP, + CONTROL_STAGE_DATA, + CONTROL_STAGE_ACK +}; + +enum { + TUSB_INDEX_INVALID_8 = 0xFFu +}; + +//--------------------------------------------------------------------+ +// USB Descriptors +//--------------------------------------------------------------------+ + +// Start of all packed definitions for compiler without per-type packed +TU_ATTR_PACKED_BEGIN +TU_ATTR_BIT_FIELD_ORDER_BEGIN + +/// USB Device Descriptor +typedef struct TU_ATTR_PACKED { + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< DEVICE Descriptor Type. + uint16_t bcdUSB ; ///< BUSB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H). + + uint8_t bDeviceClass ; ///< Class code (assigned by the USB-IF). + uint8_t bDeviceSubClass ; ///< Subclass code (assigned by the USB-IF). + uint8_t bDeviceProtocol ; ///< Protocol code (assigned by the USB-IF). + uint8_t bMaxPacketSize0 ; ///< Maximum packet size for endpoint zero (only 8, 16, 32, or 64 are valid). For HS devices is fixed to 64. + + uint16_t idVendor ; ///< Vendor ID (assigned by the USB-IF). + uint16_t idProduct ; ///< Product ID (assigned by the manufacturer). + uint16_t bcdDevice ; ///< Device release number in binary-coded decimal. + uint8_t iManufacturer ; ///< Index of string descriptor describing manufacturer. + uint8_t iProduct ; ///< Index of string descriptor describing product. + uint8_t iSerialNumber ; ///< Index of string descriptor describing the device's serial number. + + uint8_t bNumConfigurations ; ///< Number of possible configurations. +} tusb_desc_device_t; + +TU_VERIFY_STATIC( sizeof(tusb_desc_device_t) == 18, "size is not correct"); + +// USB Binary Device Object Store (BOS) Descriptor +typedef struct TU_ATTR_PACKED { + uint8_t bLength ; ///< Size of this descriptor in bytes + uint8_t bDescriptorType ; ///< CONFIGURATION Descriptor Type + uint16_t wTotalLength ; ///< Total length of data returned for this descriptor + uint8_t bNumDeviceCaps ; ///< Number of device capability descriptors in the BOS +} tusb_desc_bos_t; + +TU_VERIFY_STATIC( sizeof(tusb_desc_bos_t) == 5, "size is not correct"); + +/// USB Configuration Descriptor +typedef struct TU_ATTR_PACKED { + uint8_t bLength ; ///< Size of this descriptor in bytes + uint8_t bDescriptorType ; ///< CONFIGURATION Descriptor Type + uint16_t wTotalLength ; ///< Total length of data returned for this configuration. Includes the combined length of all descriptors (configuration, interface, endpoint, and class- or vendor-specific) returned for this configuration. + + uint8_t bNumInterfaces ; ///< Number of interfaces supported by this configuration + uint8_t bConfigurationValue ; ///< Value to use as an argument to the SetConfiguration() request to select this configuration. + uint8_t iConfiguration ; ///< Index of string descriptor describing this configuration + uint8_t bmAttributes ; ///< Configuration characteristics \n D7: Reserved (set to one)\n D6: Self-powered \n D5: Remote Wakeup \n D4...0: Reserved (reset to zero) \n D7 is reserved and must be set to one for historical reasons. \n A device configuration that uses power from the bus and a local source reports a non-zero value in bMaxPower to indicate the amount of bus power required and sets D6. The actual power source at runtime may be determined using the GetStatus(DEVICE) request (see USB 2.0 spec Section 9.4.5). \n If a device configuration supports remote wakeup, D5 is set to one. + uint8_t bMaxPower ; ///< Maximum power consumption of the USB device from the bus in this specific configuration when the device is fully operational. Expressed in 2 mA units (i.e., 50 = 100 mA). +} tusb_desc_configuration_t; + +TU_VERIFY_STATIC( sizeof(tusb_desc_configuration_t) == 9, "size is not correct"); + +/// USB Interface Descriptor +typedef struct TU_ATTR_PACKED { + uint8_t bLength ; ///< Size of this descriptor in bytes + uint8_t bDescriptorType ; ///< INTERFACE Descriptor Type + + uint8_t bInterfaceNumber ; ///< Number of this interface. Zero-based value identifying the index in the array of concurrent interfaces supported by this configuration. + uint8_t bAlternateSetting ; ///< Value used to select this alternate setting for the interface identified in the prior field + uint8_t bNumEndpoints ; ///< Number of endpoints used by this interface (excluding endpoint zero). If this value is zero, this interface only uses the Default Control Pipe. + uint8_t bInterfaceClass ; ///< Class code (assigned by the USB-IF). \li A value of zero is reserved for future standardization. \li If this field is set to FFH, the interface class is vendor-specific. \li All other values are reserved for assignment by the USB-IF. + uint8_t bInterfaceSubClass ; ///< Subclass code (assigned by the USB-IF). \n These codes are qualified by the value of the bInterfaceClass field. \li If the bInterfaceClass field is reset to zero, this field must also be reset to zero. \li If the bInterfaceClass field is not set to FFH, all values are reserved for assignment by the USB-IF. + uint8_t bInterfaceProtocol ; ///< Protocol code (assigned by the USB). \n These codes are qualified by the value of the bInterfaceClass and the bInterfaceSubClass fields. If an interface supports class-specific requests, this code identifies the protocols that the device uses as defined by the specification of the device class. \li If this field is reset to zero, the device does not use a class-specific protocol on this interface. \li If this field is set to FFH, the device uses a vendor-specific protocol for this interface. + uint8_t iInterface ; ///< Index of string descriptor describing this interface +} tusb_desc_interface_t; + +TU_VERIFY_STATIC( sizeof(tusb_desc_interface_t) == 9, "size is not correct"); + +/// USB Endpoint Descriptor +typedef struct TU_ATTR_PACKED { + uint8_t bLength ; // Size of this descriptor in bytes + uint8_t bDescriptorType ; // ENDPOINT Descriptor Type + + uint8_t bEndpointAddress ; // The address of the endpoint + + struct TU_ATTR_PACKED { + uint8_t xfer : 2; // Control, ISO, Bulk, Interrupt + uint8_t sync : 2; // None, Asynchronous, Adaptive, Synchronous + uint8_t usage : 2; // Data, Feedback, Implicit feedback + uint8_t : 2; + } bmAttributes; + + uint16_t wMaxPacketSize ; // Bit 10..0 : max packet size, bit 12..11 additional transaction per highspeed micro-frame + uint8_t bInterval ; // Polling interval, in frames or microframes depending on the operating speed +} tusb_desc_endpoint_t; + +TU_VERIFY_STATIC( sizeof(tusb_desc_endpoint_t) == 7, "size is not correct"); + +/// USB Other Speed Configuration Descriptor +typedef struct TU_ATTR_PACKED { + uint8_t bLength ; ///< Size of descriptor + uint8_t bDescriptorType ; ///< Other_speed_Configuration Type + uint16_t wTotalLength ; ///< Total length of data returned + + uint8_t bNumInterfaces ; ///< Number of interfaces supported by this speed configuration + uint8_t bConfigurationValue ; ///< Value to use to select configuration + uint8_t iConfiguration ; ///< Index of string descriptor + uint8_t bmAttributes ; ///< Same as Configuration descriptor + uint8_t bMaxPower ; ///< Same as Configuration descriptor +} tusb_desc_other_speed_t; + +/// USB Device Qualifier Descriptor +typedef struct TU_ATTR_PACKED { + uint8_t bLength ; ///< Size of descriptor + uint8_t bDescriptorType ; ///< Device Qualifier Type + uint16_t bcdUSB ; ///< USB specification version number (e.g., 0200H for V2.00) + + uint8_t bDeviceClass ; ///< Class Code + uint8_t bDeviceSubClass ; ///< SubClass Code + uint8_t bDeviceProtocol ; ///< Protocol Code + + uint8_t bMaxPacketSize0 ; ///< Maximum packet size for other speed + uint8_t bNumConfigurations ; ///< Number of Other-speed Configurations + uint8_t bReserved ; ///< Reserved for future use, must be zero +} tusb_desc_device_qualifier_t; + +TU_VERIFY_STATIC( sizeof(tusb_desc_device_qualifier_t) == 10, "size is not correct"); + +/// USB Interface Association Descriptor (IAD ECN) +typedef struct TU_ATTR_PACKED { + uint8_t bLength ; ///< Size of descriptor + uint8_t bDescriptorType ; ///< Other_speed_Configuration Type + + uint8_t bFirstInterface ; ///< Index of the first associated interface. + uint8_t bInterfaceCount ; ///< Total number of associated interfaces. + + uint8_t bFunctionClass ; ///< Interface class ID. + uint8_t bFunctionSubClass ; ///< Interface subclass ID. + uint8_t bFunctionProtocol ; ///< Interface protocol ID. + + uint8_t iFunction ; ///< Index of the string descriptor describing the interface association. +} tusb_desc_interface_assoc_t; + +TU_VERIFY_STATIC( sizeof(tusb_desc_interface_assoc_t) == 8, "size is not correct"); + +// USB String Descriptor +typedef struct TU_ATTR_PACKED { + uint8_t bLength ; ///< Size of this descriptor in bytes + uint8_t bDescriptorType ; ///< Descriptor Type + uint16_t unicode_string[]; +} tusb_desc_string_t; + +// USB Binary Device Object Store (BOS) +typedef struct TU_ATTR_PACKED { + uint8_t bLength; + uint8_t bDescriptorType ; + uint8_t bDevCapabilityType; + uint8_t bReserved; + uint8_t PlatformCapabilityUUID[16]; + uint8_t CapabilityData[]; +} tusb_desc_bos_platform_t; + +// USB WebUSB URL Descriptor +typedef struct TU_ATTR_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bScheme; + char url[]; +} tusb_desc_webusb_url_t; + +// DFU Functional Descriptor +typedef struct TU_ATTR_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + + union { + struct TU_ATTR_PACKED { + uint8_t bitCanDnload : 1; + uint8_t bitCanUpload : 1; + uint8_t bitManifestationTolerant : 1; + uint8_t bitWillDetach : 1; + uint8_t reserved : 4; + } bmAttributes; + + uint8_t bAttributes; + }; + + uint16_t wDetachTimeOut; + uint16_t wTransferSize; + uint16_t bcdDFUVersion; +} tusb_desc_dfu_functional_t; + +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ + +typedef struct TU_ATTR_PACKED { + union { + struct TU_ATTR_PACKED { + uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t. + uint8_t type : 2; ///< Request type tusb_request_type_t. + uint8_t direction : 1; ///< Direction type. tusb_dir_t + } bmRequestType_bit; + + uint8_t bmRequestType; + }; + + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} tusb_control_request_t; + +TU_VERIFY_STATIC( sizeof(tusb_control_request_t) == 8, "size is not correct"); + +TU_ATTR_PACKED_END // End of all packed definitions +TU_ATTR_BIT_FIELD_ORDER_END + +//--------------------------------------------------------------------+ +// Endpoint helper +//--------------------------------------------------------------------+ + +// Get direction from Endpoint address +TU_ATTR_ALWAYS_INLINE static inline tusb_dir_t tu_edpt_dir(uint8_t addr) { + return (addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT; +} + +// Get Endpoint number from address +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_number(uint8_t addr) { + return (uint8_t)(addr & (~TUSB_DIR_IN_MASK)); +} + +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_addr(uint8_t num, uint8_t dir) { + return (uint8_t)(num | (dir ? TUSB_DIR_IN_MASK : 0)); +} + +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_edpt_packet_size(tusb_desc_endpoint_t const* desc_ep) { + return tu_le16toh(desc_ep->wMaxPacketSize) & 0x7FF; +} + +#if CFG_TUSB_DEBUG +TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_type_str(tusb_xfer_type_t t) { + tu_static const char *str[] = {"control", "isochronous", "bulk", "interrupt"}; + return str[t]; +} +#endif + +//--------------------------------------------------------------------+ +// Descriptor helper +//--------------------------------------------------------------------+ + +// return next descriptor +TU_ATTR_ALWAYS_INLINE static inline uint8_t const * tu_desc_next(void const* desc) { + uint8_t const* desc8 = (uint8_t const*) desc; + return desc8 + desc8[DESC_OFFSET_LEN]; +} + +// get descriptor type +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_type(void const* desc) { + return ((uint8_t const*) desc)[DESC_OFFSET_TYPE]; +} + +// get descriptor length +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_len(void const* desc) { + return ((uint8_t const*) desc)[DESC_OFFSET_LEN]; +} + +// find descriptor that match byte1 (type) +uint8_t const * tu_desc_find(uint8_t const* desc, uint8_t const* end, uint8_t byte1); + +// find descriptor that match byte1 (type) and byte2 +uint8_t const * tu_desc_find2(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2); + +// find descriptor that match byte1 (type) and byte2 +uint8_t const * tu_desc_find3(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2, uint8_t byte3); + +#ifdef __cplusplus + } +#endif + +#endif // TUSB_TYPES_H_ diff --git a/test-devices/composite-stm32/lib/tinyusb/common/tusb_verify.h b/test-devices/composite-stm32/lib/tinyusb/common/tusb_verify.h new file mode 100644 index 00000000..0a9549c9 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/common/tusb_verify.h @@ -0,0 +1,137 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ +#ifndef TUSB_VERIFY_H_ +#define TUSB_VERIFY_H_ + +#include +#include +#include "tusb_option.h" +#include "tusb_compiler.h" + +/*------------------------------------------------------------------*/ +/* This file use an advanced macro technique to mimic the default parameter + * as C++ for the sake of code simplicity. Beware of a headache macro + * manipulation that you are told to stay away. + * + * This contains macros for both VERIFY and ASSERT: + * + * VERIFY: Used when there is an error condition which is not the + * fault of the MCU. For example, bounds checking on data + * sent to the micro over USB should use this function. + * Another example is checking for buffer overflows, where + * returning from the active function causes a NAK. + * + * ASSERT: Used for error conditions that are caused by MCU firmware + * bugs. This is used to discover bugs in the code more + * quickly. One example would be adding assertions in library + * function calls to confirm a function's (untainted) + * parameters are valid. + * + * The difference in behavior is that ASSERT triggers a breakpoint while + * verify does not. + * + * #define TU_VERIFY(cond) if(cond) return false; + * #define TU_VERIFY(cond,ret) if(cond) return ret; + * + * #define TU_ASSERT(cond) if(cond) {_MESS_FAILED(); TU_BREAKPOINT(), return false;} + * #define TU_ASSERT(cond,ret) if(cond) {_MESS_FAILED(); TU_BREAKPOINT(), return ret;} + *------------------------------------------------------------------*/ + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// TU_VERIFY Helper +//--------------------------------------------------------------------+ + +#if CFG_TUSB_DEBUG + #include + #define _MESS_FAILED() tu_printf("%s %d: ASSERT FAILED\r\n", __func__, __LINE__) +#else + #define _MESS_FAILED() do {} while (0) +#endif + +// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7, M33. M55 +#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__) || defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8_1M_MAIN__) || \ + defined(__ARM7M__) || defined (__ARM7EM__) || defined(__ARM8M_MAINLINE__) || defined(__ARM8EM_MAINLINE__) + #define TU_BREAKPOINT() do \ + { \ + volatile uint32_t* ARM_CM_DHCSR = ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ \ + if ( (*ARM_CM_DHCSR) & 1UL ) __asm("BKPT #0\n"); /* Only halt mcu if debugger is attached */ \ + } while(0) + +#elif defined(__riscv) && !TUP_MCU_ESPRESSIF + #define TU_BREAKPOINT() do { __asm("ebreak\n"); } while(0) + +#elif defined(_mips) + #define TU_BREAKPOINT() do { __asm("sdbbp 0"); } while (0) + +#else + #define TU_BREAKPOINT() do {} while (0) +#endif + +// Helper to implement optional parameter for TU_VERIFY Macro family +#define _GET_3RD_ARG(arg1, arg2, arg3, ...) arg3 + +/*------------------------------------------------------------------*/ +/* TU_VERIFY + * - TU_VERIFY_1ARGS : return false if failed + * - TU_VERIFY_2ARGS : return provided value if failed + *------------------------------------------------------------------*/ +#define TU_VERIFY_DEFINE(_cond, _ret) \ + do { \ + if ( !(_cond) ) { return _ret; } \ + } while(0) + +#define TU_VERIFY_1ARGS(_cond) TU_VERIFY_DEFINE(_cond, false) +#define TU_VERIFY_2ARGS(_cond, _ret) TU_VERIFY_DEFINE(_cond, _ret) + +#define TU_VERIFY(...) _GET_3RD_ARG(__VA_ARGS__, TU_VERIFY_2ARGS, TU_VERIFY_1ARGS, _dummy)(__VA_ARGS__) + +/*------------------------------------------------------------------*/ +/* ASSERT + * basically TU_VERIFY with TU_BREAKPOINT() as handler + * - 1 arg : return false if failed + * - 2 arg : return error if failed + *------------------------------------------------------------------*/ +#define TU_ASSERT_DEFINE(_cond, _ret) \ + do { \ + if ( !(_cond) ) { _MESS_FAILED(); TU_BREAKPOINT(); return _ret; } \ + } while(0) + +#define TU_ASSERT_1ARGS(_cond) TU_ASSERT_DEFINE(_cond, false) +#define TU_ASSERT_2ARGS(_cond, _ret) TU_ASSERT_DEFINE(_cond, _ret) + +#ifndef TU_ASSERT +#define TU_ASSERT(...) _GET_3RD_ARG(__VA_ARGS__, TU_ASSERT_2ARGS, TU_ASSERT_1ARGS, _dummy)(__VA_ARGS__) +#endif + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/test-devices/composite-stm32/lib/tinyusb/device/dcd.h b/test-devices/composite-stm32/lib/tinyusb/device/dcd.h new file mode 100644 index 00000000..d4f105aa --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/device/dcd.h @@ -0,0 +1,242 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_DCD_H_ +#define _TUSB_DCD_H_ + +#include "common/tusb_common.h" +#include "osal/osal.h" +#include "common/tusb_fifo.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Configuration +//--------------------------------------------------------------------+ + +#ifndef CFG_TUD_ENDPPOINT_MAX + #define CFG_TUD_ENDPPOINT_MAX TUP_DCD_ENDPOINT_MAX +#endif + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF PROTYPES +//--------------------------------------------------------------------+ + +typedef enum { + DCD_EVENT_INVALID = 0, + DCD_EVENT_BUS_RESET, + DCD_EVENT_UNPLUGGED, + DCD_EVENT_SOF, + DCD_EVENT_SUSPEND, // TODO LPM Sleep L1 support + DCD_EVENT_RESUME, + + DCD_EVENT_SETUP_RECEIVED, + DCD_EVENT_XFER_COMPLETE, + + // Not an DCD event, just a convenient way to defer ISR function + USBD_EVENT_FUNC_CALL, + + DCD_EVENT_COUNT +} dcd_eventid_t; + +typedef struct TU_ATTR_ALIGNED(4) { + uint8_t rhport; + uint8_t event_id; + + union { + // BUS RESET + struct { + tusb_speed_t speed; + } bus_reset; + + // SOF + struct { + uint32_t frame_count; + }sof; + + // SETUP_RECEIVED + tusb_control_request_t setup_received; + + // XFER_COMPLETE + struct { + uint8_t ep_addr; + uint8_t result; + uint32_t len; + }xfer_complete; + + // FUNC_CALL + struct { + void (*func) (void*); + void* param; + }func_call; + }; +} dcd_event_t; + +//TU_VERIFY_STATIC(sizeof(dcd_event_t) <= 12, "size is not correct"); + +//--------------------------------------------------------------------+ +// Memory API +//--------------------------------------------------------------------+ + +// clean/flush data cache: write cache -> memory. +// Required before an DMA TX transfer to make sure data is in memory +void dcd_dcache_clean(void const* addr, uint32_t data_size) TU_ATTR_WEAK; + +// invalidate data cache: mark cache as invalid, next read will read from memory +// Required BOTH before and after an DMA RX transfer +void dcd_dcache_invalidate(void const* addr, uint32_t data_size) TU_ATTR_WEAK; + +// clean and invalidate data cache +// Required before an DMA transfer where memory is both read/write by DMA +void dcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) TU_ATTR_WEAK; + +//--------------------------------------------------------------------+ +// Controller API +//--------------------------------------------------------------------+ + +// Initialize controller to device mode +void dcd_init(uint8_t rhport); + +// Deinitialize controller, unset device mode. +bool dcd_deinit(uint8_t rhport); + +// Interrupt Handler +void dcd_int_handler(uint8_t rhport); + +// Enable device interrupt +void dcd_int_enable (uint8_t rhport); + +// Disable device interrupt +void dcd_int_disable(uint8_t rhport); + +// Receive Set Address request, mcu port must also include status IN response +void dcd_set_address(uint8_t rhport, uint8_t dev_addr); + +// Wake up host +void dcd_remote_wakeup(uint8_t rhport); + +// Connect by enabling internal pull-up resistor on D+/D- +void dcd_connect(uint8_t rhport) TU_ATTR_WEAK; + +// Disconnect by disabling internal pull-up resistor on D+/D- +void dcd_disconnect(uint8_t rhport) TU_ATTR_WEAK; + +// Enable/Disable Start-of-frame interrupt. Default is disabled +void dcd_sof_enable(uint8_t rhport, bool en); + +//--------------------------------------------------------------------+ +// Endpoint API +//--------------------------------------------------------------------+ + +// Invoked when a control transfer's status stage is complete. +// May help DCD to prepare for next control transfer, this API is optional. +void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request); + +// Configure endpoint's registers according to descriptor +bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_ep); + +// Close all non-control endpoints, cancel all pending transfers if any. +// Invoked when switching from a non-zero Configuration by SET_CONFIGURE therefore +// required for multiple configuration support. +void dcd_edpt_close_all (uint8_t rhport); + +// Close an endpoint. +// Since it is weak, caller must TU_ASSERT this function's existence before calling it. +void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr) TU_ATTR_WEAK; + +// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack +bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes); + +// Submit an transfer using fifo, When complete dcd_event_xfer_complete() is invoked to notify the stack +// This API is optional, may be useful for register-based for transferring data. +bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) TU_ATTR_WEAK; + +// Stall endpoint, any queuing transfer should be removed from endpoint +void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr); + +// clear stall, data toggle is also reset to DATA0 +// This API never calls with control endpoints, since it is auto cleared when receiving setup packet +void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr); + +// Allocate packet buffer used by ISO endpoints +// Some MCU need manual packet buffer allocation, we allocate the largest size to avoid clustering +TU_ATTR_WEAK bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size); + +// Configure and enable an ISO endpoint according to descriptor +TU_ATTR_WEAK bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc); + +//--------------------------------------------------------------------+ +// Event API (implemented by stack) +//--------------------------------------------------------------------+ + +// Called by DCD to notify device stack +extern void dcd_event_handler(dcd_event_t const * event, bool in_isr); + +// helper to send bus signal event +TU_ATTR_ALWAYS_INLINE static inline void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr) { + dcd_event_t event = { .rhport = rhport, .event_id = eid }; + dcd_event_handler(&event, in_isr); +} + +// helper to send bus reset event +TU_ATTR_ALWAYS_INLINE static inline void dcd_event_bus_reset (uint8_t rhport, tusb_speed_t speed, bool in_isr) { + dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_BUS_RESET }; + event.bus_reset.speed = speed; + dcd_event_handler(&event, in_isr); +} + +// helper to send setup received +TU_ATTR_ALWAYS_INLINE static inline void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr) { + dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SETUP_RECEIVED }; + memcpy(&event.setup_received, setup, sizeof(tusb_control_request_t)); + + dcd_event_handler(&event, in_isr); +} + +// helper to send transfer complete event +TU_ATTR_ALWAYS_INLINE static inline void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr) { + dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_XFER_COMPLETE }; + + event.xfer_complete.ep_addr = ep_addr; + event.xfer_complete.len = xferred_bytes; + event.xfer_complete.result = result; + + dcd_event_handler(&event, in_isr); +} + +TU_ATTR_ALWAYS_INLINE static inline void dcd_event_sof(uint8_t rhport, uint32_t frame_count, bool in_isr) { + dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SOF }; + event.sof.frame_count = frame_count; + dcd_event_handler(&event, in_isr); +} + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_DCD_H_ */ diff --git a/test-devices/composite-stm32/lib/tinyusb/device/usbd.c b/test-devices/composite-stm32/lib/tinyusb/device/usbd.c new file mode 100644 index 00000000..e51aa0fc --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/device/usbd.c @@ -0,0 +1,1391 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if CFG_TUD_ENABLED + +#include "device/dcd.h" +#include "tusb.h" +#include "common/tusb_private.h" + +#include "device/usbd.h" +#include "device/usbd_pvt.h" + +//--------------------------------------------------------------------+ +// USBD Configuration +//--------------------------------------------------------------------+ +#ifndef CFG_TUD_TASK_QUEUE_SZ + #define CFG_TUD_TASK_QUEUE_SZ 16 +#endif + +//--------------------------------------------------------------------+ +// Weak stubs: invoked if no strong implementation is available +//--------------------------------------------------------------------+ +TU_ATTR_WEAK bool dcd_deinit(uint8_t rhport) { + (void) rhport; + return false; +} + +TU_ATTR_WEAK void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr) { + (void)rhport; + (void)eventid; + (void)in_isr; +} + +//--------------------------------------------------------------------+ +// Device Data +//--------------------------------------------------------------------+ + +// Invalid driver ID in itf2drv[] ep2drv[][] mapping +enum { DRVID_INVALID = 0xFFu }; + +typedef struct { + struct TU_ATTR_PACKED { + volatile uint8_t connected : 1; + volatile uint8_t addressed : 1; + volatile uint8_t suspended : 1; + + uint8_t remote_wakeup_en : 1; // enable/disable by host + uint8_t remote_wakeup_support : 1; // configuration descriptor's attribute + uint8_t self_powered : 1; // configuration descriptor's attribute + }; + volatile uint8_t cfg_num; // current active configuration (0x00 is not configured) + uint8_t speed; + volatile uint8_t setup_count; + + uint8_t itf2drv[CFG_TUD_INTERFACE_MAX]; // map interface number to driver (0xff is invalid) + uint8_t ep2drv[CFG_TUD_ENDPPOINT_MAX][2]; // map endpoint to driver ( 0xff is invalid ), can use only 4-bit each + + tu_edpt_state_t ep_status[CFG_TUD_ENDPPOINT_MAX][2]; + +}usbd_device_t; + +tu_static usbd_device_t _usbd_dev; + +//--------------------------------------------------------------------+ +// Class Driver +//--------------------------------------------------------------------+ +#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL + #define DRIVER_NAME(_name) .name = _name, +#else + #define DRIVER_NAME(_name) +#endif + +// Built-in class drivers +tu_static usbd_class_driver_t const _usbd_driver[] = { + #if CFG_TUD_CDC + { + DRIVER_NAME("CDC") + .init = cdcd_init, + .deinit = cdcd_deinit, + .reset = cdcd_reset, + .open = cdcd_open, + .control_xfer_cb = cdcd_control_xfer_cb, + .xfer_cb = cdcd_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_MSC + { + DRIVER_NAME("MSC") + .init = mscd_init, + .deinit = NULL, + .reset = mscd_reset, + .open = mscd_open, + .control_xfer_cb = mscd_control_xfer_cb, + .xfer_cb = mscd_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_HID + { + DRIVER_NAME("HID") + .init = hidd_init, + .deinit = hidd_deinit, + .reset = hidd_reset, + .open = hidd_open, + .control_xfer_cb = hidd_control_xfer_cb, + .xfer_cb = hidd_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_AUDIO + { + DRIVER_NAME("AUDIO") + .init = audiod_init, + .deinit = audiod_deinit, + .reset = audiod_reset, + .open = audiod_open, + .control_xfer_cb = audiod_control_xfer_cb, + .xfer_cb = audiod_xfer_cb, + .sof = audiod_sof_isr + }, + #endif + + #if CFG_TUD_VIDEO + { + DRIVER_NAME("VIDEO") + .init = videod_init, + .deinit = videod_deinit, + .reset = videod_reset, + .open = videod_open, + .control_xfer_cb = videod_control_xfer_cb, + .xfer_cb = videod_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_MIDI + { + DRIVER_NAME("MIDI") + .init = midid_init, + .deinit = midid_deinit, + .open = midid_open, + .reset = midid_reset, + .control_xfer_cb = midid_control_xfer_cb, + .xfer_cb = midid_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_VENDOR + { + DRIVER_NAME("VENDOR") + .init = vendord_init, + .deinit = vendord_deinit, + .reset = vendord_reset, + .open = vendord_open, + .control_xfer_cb = tud_vendor_control_xfer_cb, + .xfer_cb = vendord_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_USBTMC + { + DRIVER_NAME("TMC") + .init = usbtmcd_init_cb, + .deinit = usbtmcd_deinit, + .reset = usbtmcd_reset_cb, + .open = usbtmcd_open_cb, + .control_xfer_cb = usbtmcd_control_xfer_cb, + .xfer_cb = usbtmcd_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_DFU_RUNTIME + { + DRIVER_NAME("DFU-RUNTIME") + .init = dfu_rtd_init, + .deinit = dfu_rtd_deinit, + .reset = dfu_rtd_reset, + .open = dfu_rtd_open, + .control_xfer_cb = dfu_rtd_control_xfer_cb, + .xfer_cb = NULL, + .sof = NULL + }, + #endif + + #if CFG_TUD_DFU + { + DRIVER_NAME("DFU") + .init = dfu_moded_init, + .deinit = dfu_moded_deinit, + .reset = dfu_moded_reset, + .open = dfu_moded_open, + .control_xfer_cb = dfu_moded_control_xfer_cb, + .xfer_cb = NULL, + .sof = NULL + }, + #endif + + #if CFG_TUD_ECM_RNDIS || CFG_TUD_NCM + { + DRIVER_NAME("NET") + .init = netd_init, + .deinit = netd_deinit, + .reset = netd_reset, + .open = netd_open, + .control_xfer_cb = netd_control_xfer_cb, + .xfer_cb = netd_xfer_cb, + .sof = NULL, + }, + #endif + + #if CFG_TUD_BTH + { + DRIVER_NAME("BTH") + .init = btd_init, + .deinit = btd_deinit, + .reset = btd_reset, + .open = btd_open, + .control_xfer_cb = btd_control_xfer_cb, + .xfer_cb = btd_xfer_cb, + .sof = NULL + }, + #endif +}; + +enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(_usbd_driver) }; + +// Additional class drivers implemented by application +tu_static usbd_class_driver_t const * _app_driver = NULL; +tu_static uint8_t _app_driver_count = 0; + +#define TOTAL_DRIVER_COUNT (_app_driver_count + BUILTIN_DRIVER_COUNT) + +// virtually joins built-in and application drivers together. +// Application is positioned first to allow overwriting built-in ones. +TU_ATTR_ALWAYS_INLINE static inline usbd_class_driver_t const * get_driver(uint8_t drvid) { + usbd_class_driver_t const * driver = NULL; + if ( drvid < _app_driver_count ) { + // Application drivers + driver = &_app_driver[drvid]; + } else if ( drvid < TOTAL_DRIVER_COUNT && BUILTIN_DRIVER_COUNT > 0 ){ + driver = &_usbd_driver[drvid - _app_driver_count]; + } + return driver; +} + +//--------------------------------------------------------------------+ +// DCD Event +//--------------------------------------------------------------------+ + +enum { RHPORT_INVALID = 0xFFu }; +tu_static uint8_t _usbd_rhport = RHPORT_INVALID; + +// Event queue +// usbd_int_set() is used as mutex in OS NONE config +OSAL_QUEUE_DEF(usbd_int_set, _usbd_qdef, CFG_TUD_TASK_QUEUE_SZ, dcd_event_t); +tu_static osal_queue_t _usbd_q; + +// Mutex for claiming endpoint +#if OSAL_MUTEX_REQUIRED + tu_static osal_mutex_def_t _ubsd_mutexdef; + tu_static osal_mutex_t _usbd_mutex; +#else + #define _usbd_mutex NULL +#endif + +TU_ATTR_ALWAYS_INLINE static inline bool queue_event(dcd_event_t const * event, bool in_isr) { + TU_ASSERT(osal_queue_send(_usbd_q, event, in_isr)); + tud_event_hook_cb(event->rhport, event->event_id, in_isr); + return true; +} + +//--------------------------------------------------------------------+ +// Prototypes +//--------------------------------------------------------------------+ +static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request); +static bool process_set_config(uint8_t rhport, uint8_t cfg_num); +static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request); + +// from usbd_control.c +void usbd_control_reset(void); +void usbd_control_set_request(tusb_control_request_t const *request); +void usbd_control_set_complete_callback( usbd_control_xfer_cb_t fp ); +bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); + + +//--------------------------------------------------------------------+ +// Debug +//--------------------------------------------------------------------+ +#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL +tu_static char const* const _usbd_event_str[DCD_EVENT_COUNT] = { + "Invalid", + "Bus Reset", + "Unplugged", + "SOF", + "Suspend", + "Resume", + "Setup Received", + "Xfer Complete", + "Func Call" +}; + +// for usbd_control to print the name of control complete driver +void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback) { + for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) { + usbd_class_driver_t const* driver = get_driver(i); + if (driver && driver->control_xfer_cb == callback) { + TU_LOG_USBD("%s control complete\r\n", driver->name); + return; + } + } +} + +#endif + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ +tusb_speed_t tud_speed_get(void) { + return (tusb_speed_t) _usbd_dev.speed; +} + +bool tud_connected(void) { + return _usbd_dev.connected; +} + +bool tud_mounted(void) { + return _usbd_dev.cfg_num ? true : false; +} + +bool tud_suspended(void) { + return _usbd_dev.suspended; +} + +bool tud_remote_wakeup(void) { + // only wake up host if this feature is supported and enabled and we are suspended + TU_VERIFY (_usbd_dev.suspended && _usbd_dev.remote_wakeup_support && _usbd_dev.remote_wakeup_en); + dcd_remote_wakeup(_usbd_rhport); + return true; +} + +bool tud_disconnect(void) { + TU_VERIFY(dcd_disconnect); + dcd_disconnect(_usbd_rhport); + return true; +} + +bool tud_connect(void) { + TU_VERIFY(dcd_connect); + dcd_connect(_usbd_rhport); + return true; +} + +//--------------------------------------------------------------------+ +// USBD Task +//--------------------------------------------------------------------+ +bool tud_inited(void) { + return _usbd_rhport != RHPORT_INVALID; +} + +bool tud_init(uint8_t rhport) { + // skip if already initialized + if (tud_inited()) return true; + + TU_LOG_USBD("USBD init on controller %u\r\n", rhport); + TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(usbd_device_t)); + TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(dcd_event_t)); + TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(tu_fifo_t)); + TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(tu_edpt_stream_t)); + + tu_varclr(&_usbd_dev); + +#if OSAL_MUTEX_REQUIRED + // Init device mutex + _usbd_mutex = osal_mutex_create(&_ubsd_mutexdef); + TU_ASSERT(_usbd_mutex); +#endif + + // Init device queue & task + _usbd_q = osal_queue_create(&_usbd_qdef); + TU_ASSERT(_usbd_q); + + // Get application driver if available + if (usbd_app_driver_get_cb) { + _app_driver = usbd_app_driver_get_cb(&_app_driver_count); + } + + // Init class drivers + for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) { + usbd_class_driver_t const* driver = get_driver(i); + TU_ASSERT(driver && driver->init); + TU_LOG_USBD("%s init\r\n", driver->name); + driver->init(); + } + + _usbd_rhport = rhport; + + // Init device controller driver + dcd_init(rhport); + dcd_int_enable(rhport); + + return true; +} + +bool tud_deinit(uint8_t rhport) { + // skip if not initialized + if (!tud_inited()) return true; + + TU_LOG_USBD("USBD deinit on controller %u\r\n", rhport); + + // Deinit device controller driver + dcd_int_disable(rhport); + dcd_disconnect(rhport); + dcd_deinit(rhport); + + // Deinit class drivers + for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) { + usbd_class_driver_t const* driver = get_driver(i); + if(driver && driver->deinit) { + TU_LOG_USBD("%s deinit\r\n", driver->name); + driver->deinit(); + } + } + + // Deinit device queue & task + osal_queue_delete(_usbd_q); + _usbd_q = NULL; + +#if OSAL_MUTEX_REQUIRED + // TODO make sure there is no task waiting on this mutex + osal_mutex_delete(_usbd_mutex); + _usbd_mutex = NULL; +#endif + + _usbd_rhport = RHPORT_INVALID; + + return true; +} + +static void configuration_reset(uint8_t rhport) { + for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) { + usbd_class_driver_t const* driver = get_driver(i); + TU_ASSERT(driver,); + driver->reset(rhport); + } + + tu_varclr(&_usbd_dev); + memset(_usbd_dev.itf2drv, DRVID_INVALID, sizeof(_usbd_dev.itf2drv)); // invalid mapping + memset(_usbd_dev.ep2drv, DRVID_INVALID, sizeof(_usbd_dev.ep2drv)); // invalid mapping +} + +static void usbd_reset(uint8_t rhport) { + configuration_reset(rhport); + usbd_control_reset(); +} + +bool tud_task_event_ready(void) { + // Skip if stack is not initialized + if (!tud_inited()) return false; + return !osal_queue_empty(_usbd_q); +} + +/* USB Device Driver task + * This top level thread manages all device controller event and delegates events to class-specific drivers. + * This should be called periodically within the mainloop or rtos thread. + * + int main(void) { + application_init(); + tusb_init(); + + while(1) { // the mainloop + application_code(); + tud_task(); // tinyusb device task + } + } + */ +void tud_task_ext(uint32_t timeout_ms, bool in_isr) { + (void) in_isr; // not implemented yet + + // Skip if stack is not initialized + if (!tud_inited()) return; + + // Loop until there is no more events in the queue + while (1) { + dcd_event_t event; + if (!osal_queue_receive(_usbd_q, &event, timeout_ms)) return; + +#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL + if (event.event_id == DCD_EVENT_SETUP_RECEIVED) TU_LOG_USBD("\r\n"); // extra line for setup + TU_LOG_USBD("USBD %s ", event.event_id < DCD_EVENT_COUNT ? _usbd_event_str[event.event_id] : "CORRUPTED"); +#endif + + switch (event.event_id) { + case DCD_EVENT_BUS_RESET: + TU_LOG_USBD(": %s Speed\r\n", tu_str_speed[event.bus_reset.speed]); + usbd_reset(event.rhport); + _usbd_dev.speed = event.bus_reset.speed; + break; + + case DCD_EVENT_UNPLUGGED: + TU_LOG_USBD("\r\n"); + usbd_reset(event.rhport); + if (tud_umount_cb) tud_umount_cb(); + break; + + case DCD_EVENT_SETUP_RECEIVED: + _usbd_dev.setup_count--; + TU_LOG_BUF(CFG_TUD_LOG_LEVEL, &event.setup_received, 8); + if (_usbd_dev.setup_count) { + TU_LOG_USBD(" Skipped since there is other SETUP in queue\r\n"); + break; + } + + // Mark as connected after receiving 1st setup packet. + // But it is easier to set it every time instead of wasting time to check then set + _usbd_dev.connected = 1; + + // mark both in & out control as free + _usbd_dev.ep_status[0][TUSB_DIR_OUT].busy = 0; + _usbd_dev.ep_status[0][TUSB_DIR_OUT].claimed = 0; + _usbd_dev.ep_status[0][TUSB_DIR_IN].busy = 0; + _usbd_dev.ep_status[0][TUSB_DIR_IN].claimed = 0; + + // Process control request + if (!process_control_request(event.rhport, &event.setup_received)) { + TU_LOG_USBD(" Stall EP0\r\n"); + // Failed -> stall both control endpoint IN and OUT + dcd_edpt_stall(event.rhport, 0); + dcd_edpt_stall(event.rhport, 0 | TUSB_DIR_IN_MASK); + } + break; + + case DCD_EVENT_XFER_COMPLETE: { + // Invoke the class callback associated with the endpoint address + uint8_t const ep_addr = event.xfer_complete.ep_addr; + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const ep_dir = tu_edpt_dir(ep_addr); + + TU_LOG_USBD("on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len); + + _usbd_dev.ep_status[epnum][ep_dir].busy = 0; + _usbd_dev.ep_status[epnum][ep_dir].claimed = 0; + + if (0 == epnum) { + usbd_control_xfer_cb(event.rhport, ep_addr, (xfer_result_t) event.xfer_complete.result, + event.xfer_complete.len); + } else { + usbd_class_driver_t const* driver = get_driver(_usbd_dev.ep2drv[epnum][ep_dir]); + TU_ASSERT(driver,); + + TU_LOG_USBD(" %s xfer callback\r\n", driver->name); + driver->xfer_cb(event.rhport, ep_addr, (xfer_result_t) event.xfer_complete.result, event.xfer_complete.len); + } + break; + } + + case DCD_EVENT_SUSPEND: + // NOTE: When plugging/unplugging device, the D+/D- state are unstable and + // can accidentally meet the SUSPEND condition ( Bus Idle for 3ms ), which result in a series of event + // e.g suspend -> resume -> unplug/plug. Skip suspend/resume if not connected + if (_usbd_dev.connected) { + TU_LOG_USBD(": Remote Wakeup = %u\r\n", _usbd_dev.remote_wakeup_en); + if (tud_suspend_cb) tud_suspend_cb(_usbd_dev.remote_wakeup_en); + } else { + TU_LOG_USBD(" Skipped\r\n"); + } + break; + + case DCD_EVENT_RESUME: + if (_usbd_dev.connected) { + TU_LOG_USBD("\r\n"); + if (tud_resume_cb) tud_resume_cb(); + } else { + TU_LOG_USBD(" Skipped\r\n"); + } + break; + + case USBD_EVENT_FUNC_CALL: + TU_LOG_USBD("\r\n"); + if (event.func_call.func) event.func_call.func(event.func_call.param); + break; + + case DCD_EVENT_SOF: + default: + TU_BREAKPOINT(); + break; + } + +#if CFG_TUSB_OS != OPT_OS_NONE && CFG_TUSB_OS != OPT_OS_PICO + // return if there is no more events, for application to run other background + if (osal_queue_empty(_usbd_q)) return; +#endif + } +} + +//--------------------------------------------------------------------+ +// Control Request Parser & Handling +//--------------------------------------------------------------------+ + +// Helper to invoke class driver control request handler +static bool invoke_class_control(uint8_t rhport, usbd_class_driver_t const * driver, tusb_control_request_t const * request) { + usbd_control_set_complete_callback(driver->control_xfer_cb); + TU_LOG_USBD(" %s control request\r\n", driver->name); + return driver->control_xfer_cb(rhport, CONTROL_STAGE_SETUP, request); +} + +// This handles the actual request and its response. +// Returns false if unable to complete the request, causing caller to stall control endpoints. +static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request) { + usbd_control_set_complete_callback(NULL); + TU_ASSERT(p_request->bmRequestType_bit.type < TUSB_REQ_TYPE_INVALID); + + // Vendor request + if ( p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR ) { + TU_VERIFY(tud_vendor_control_xfer_cb); + + usbd_control_set_complete_callback(tud_vendor_control_xfer_cb); + return tud_vendor_control_xfer_cb(rhport, CONTROL_STAGE_SETUP, p_request); + } + +#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL + if (TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type && p_request->bRequest <= TUSB_REQ_SYNCH_FRAME) { + TU_LOG_USBD(" %s", tu_str_std_request[p_request->bRequest]); + if (TUSB_REQ_GET_DESCRIPTOR != p_request->bRequest) TU_LOG_USBD("\r\n"); + } +#endif + + switch ( p_request->bmRequestType_bit.recipient ) { + //------------- Device Requests e.g in enumeration -------------// + case TUSB_REQ_RCPT_DEVICE: + if ( TUSB_REQ_TYPE_CLASS == p_request->bmRequestType_bit.type ) { + uint8_t const itf = tu_u16_low(p_request->wIndex); + TU_VERIFY(itf < TU_ARRAY_SIZE(_usbd_dev.itf2drv)); + + usbd_class_driver_t const * driver = get_driver(_usbd_dev.itf2drv[itf]); + TU_VERIFY(driver); + + // forward to class driver: "non-STD request to Interface" + return invoke_class_control(rhport, driver, p_request); + } + + if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type ) { + // Non standard request is not supported + TU_BREAKPOINT(); + return false; + } + + switch ( p_request->bRequest ) { + case TUSB_REQ_SET_ADDRESS: + // Depending on mcu, status phase could be sent either before or after changing device address, + // or even require stack to not response with status at all + // Therefore DCD must take full responsibility to response and include zlp status packet if needed. + usbd_control_set_request(p_request); // set request since DCD has no access to tud_control_status() API + dcd_set_address(rhport, (uint8_t) p_request->wValue); + // skip tud_control_status() + _usbd_dev.addressed = 1; + break; + + case TUSB_REQ_GET_CONFIGURATION: { + uint8_t cfg_num = _usbd_dev.cfg_num; + tud_control_xfer(rhport, p_request, &cfg_num, 1); + } + break; + + case TUSB_REQ_SET_CONFIGURATION: { + uint8_t const cfg_num = (uint8_t) p_request->wValue; + + // Only process if new configure is different + if (_usbd_dev.cfg_num != cfg_num) { + if ( _usbd_dev.cfg_num ) { + // already configured: need to clear all endpoints and driver first + TU_LOG_USBD(" Clear current Configuration (%u) before switching\r\n", _usbd_dev.cfg_num); + + // close all non-control endpoints, cancel all pending transfers if any + dcd_edpt_close_all(rhport); + + // close all drivers and current configured state except bus speed + uint8_t const speed = _usbd_dev.speed; + configuration_reset(rhport); + + _usbd_dev.speed = speed; // restore speed + } + + // Handle the new configuration and execute the corresponding callback + if ( cfg_num ) { + // switch to new configuration if not zero + TU_ASSERT( process_set_config(rhport, cfg_num) ); + if ( tud_mount_cb ) tud_mount_cb(); + } else { + if ( tud_umount_cb ) tud_umount_cb(); + } + } + + _usbd_dev.cfg_num = cfg_num; + tud_control_status(rhport, p_request); + } + break; + + case TUSB_REQ_GET_DESCRIPTOR: + TU_VERIFY( process_get_descriptor(rhport, p_request) ); + break; + + case TUSB_REQ_SET_FEATURE: + // Only support remote wakeup for device feature + TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue); + + TU_LOG_USBD(" Enable Remote Wakeup\r\n"); + + // Host may enable remote wake up before suspending especially HID device + _usbd_dev.remote_wakeup_en = true; + tud_control_status(rhport, p_request); + break; + + case TUSB_REQ_CLEAR_FEATURE: + // Only support remote wakeup for device feature + TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue); + + TU_LOG_USBD(" Disable Remote Wakeup\r\n"); + + // Host may disable remote wake up after resuming + _usbd_dev.remote_wakeup_en = false; + tud_control_status(rhport, p_request); + break; + + case TUSB_REQ_GET_STATUS: { + // Device status bit mask + // - Bit 0: Self Powered + // - Bit 1: Remote Wakeup enabled + uint16_t status = (uint16_t) ((_usbd_dev.self_powered ? 1u : 0u) | (_usbd_dev.remote_wakeup_en ? 2u : 0u)); + tud_control_xfer(rhport, p_request, &status, 2); + break; + } + + // Unknown/Unsupported request + default: TU_BREAKPOINT(); return false; + } + break; + + //------------- Class/Interface Specific Request -------------// + case TUSB_REQ_RCPT_INTERFACE: { + uint8_t const itf = tu_u16_low(p_request->wIndex); + TU_VERIFY(itf < TU_ARRAY_SIZE(_usbd_dev.itf2drv)); + + usbd_class_driver_t const * driver = get_driver(_usbd_dev.itf2drv[itf]); + TU_VERIFY(driver); + + // all requests to Interface (STD or Class) is forwarded to class driver. + // notable requests are: GET HID REPORT DESCRIPTOR, SET_INTERFACE, GET_INTERFACE + if ( !invoke_class_control(rhport, driver, p_request) ) { + // For GET_INTERFACE and SET_INTERFACE, it is mandatory to respond even if the class + // driver doesn't use alternate settings or implement this + TU_VERIFY(TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type); + + switch(p_request->bRequest) { + case TUSB_REQ_GET_INTERFACE: + case TUSB_REQ_SET_INTERFACE: + // Clear complete callback if driver set since it can also stall the request. + usbd_control_set_complete_callback(NULL); + + if (TUSB_REQ_GET_INTERFACE == p_request->bRequest) { + uint8_t alternate = 0; + tud_control_xfer(rhport, p_request, &alternate, 1); + }else { + tud_control_status(rhport, p_request); + } + break; + + default: return false; + } + } + break; + } + + //------------- Endpoint Request -------------// + case TUSB_REQ_RCPT_ENDPOINT: { + uint8_t const ep_addr = tu_u16_low(p_request->wIndex); + uint8_t const ep_num = tu_edpt_number(ep_addr); + uint8_t const ep_dir = tu_edpt_dir(ep_addr); + + TU_ASSERT(ep_num < TU_ARRAY_SIZE(_usbd_dev.ep2drv) ); + usbd_class_driver_t const * driver = get_driver(_usbd_dev.ep2drv[ep_num][ep_dir]); + + if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type ) { + // Forward class request to its driver + TU_VERIFY(driver); + return invoke_class_control(rhport, driver, p_request); + } else { + // Handle STD request to endpoint + switch ( p_request->bRequest ) { + case TUSB_REQ_GET_STATUS: { + uint16_t status = usbd_edpt_stalled(rhport, ep_addr) ? 0x0001 : 0x0000; + tud_control_xfer(rhport, p_request, &status, 2); + } + break; + + case TUSB_REQ_CLEAR_FEATURE: + case TUSB_REQ_SET_FEATURE: { + if ( TUSB_REQ_FEATURE_EDPT_HALT == p_request->wValue ) { + if ( TUSB_REQ_CLEAR_FEATURE == p_request->bRequest ) { + usbd_edpt_clear_stall(rhport, ep_addr); + }else { + usbd_edpt_stall(rhport, ep_addr); + } + } + + if (driver) { + // Some classes such as USBTMC needs to clear/re-init its buffer when receiving CLEAR_FEATURE request + // We will also forward std request targeted endpoint to class drivers as well + + // STD request must always be ACKed regardless of driver returned value + // Also clear complete callback if driver set since it can also stall the request. + (void) invoke_class_control(rhport, driver, p_request); + usbd_control_set_complete_callback(NULL); + + // skip ZLP status if driver already did that + if ( !_usbd_dev.ep_status[0][TUSB_DIR_IN].busy ) tud_control_status(rhport, p_request); + } + } + break; + + // Unknown/Unsupported request + default: + TU_BREAKPOINT(); + return false; + } + } + } + break; + + // Unknown recipient + default: + TU_BREAKPOINT(); + return false; + } + + return true; +} + +// Process Set Configure Request +// This function parse configuration descriptor & open drivers accordingly +static bool process_set_config(uint8_t rhport, uint8_t cfg_num) +{ + // index is cfg_num-1 + tusb_desc_configuration_t const * desc_cfg = (tusb_desc_configuration_t const *) tud_descriptor_configuration_cb(cfg_num-1); + TU_ASSERT(desc_cfg != NULL && desc_cfg->bDescriptorType == TUSB_DESC_CONFIGURATION); + + // Parse configuration descriptor + _usbd_dev.remote_wakeup_support = (desc_cfg->bmAttributes & TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP) ? 1u : 0u; + _usbd_dev.self_powered = (desc_cfg->bmAttributes & TUSB_DESC_CONFIG_ATT_SELF_POWERED ) ? 1u : 0u; + + // Parse interface descriptor + uint8_t const * p_desc = ((uint8_t const*) desc_cfg) + sizeof(tusb_desc_configuration_t); + uint8_t const * desc_end = ((uint8_t const*) desc_cfg) + tu_le16toh(desc_cfg->wTotalLength); + + while( p_desc < desc_end ) + { + uint8_t assoc_itf_count = 1; + + // Class will always starts with Interface Association (if any) and then Interface descriptor + if ( TUSB_DESC_INTERFACE_ASSOCIATION == tu_desc_type(p_desc) ) + { + tusb_desc_interface_assoc_t const * desc_iad = (tusb_desc_interface_assoc_t const *) p_desc; + assoc_itf_count = desc_iad->bInterfaceCount; + + p_desc = tu_desc_next(p_desc); // next to Interface + + // IAD's first interface number and class should match with opened interface + //TU_ASSERT(desc_iad->bFirstInterface == desc_itf->bInterfaceNumber && + // desc_iad->bFunctionClass == desc_itf->bInterfaceClass); + } + + TU_ASSERT( TUSB_DESC_INTERFACE == tu_desc_type(p_desc) ); + tusb_desc_interface_t const * desc_itf = (tusb_desc_interface_t const*) p_desc; + + // Find driver for this interface + uint16_t const remaining_len = (uint16_t) (desc_end-p_desc); + uint8_t drv_id; + for (drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++) + { + usbd_class_driver_t const *driver = get_driver(drv_id); + TU_ASSERT(driver); + uint16_t const drv_len = driver->open(rhport, desc_itf, remaining_len); + + if ( (sizeof(tusb_desc_interface_t) <= drv_len) && (drv_len <= remaining_len) ) + { + // Open successfully + TU_LOG_USBD(" %s opened\r\n", driver->name); + + // Some drivers use 2 or more interfaces but may not have IAD e.g MIDI (always) or + // BTH (even CDC) with class in device descriptor (single interface) + if ( assoc_itf_count == 1) + { + #if CFG_TUD_CDC + if ( driver->open == cdcd_open ) assoc_itf_count = 2; + #endif + + #if CFG_TUD_MIDI + if ( driver->open == midid_open ) assoc_itf_count = 2; + #endif + + #if CFG_TUD_BTH && CFG_TUD_BTH_ISO_ALT_COUNT + if ( driver->open == btd_open ) assoc_itf_count = 2; + #endif + } + + // bind (associated) interfaces to found driver + for(uint8_t i=0; ibInterfaceNumber+i; + + // Interface number must not be used already + TU_ASSERT(DRVID_INVALID == _usbd_dev.itf2drv[itf_num]); + _usbd_dev.itf2drv[itf_num] = drv_id; + } + + // bind all endpoints to found driver + tu_edpt_bind_driver(_usbd_dev.ep2drv, desc_itf, drv_len, drv_id); + + // next Interface + p_desc += drv_len; + + break; // exit driver find loop + } + } + + // Failed if there is no supported drivers + TU_ASSERT(drv_id < TOTAL_DRIVER_COUNT); + } + + return true; +} + +// return descriptor's buffer and update desc_len +static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request) +{ + tusb_desc_type_t const desc_type = (tusb_desc_type_t) tu_u16_high(p_request->wValue); + uint8_t const desc_index = tu_u16_low( p_request->wValue ); + + switch(desc_type) + { + case TUSB_DESC_DEVICE: + { + TU_LOG_USBD(" Device\r\n"); + + void* desc_device = (void*) (uintptr_t) tud_descriptor_device_cb(); + + // Only response with exactly 1 Packet if: not addressed and host requested more data than device descriptor has. + // This only happens with the very first get device descriptor and EP0 size = 8 or 16. + if ((CFG_TUD_ENDPOINT0_SIZE < sizeof(tusb_desc_device_t)) && !_usbd_dev.addressed && + ((tusb_control_request_t const*) p_request)->wLength > sizeof(tusb_desc_device_t)) + { + // Hack here: we modify the request length to prevent usbd_control response with zlp + // since we are responding with 1 packet & less data than wLength. + tusb_control_request_t mod_request = *p_request; + mod_request.wLength = CFG_TUD_ENDPOINT0_SIZE; + + return tud_control_xfer(rhport, &mod_request, desc_device, CFG_TUD_ENDPOINT0_SIZE); + }else + { + return tud_control_xfer(rhport, p_request, desc_device, sizeof(tusb_desc_device_t)); + } + } + // break; // unreachable + + case TUSB_DESC_BOS: + { + TU_LOG_USBD(" BOS\r\n"); + + // requested by host if USB > 2.0 ( i.e 2.1 or 3.x ) + if (!tud_descriptor_bos_cb) return false; + + uintptr_t desc_bos = (uintptr_t) tud_descriptor_bos_cb(); + TU_ASSERT(desc_bos); + + // Use offsetof to avoid pointer to the odd/misaligned address + uint16_t const total_len = tu_le16toh( tu_unaligned_read16((const void*) (desc_bos + offsetof(tusb_desc_bos_t, wTotalLength))) ); + + return tud_control_xfer(rhport, p_request, (void*) desc_bos, total_len); + } + // break; // unreachable + + case TUSB_DESC_CONFIGURATION: + case TUSB_DESC_OTHER_SPEED_CONFIG: + { + uintptr_t desc_config; + + if ( desc_type == TUSB_DESC_CONFIGURATION ) + { + TU_LOG_USBD(" Configuration[%u]\r\n", desc_index); + desc_config = (uintptr_t) tud_descriptor_configuration_cb(desc_index); + }else + { + // Host only request this after getting Device Qualifier descriptor + TU_LOG_USBD(" Other Speed Configuration\r\n"); + TU_VERIFY( tud_descriptor_other_speed_configuration_cb ); + desc_config = (uintptr_t) tud_descriptor_other_speed_configuration_cb(desc_index); + } + + TU_ASSERT(desc_config); + + // Use offsetof to avoid pointer to the odd/misaligned address + uint16_t const total_len = tu_le16toh( tu_unaligned_read16((const void*) (desc_config + offsetof(tusb_desc_configuration_t, wTotalLength))) ); + + return tud_control_xfer(rhport, p_request, (void*) desc_config, total_len); + } + // break; // unreachable + + case TUSB_DESC_STRING: + { + TU_LOG_USBD(" String[%u]\r\n", desc_index); + + // String Descriptor always uses the desc set from user + uint8_t const* desc_str = (uint8_t const*) tud_descriptor_string_cb(desc_index, tu_le16toh(p_request->wIndex)); + TU_VERIFY(desc_str); + + // first byte of descriptor is its size + return tud_control_xfer(rhport, p_request, (void*) (uintptr_t) desc_str, tu_desc_len(desc_str)); + } + // break; // unreachable + + case TUSB_DESC_DEVICE_QUALIFIER: + { + TU_LOG_USBD(" Device Qualifier\r\n"); + + TU_VERIFY( tud_descriptor_device_qualifier_cb ); + + uint8_t const* desc_qualifier = tud_descriptor_device_qualifier_cb(); + TU_VERIFY(desc_qualifier); + + // first byte of descriptor is its size + return tud_control_xfer(rhport, p_request, (void*) (uintptr_t) desc_qualifier, tu_desc_len(desc_qualifier)); + } + // break; // unreachable + + default: return false; + } +} + +//--------------------------------------------------------------------+ +// DCD Event Handler +//--------------------------------------------------------------------+ +TU_ATTR_FAST_FUNC void dcd_event_handler(dcd_event_t const* event, bool in_isr) { + bool send = false; + switch (event->event_id) { + case DCD_EVENT_UNPLUGGED: + _usbd_dev.connected = 0; + _usbd_dev.addressed = 0; + _usbd_dev.cfg_num = 0; + _usbd_dev.suspended = 0; + send = true; + break; + + case DCD_EVENT_SUSPEND: + // NOTE: When plugging/unplugging device, the D+/D- state are unstable and + // can accidentally meet the SUSPEND condition ( Bus Idle for 3ms ). + // In addition, some MCUs such as SAMD or boards that haven no VBUS detection cannot distinguish + // suspended vs disconnected. We will skip handling SUSPEND/RESUME event if not currently connected + if (_usbd_dev.connected) { + _usbd_dev.suspended = 1; + send = true; + } + break; + + case DCD_EVENT_RESUME: + // skip event if not connected (especially required for SAMD) + if (_usbd_dev.connected) { + _usbd_dev.suspended = 0; + send = true; + } + break; + + case DCD_EVENT_SOF: + // Some MCUs after running dcd_remote_wakeup() does not have way to detect the end of remote wakeup + // which last 1-15 ms. DCD can use SOF as a clear indicator that bus is back to operational + if (_usbd_dev.suspended) { + _usbd_dev.suspended = 0; + + dcd_event_t const event_resume = {.rhport = event->rhport, .event_id = DCD_EVENT_RESUME}; + queue_event(&event_resume, in_isr); + } + + // SOF driver handler in ISR context + for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) { + usbd_class_driver_t const* driver = get_driver(i); + if (driver && driver->sof) { + driver->sof(event->rhport, event->sof.frame_count); + } + } + + // skip osal queue for SOF in usbd task + break; + + case DCD_EVENT_SETUP_RECEIVED: + _usbd_dev.setup_count++; + send = true; + break; + + default: + send = true; + break; + } + + if (send) { + queue_event(event, in_isr); + } +} + +//--------------------------------------------------------------------+ +// USBD API For Class Driver +//--------------------------------------------------------------------+ + +void usbd_int_set(bool enabled) +{ + if (enabled) + { + dcd_int_enable(_usbd_rhport); + }else + { + dcd_int_disable(_usbd_rhport); + } +} + +// Parse consecutive endpoint descriptors (IN & OUT) +bool usbd_open_edpt_pair(uint8_t rhport, uint8_t const* p_desc, uint8_t ep_count, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in) +{ + for(int i=0; ibDescriptorType && xfer_type == desc_ep->bmAttributes.xfer); + TU_ASSERT(usbd_edpt_open(rhport, desc_ep)); + + if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN ) + { + (*ep_in) = desc_ep->bEndpointAddress; + }else + { + (*ep_out) = desc_ep->bEndpointAddress; + } + + p_desc = tu_desc_next(p_desc); + } + + return true; +} + +// Helper to defer an isr function +void usbd_defer_func(osal_task_func_t func, void* param, bool in_isr) { + dcd_event_t event = { + .rhport = 0, + .event_id = USBD_EVENT_FUNC_CALL, + }; + event.func_call.func = func; + event.func_call.param = param; + + queue_event(&event, in_isr); +} + +//--------------------------------------------------------------------+ +// USBD Endpoint API +//--------------------------------------------------------------------+ + +bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep) { + rhport = _usbd_rhport; + + TU_ASSERT(tu_edpt_number(desc_ep->bEndpointAddress) < CFG_TUD_ENDPPOINT_MAX); + TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t) _usbd_dev.speed)); + + return dcd_edpt_open(rhport, desc_ep); +} + +bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr) { + (void) rhport; + + // TODO add this check later, also make sure we don't starve an out endpoint while suspending + // TU_VERIFY(tud_ready()); + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + tu_edpt_state_t* ep_state = &_usbd_dev.ep_status[epnum][dir]; + + return tu_edpt_claim(ep_state, _usbd_mutex); +} + +bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr) { + (void) rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + tu_edpt_state_t* ep_state = &_usbd_dev.ep_status[epnum][dir]; + + return tu_edpt_release(ep_state, _usbd_mutex); +} + +bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { + rhport = _usbd_rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + // TODO skip ready() check for now since enumeration also use this API + // TU_VERIFY(tud_ready()); + + TU_LOG_USBD(" Queue EP %02X with %u bytes ...\r\n", ep_addr, total_bytes); +#if CFG_TUD_LOG_LEVEL >= 3 + if(dir == TUSB_DIR_IN) { + TU_LOG_MEM(CFG_TUD_LOG_LEVEL, buffer, total_bytes, 2); + } +#endif + + // Attempt to transfer on a busy endpoint, sound like an race condition ! + TU_ASSERT(_usbd_dev.ep_status[epnum][dir].busy == 0); + + // Set busy first since the actual transfer can be complete before dcd_edpt_xfer() + // could return and USBD task can preempt and clear the busy + _usbd_dev.ep_status[epnum][dir].busy = 1; + + if (dcd_edpt_xfer(rhport, ep_addr, buffer, total_bytes)) { + return true; + } else { + // DCD error, mark endpoint as ready to allow next transfer + _usbd_dev.ep_status[epnum][dir].busy = 0; + _usbd_dev.ep_status[epnum][dir].claimed = 0; + TU_LOG_USBD("FAILED\r\n"); + TU_BREAKPOINT(); + return false; + } +} + +// The number of bytes has to be given explicitly to allow more flexible control of how many +// bytes should be written and second to keep the return value free to give back a boolean +// success message. If total_bytes is too big, the FIFO will copy only what is available +// into the USB buffer! +bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t total_bytes) { + rhport = _usbd_rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + TU_LOG_USBD(" Queue ISO EP %02X with %u bytes ... ", ep_addr, total_bytes); + + // Attempt to transfer on a busy endpoint, sound like an race condition ! + TU_ASSERT(_usbd_dev.ep_status[epnum][dir].busy == 0); + + // Set busy first since the actual transfer can be complete before dcd_edpt_xfer() could return + // and usbd task can preempt and clear the busy + _usbd_dev.ep_status[epnum][dir].busy = 1; + + if (dcd_edpt_xfer_fifo(rhport, ep_addr, ff, total_bytes)) { + TU_LOG_USBD("OK\r\n"); + return true; + } else { + // DCD error, mark endpoint as ready to allow next transfer + _usbd_dev.ep_status[epnum][dir].busy = 0; + _usbd_dev.ep_status[epnum][dir].claimed = 0; + TU_LOG_USBD("failed\r\n"); + TU_BREAKPOINT(); + return false; + } +} + +bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr) { + (void) rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + return _usbd_dev.ep_status[epnum][dir].busy; +} + +void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { + rhport = _usbd_rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + // only stalled if currently cleared + TU_LOG_USBD(" Stall EP %02X\r\n", ep_addr); + dcd_edpt_stall(rhport, ep_addr); + _usbd_dev.ep_status[epnum][dir].stalled = 1; + _usbd_dev.ep_status[epnum][dir].busy = 1; +} + +void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { + rhport = _usbd_rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + // only clear if currently stalled + TU_LOG_USBD(" Clear Stall EP %02X\r\n", ep_addr); + dcd_edpt_clear_stall(rhport, ep_addr); + _usbd_dev.ep_status[epnum][dir].stalled = 0; + _usbd_dev.ep_status[epnum][dir].busy = 0; +} + +bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr) { + (void) rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + return _usbd_dev.ep_status[epnum][dir].stalled; +} + +/** + * usbd_edpt_close will disable an endpoint. + * In progress transfers on this EP may be delivered after this call. + */ +void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr) { + rhport = _usbd_rhport; + + TU_ASSERT(dcd_edpt_close, /**/); + TU_LOG_USBD(" CLOSING Endpoint: 0x%02X\r\n", ep_addr); + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + dcd_edpt_close(rhport, ep_addr); + _usbd_dev.ep_status[epnum][dir].stalled = 0; + _usbd_dev.ep_status[epnum][dir].busy = 0; + _usbd_dev.ep_status[epnum][dir].claimed = 0; + + return; +} + +void usbd_sof_enable(uint8_t rhport, bool en) { + rhport = _usbd_rhport; + + // TODO: Check needed if all drivers including the user sof_cb does not need an active SOF ISR any more. + // Only if all drivers switched off SOF calls the SOF interrupt may be disabled + dcd_sof_enable(rhport, en); +} + +bool usbd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + rhport = _usbd_rhport; + + TU_ASSERT(dcd_edpt_iso_alloc); + TU_ASSERT(tu_edpt_number(ep_addr) < CFG_TUD_ENDPPOINT_MAX); + + return dcd_edpt_iso_alloc(rhport, ep_addr, largest_packet_size); +} + +bool usbd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep) { + rhport = _usbd_rhport; + + uint8_t const epnum = tu_edpt_number(desc_ep->bEndpointAddress); + uint8_t const dir = tu_edpt_dir(desc_ep->bEndpointAddress); + + TU_ASSERT(dcd_edpt_iso_activate); + TU_ASSERT(epnum < CFG_TUD_ENDPPOINT_MAX); + TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t) _usbd_dev.speed)); + + _usbd_dev.ep_status[epnum][dir].stalled = 0; + _usbd_dev.ep_status[epnum][dir].busy = 0; + _usbd_dev.ep_status[epnum][dir].claimed = 0; + return dcd_edpt_iso_activate(rhport, desc_ep); +} + +#endif diff --git a/test-devices/composite-stm32/lib/tinyusb/device/usbd.h b/test-devices/composite-stm32/lib/tinyusb/device/usbd.h new file mode 100644 index 00000000..f3673404 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/device/usbd.h @@ -0,0 +1,872 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_USBD_H_ +#define _TUSB_USBD_H_ + +#include "common/tusb_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ + +// Init device stack on roothub port +bool tud_init (uint8_t rhport); + +// Deinit device stack on roothub port +bool tud_deinit(uint8_t rhport); + +// Check if device stack is already initialized +bool tud_inited(void); + +// Task function should be called in main/rtos loop, extended version of tud_task() +// - timeout_ms: millisecond to wait, zero = no wait, 0xFFFFFFFF = wait forever +// - in_isr: if function is called in ISR +void tud_task_ext(uint32_t timeout_ms, bool in_isr); + +// Task function should be called in main/rtos loop +TU_ATTR_ALWAYS_INLINE static inline +void tud_task (void) { + tud_task_ext(UINT32_MAX, false); +} + +// Check if there is pending events need processing by tud_task() +bool tud_task_event_ready(void); + +#ifndef _TUSB_DCD_H_ +extern void dcd_int_handler(uint8_t rhport); +#endif + +// Interrupt handler, name alias to DCD +#define tud_int_handler dcd_int_handler + +// Get current bus speed +tusb_speed_t tud_speed_get(void); + +// Check if device is connected (may not mounted/configured yet) +// True if just got out of Bus Reset and received the very first data from host +bool tud_connected(void); + +// Check if device is connected and configured +bool tud_mounted(void); + +// Check if device is suspended +bool tud_suspended(void); + +// Check if device is ready to transfer +TU_ATTR_ALWAYS_INLINE static inline +bool tud_ready(void) { + return tud_mounted() && !tud_suspended(); +} + +// Remote wake up host, only if suspended and enabled by host +bool tud_remote_wakeup(void); + +// Enable pull-up resistor on D+ D- +// Return false on unsupported MCUs +bool tud_disconnect(void); + +// Disable pull-up resistor on D+ D- +// Return false on unsupported MCUs +bool tud_connect(void); + +// Carry out Data and Status stage of control transfer +// - If len = 0, it is equivalent to sending status only +// - If len > wLength : it will be truncated +bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, void* buffer, uint16_t len); + +// Send STATUS (zero length) packet +bool tud_control_status(uint8_t rhport, tusb_control_request_t const * request); + +//--------------------------------------------------------------------+ +// Application Callbacks (WEAK is optional) +//--------------------------------------------------------------------+ + +// Invoked when received GET DEVICE DESCRIPTOR request +// Application return pointer to descriptor +uint8_t const * tud_descriptor_device_cb(void); + +// Invoked when received GET CONFIGURATION DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint8_t const * tud_descriptor_configuration_cb(uint8_t index); + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid); + +// Invoked when received GET BOS DESCRIPTOR request +// Application return pointer to descriptor +TU_ATTR_WEAK uint8_t const * tud_descriptor_bos_cb(void); + +// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete. +// device_qualifier descriptor describes information about a high-speed capable device that would +// change if the device were operating at the other speed. If not highspeed capable stall this request. +TU_ATTR_WEAK uint8_t const* tud_descriptor_device_qualifier_cb(void); + +// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa +TU_ATTR_WEAK uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index); + +// Invoked when device is mounted (configured) +TU_ATTR_WEAK void tud_mount_cb(void); + +// Invoked when device is unmounted +TU_ATTR_WEAK void tud_umount_cb(void); + +// Invoked when usb bus is suspended +// Within 7ms, device must draw an average of current less than 2.5 mA from bus +TU_ATTR_WEAK void tud_suspend_cb(bool remote_wakeup_en); + +// Invoked when usb bus is resumed +TU_ATTR_WEAK void tud_resume_cb(void); + +// Invoked when there is a new usb event, which need to be processed by tud_task()/tud_task_ext() +void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr); + +// Invoked when received control request with VENDOR TYPE +TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); + +//--------------------------------------------------------------------+ +// Binary Device Object Store (BOS) Descriptor Templates +//--------------------------------------------------------------------+ + +#define TUD_BOS_DESC_LEN 5 + +// total length, number of device caps +#define TUD_BOS_DESCRIPTOR(_total_len, _caps_num) \ + 5, TUSB_DESC_BOS, U16_TO_U8S_LE(_total_len), _caps_num + +// Device Capability Platform 128-bit UUID + Data +#define TUD_BOS_PLATFORM_DESCRIPTOR(...) \ + 4+TU_ARGS_NUM(__VA_ARGS__), TUSB_DESC_DEVICE_CAPABILITY, DEVICE_CAPABILITY_PLATFORM, 0x00, __VA_ARGS__ + +//------------- WebUSB BOS Platform -------------// + +// Descriptor Length +#define TUD_BOS_WEBUSB_DESC_LEN 24 + +// Vendor Code, iLandingPage +#define TUD_BOS_WEBUSB_DESCRIPTOR(_vendor_code, _ipage) \ + TUD_BOS_PLATFORM_DESCRIPTOR(TUD_BOS_WEBUSB_UUID, U16_TO_U8S_LE(0x0100), _vendor_code, _ipage) + +#define TUD_BOS_WEBUSB_UUID \ + 0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47, \ + 0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65 + +//------------- Microsoft OS 2.0 Platform -------------// +#define TUD_BOS_MICROSOFT_OS_DESC_LEN 28 + +// Total Length of descriptor set, vendor code +#define TUD_BOS_MS_OS_20_DESCRIPTOR(_desc_set_len, _vendor_code) \ + TUD_BOS_PLATFORM_DESCRIPTOR(TUD_BOS_MS_OS_20_UUID, U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(_desc_set_len), _vendor_code, 0) + +#define TUD_BOS_MS_OS_20_UUID \ + 0xDF, 0x60, 0xDD, 0xD8, 0x89, 0x45, 0xC7, 0x4C, \ + 0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F + +//--------------------------------------------------------------------+ +// Configuration Descriptor Templates +//--------------------------------------------------------------------+ + +#define TUD_CONFIG_DESC_LEN (9) + +// Config number, interface count, string index, total length, attribute, power in mA +#define TUD_CONFIG_DESCRIPTOR(config_num, _itfcount, _stridx, _total_len, _attribute, _power_ma) \ + 9, TUSB_DESC_CONFIGURATION, U16_TO_U8S_LE(_total_len), _itfcount, config_num, _stridx, TU_BIT(7) | _attribute, (_power_ma)/2 + +//--------------------------------------------------------------------+ +// CDC Descriptor Templates +//--------------------------------------------------------------------+ + +// Length of template descriptor: 66 bytes +#define TUD_CDC_DESC_LEN (8+9+5+5+4+5+7+9+7+7) + +// CDC Descriptor Template +// Interface number, string index, EP notification address and size, EP data address (out, in) and size. +#define TUD_CDC_DESCRIPTOR(_itfnum, _stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize) \ + /* Interface Associate */\ + 8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL, CDC_COMM_PROTOCOL_NONE, 0,\ + /* CDC Control Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL, CDC_COMM_PROTOCOL_NONE, _stridx,\ + /* CDC Header */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0120),\ + /* CDC Call */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_CALL_MANAGEMENT, 0, (uint8_t)((_itfnum) + 1),\ + /* CDC ACM: support line request + send break */\ + 4, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, 6,\ + /* CDC Union */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\ + /* Endpoint Notification */\ + 7, TUSB_DESC_ENDPOINT, _ep_notif, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_notif_size), 16,\ + /* CDC Data Interface */\ + 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 2, TUSB_CLASS_CDC_DATA, 0, 0, 0,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 + +//--------------------------------------------------------------------+ +// MSC Descriptor Templates +//--------------------------------------------------------------------+ + +// Length of template descriptor: 23 bytes +#define TUD_MSC_DESC_LEN (9 + 7 + 7) + +// Interface number, string index, EP Out & EP In address, EP size +#define TUD_MSC_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \ + /* Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_MSC, MSC_SUBCLASS_SCSI, MSC_PROTOCOL_BOT, _stridx,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 + + +//--------------------------------------------------------------------+ +// HID Descriptor Templates +//--------------------------------------------------------------------+ + +// Length of template descriptor: 25 bytes +#define TUD_HID_DESC_LEN (9 + 9 + 7) + +// HID Input only descriptor +// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval +#define TUD_HID_DESCRIPTOR(_itfnum, _stridx, _boot_protocol, _report_desc_len, _epin, _epsize, _ep_interval) \ + /* Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUSB_CLASS_HID, (uint8_t)((_boot_protocol) ? (uint8_t)HID_SUBCLASS_BOOT : 0), _boot_protocol, _stridx,\ + /* HID descriptor */\ + 9, HID_DESC_TYPE_HID, U16_TO_U8S_LE(0x0111), 0, 1, HID_DESC_TYPE_REPORT, U16_TO_U8S_LE(_report_desc_len),\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _ep_interval + +// Length of template descriptor: 32 bytes +#define TUD_HID_INOUT_DESC_LEN (9 + 9 + 7 + 7) + +// HID Input & Output descriptor +// Interface number, string index, protocol, report descriptor len, EP OUT & IN address, size & polling interval +#define TUD_HID_INOUT_DESCRIPTOR(_itfnum, _stridx, _boot_protocol, _report_desc_len, _epout, _epin, _epsize, _ep_interval) \ + /* Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_HID, (uint8_t)((_boot_protocol) ? (uint8_t)HID_SUBCLASS_BOOT : 0), _boot_protocol, _stridx,\ + /* HID descriptor */\ + 9, HID_DESC_TYPE_HID, U16_TO_U8S_LE(0x0111), 0, 1, HID_DESC_TYPE_REPORT, U16_TO_U8S_LE(_report_desc_len),\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _ep_interval, \ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _ep_interval + +//--------------------------------------------------------------------+ +// MIDI Descriptor Templates +// Note: MIDI v1.0 is based on Audio v1.0 +//--------------------------------------------------------------------+ + +#define TUD_MIDI_DESC_HEAD_LEN (9 + 9 + 9 + 7) +#define TUD_MIDI_DESC_HEAD(_itfnum, _stridx, _numcables) \ + /* Audio Control (AC) Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_CONTROL, AUDIO_FUNC_PROTOCOL_CODE_UNDEF, _stridx,\ + /* AC Header */\ + 9, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_HEADER, U16_TO_U8S_LE(0x0100), U16_TO_U8S_LE(0x0009), 1, (uint8_t)((_itfnum) + 1),\ + /* MIDI Streaming (MS) Interface */\ + 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum) + 1), 0, 2, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_MIDI_STREAMING, AUDIO_FUNC_PROTOCOL_CODE_UNDEF, 0,\ + /* MS Header */\ + 7, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_HEADER, U16_TO_U8S_LE(0x0100), U16_TO_U8S_LE(7 + (_numcables) * TUD_MIDI_DESC_JACK_LEN + 2 * TUD_MIDI_DESC_EP_LEN(_numcables)) + +#define TUD_MIDI_JACKID_IN_EMB(_cablenum) \ + (uint8_t)(((_cablenum) - 1) * 4 + 1) + +#define TUD_MIDI_JACKID_IN_EXT(_cablenum) \ + (uint8_t)(((_cablenum) - 1) * 4 + 2) + +#define TUD_MIDI_JACKID_OUT_EMB(_cablenum) \ + (uint8_t)(((_cablenum) - 1) * 4 + 3) + +#define TUD_MIDI_JACKID_OUT_EXT(_cablenum) \ + (uint8_t)(((_cablenum) - 1) * 4 + 4) + +#define TUD_MIDI_DESC_JACK_LEN (6 + 6 + 9 + 9) +#define TUD_MIDI_DESC_JACK_DESC(_cablenum, _stridx) \ + /* MS In Jack (Embedded) */\ + 6, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_IN_JACK, MIDI_JACK_EMBEDDED, TUD_MIDI_JACKID_IN_EMB(_cablenum), _stridx,\ + /* MS In Jack (External) */\ + 6, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_IN_JACK, MIDI_JACK_EXTERNAL, TUD_MIDI_JACKID_IN_EXT(_cablenum), _stridx,\ + /* MS Out Jack (Embedded), connected to In Jack External */\ + 9, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_OUT_JACK, MIDI_JACK_EMBEDDED, TUD_MIDI_JACKID_OUT_EMB(_cablenum), 1, TUD_MIDI_JACKID_IN_EXT(_cablenum), 1, _stridx,\ + /* MS Out Jack (External), connected to In Jack Embedded */\ + 9, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_OUT_JACK, MIDI_JACK_EXTERNAL, TUD_MIDI_JACKID_OUT_EXT(_cablenum), 1, TUD_MIDI_JACKID_IN_EMB(_cablenum), 1, _stridx + +#define TUD_MIDI_DESC_JACK(_cablenum) TUD_MIDI_DESC_JACK_DESC(_cablenum, 0) + +#define TUD_MIDI_DESC_EP_LEN(_numcables) (9 + 4 + (_numcables)) +#define TUD_MIDI_DESC_EP(_epout, _epsize, _numcables) \ + /* Endpoint: Note Audio v1.0's endpoint has 9 bytes instead of 7 */\ + 9, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0, 0, 0, \ + /* MS Endpoint (connected to embedded jack) */\ + (uint8_t)(4 + (_numcables)), TUSB_DESC_CS_ENDPOINT, MIDI_CS_ENDPOINT_GENERAL, _numcables + +// Length of template descriptor (88 bytes) +#define TUD_MIDI_DESC_LEN (TUD_MIDI_DESC_HEAD_LEN + TUD_MIDI_DESC_JACK_LEN + TUD_MIDI_DESC_EP_LEN(1) * 2) + +// MIDI simple descriptor +// - 1 Embedded Jack In connected to 1 External Jack Out +// - 1 Embedded Jack out connected to 1 External Jack In +#define TUD_MIDI_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \ + TUD_MIDI_DESC_HEAD(_itfnum, _stridx, 1),\ + TUD_MIDI_DESC_JACK_DESC(1, 0),\ + TUD_MIDI_DESC_EP(_epout, _epsize, 1),\ + TUD_MIDI_JACKID_IN_EMB(1),\ + TUD_MIDI_DESC_EP(_epin, _epsize, 1),\ + TUD_MIDI_JACKID_OUT_EMB(1) + +//--------------------------------------------------------------------+ +// Audio v2.0 Descriptor Templates +//--------------------------------------------------------------------+ + +/* Standard Interface Association Descriptor (IAD) */ +#define TUD_AUDIO_DESC_IAD_LEN 8 +#define TUD_AUDIO_DESC_IAD(_firstitf, _nitfs, _stridx) \ + TUD_AUDIO_DESC_IAD_LEN, TUSB_DESC_INTERFACE_ASSOCIATION, _firstitf, _nitfs, TUSB_CLASS_AUDIO, AUDIO_FUNCTION_SUBCLASS_UNDEFINED, AUDIO_FUNC_PROTOCOL_CODE_V2, _stridx + +/* Standard AC Interface Descriptor(4.7.1) */ +#define TUD_AUDIO_DESC_STD_AC_LEN 9 +#define TUD_AUDIO_DESC_STD_AC(_itfnum, _nEPs, _stridx) /* _nEPs is 0 or 1 */\ + TUD_AUDIO_DESC_STD_AC_LEN, TUSB_DESC_INTERFACE, _itfnum, /* fixed to zero */ 0x00, _nEPs, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_CONTROL, AUDIO_INT_PROTOCOL_CODE_V2, _stridx + +/* Class-Specific AC Interface Header Descriptor(4.7.2) */ +#define TUD_AUDIO_DESC_CS_AC_LEN 9 +#define TUD_AUDIO_DESC_CS_AC(_bcdADC, _category, _totallen, _ctrl) /* _bcdADC : Audio Device Class Specification Release Number in Binary-Coded Decimal, _category : see audio_function_t, _totallen : Total number of bytes returned for the class-specific AudioControl interface i.e. Clock Source, Unit and Terminal descriptors - Do not include TUD_AUDIO_DESC_CS_AC_LEN, we already do this here*/ \ + TUD_AUDIO_DESC_CS_AC_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_HEADER, U16_TO_U8S_LE(_bcdADC), _category, U16_TO_U8S_LE(_totallen + TUD_AUDIO_DESC_CS_AC_LEN), _ctrl + +/* Clock Source Descriptor(4.7.2.1) */ +#define TUD_AUDIO_DESC_CLK_SRC_LEN 8 +#define TUD_AUDIO_DESC_CLK_SRC(_clkid, _attr, _ctrl, _assocTerm, _stridx) \ + TUD_AUDIO_DESC_CLK_SRC_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_CLOCK_SOURCE, _clkid, _attr, _ctrl, _assocTerm, _stridx + +/* Input Terminal Descriptor(4.7.2.4) */ +#define TUD_AUDIO_DESC_INPUT_TERM_LEN 17 +#define TUD_AUDIO_DESC_INPUT_TERM(_termid, _termtype, _assocTerm, _clkid, _nchannelslogical, _channelcfg, _idxchannelnames, _ctrl, _stridx) \ + TUD_AUDIO_DESC_INPUT_TERM_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL, _termid, U16_TO_U8S_LE(_termtype), _assocTerm, _clkid, _nchannelslogical, U32_TO_U8S_LE(_channelcfg), _idxchannelnames, U16_TO_U8S_LE(_ctrl), _stridx + +/* Output Terminal Descriptor(4.7.2.5) */ +#define TUD_AUDIO_DESC_OUTPUT_TERM_LEN 12 +#define TUD_AUDIO_DESC_OUTPUT_TERM(_termid, _termtype, _assocTerm, _srcid, _clkid, _ctrl, _stridx) \ + TUD_AUDIO_DESC_OUTPUT_TERM_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL, _termid, U16_TO_U8S_LE(_termtype), _assocTerm, _srcid, _clkid, U16_TO_U8S_LE(_ctrl), _stridx + +/* Feature Unit Descriptor(4.7.2.8) */ +// 1 - Channel +#define TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN 6+(1+1)*4 +#define TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(_unitid, _srcid, _ctrlch0master, _ctrlch1, _stridx) \ + TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_FEATURE_UNIT, _unitid, _srcid, U32_TO_U8S_LE(_ctrlch0master), U32_TO_U8S_LE(_ctrlch1), _stridx + +// 2 - Channels +#define TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN (6+(2+1)*4) +#define TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(_unitid, _srcid, _ctrlch0master, _ctrlch1, _ctrlch2, _stridx) \ + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_FEATURE_UNIT, _unitid, _srcid, U32_TO_U8S_LE(_ctrlch0master), U32_TO_U8S_LE(_ctrlch1), U32_TO_U8S_LE(_ctrlch2), _stridx +// 4 - Channels +#define TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL_LEN (6+(4+1)*4) +#define TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL(_unitid, _srcid, _ctrlch0master, _ctrlch1, _ctrlch2, _ctrlch3, _ctrlch4, _stridx) \ + TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_FEATURE_UNIT, _unitid, _srcid, U32_TO_U8S_LE(_ctrlch0master), U32_TO_U8S_LE(_ctrlch1), U32_TO_U8S_LE(_ctrlch2), U32_TO_U8S_LE(_ctrlch3), U32_TO_U8S_LE(_ctrlch4), _stridx + +// For more channels, add definitions here + +/* Standard AC Interrupt Endpoint Descriptor(4.8.2.1) */ +#define TUD_AUDIO_DESC_STD_AC_INT_EP_LEN 7 +#define TUD_AUDIO_DESC_STD_AC_INT_EP(_ep, _interval) \ + TUD_AUDIO_DESC_STD_AC_INT_EP_LEN, TUSB_DESC_ENDPOINT, _ep, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(6), _interval + +/* Standard AS Interface Descriptor(4.9.1) */ +#define TUD_AUDIO_DESC_STD_AS_INT_LEN 9 +#define TUD_AUDIO_DESC_STD_AS_INT(_itfnum, _altset, _nEPs, _stridx) \ + TUD_AUDIO_DESC_STD_AS_INT_LEN, TUSB_DESC_INTERFACE, _itfnum, _altset, _nEPs, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_STREAMING, AUDIO_INT_PROTOCOL_CODE_V2, _stridx + +/* Class-Specific AS Interface Descriptor(4.9.2) */ +#define TUD_AUDIO_DESC_CS_AS_INT_LEN 16 +#define TUD_AUDIO_DESC_CS_AS_INT(_termid, _ctrl, _formattype, _formats, _nchannelsphysical, _channelcfg, _stridx) \ + TUD_AUDIO_DESC_CS_AS_INT_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AS_INTERFACE_AS_GENERAL, _termid, _ctrl, _formattype, U32_TO_U8S_LE(_formats), _nchannelsphysical, U32_TO_U8S_LE(_channelcfg), _stridx + +/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ +#define TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN 6 +#define TUD_AUDIO_DESC_TYPE_I_FORMAT(_subslotsize, _bitresolution) /* _subslotsize is number of bytes per sample (i.e. subslot) and can be 1,2,3, or 4 */\ + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AS_INTERFACE_FORMAT_TYPE, AUDIO_FORMAT_TYPE_I, _subslotsize, _bitresolution + +/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ +#define TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN 7 +#define TUD_AUDIO_DESC_STD_AS_ISO_EP(_ep, _attr, _maxEPsize, _interval) \ + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN, TUSB_DESC_ENDPOINT, _ep, _attr, U16_TO_U8S_LE(_maxEPsize), _interval + +/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ +#define TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN 8 +#define TUD_AUDIO_DESC_CS_AS_ISO_EP(_attr, _ctrl, _lockdelayunit, _lockdelay) \ + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN, TUSB_DESC_CS_ENDPOINT, AUDIO_CS_EP_SUBTYPE_GENERAL, _attr, _ctrl, _lockdelayunit, U16_TO_U8S_LE(_lockdelay) + +/* Standard AS Isochronous Feedback Endpoint Descriptor(4.10.2.1) */ +#define TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN 7 +#define TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(_ep, _interval) \ + TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN, TUSB_DESC_ENDPOINT, _ep, (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_NO_SYNC | (uint8_t)TUSB_ISO_EP_ATT_EXPLICIT_FB), U16_TO_U8S_LE(4), _interval + +// AUDIO simple descriptor (UAC2) for 1 microphone input +// - 1 Input Terminal, 1 Feature Unit (Mute and Volume Control), 1 Output Terminal, 1 Clock Source + +#define TUD_AUDIO_MIC_ONE_CH_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN\ + + TUD_AUDIO_DESC_STD_AC_LEN\ + + TUD_AUDIO_DESC_CS_AC_LEN\ + + TUD_AUDIO_DESC_CLK_SRC_LEN\ + + TUD_AUDIO_DESC_INPUT_TERM_LEN\ + + TUD_AUDIO_DESC_OUTPUT_TERM_LEN\ + + TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN\ + + TUD_AUDIO_DESC_STD_AS_INT_LEN\ + + TUD_AUDIO_DESC_STD_AS_INT_LEN\ + + TUD_AUDIO_DESC_CS_AS_INT_LEN\ + + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\ + + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\ + + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN) + +#define TUD_AUDIO_MIC_ONE_CH_DESC_N_AS_INT 1 // Number of AS interfaces + +#define TUD_AUDIO_MIC_ONE_CH_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epsize) \ + /* Standard Interface Association Descriptor (IAD) */\ + TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\ + /* Standard AC Interface Descriptor(4.7.1) */\ + TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\ + /* Class-Specific AC Interface Header Descriptor(4.7.2) */\ + TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_MICROPHONE, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN+TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS),\ + /* Clock Source Descriptor(4.7.2.1) */\ + TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x01, /*_stridx*/ 0x00),\ + /* Input Terminal Descriptor(4.7.2.4) */\ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x03, /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00),\ + /* Output Terminal Descriptor(4.7.2.5) */\ + TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ 0x03, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x01, /*_srcid*/ 0x02, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\ + /* Feature Unit Descriptor(4.7.2.8) */\ + TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00),\ + /* Standard AS Interface Descriptor(4.9.1) */\ + /* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */\ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\ + /* Standard AS Interface Descriptor(4.9.1) */\ + /* Interface 1, Alternate 1 - alternate interface for data streaming */\ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00),\ + /* Class-Specific AS Interface Descriptor(4.9.2) */\ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ 0x03, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ + /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\ + TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\ + /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\ + TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\ + /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\ + TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000) + +// AUDIO simple descriptor (UAC2) for 4 microphone input +// - 1 Input Terminal, 1 Feature Unit (Mute and Volume Control), 1 Output Terminal, 1 Clock Source + +#define TUD_AUDIO_MIC_FOUR_CH_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN\ + + TUD_AUDIO_DESC_STD_AC_LEN\ + + TUD_AUDIO_DESC_CS_AC_LEN\ + + TUD_AUDIO_DESC_CLK_SRC_LEN\ + + TUD_AUDIO_DESC_INPUT_TERM_LEN\ + + TUD_AUDIO_DESC_OUTPUT_TERM_LEN\ + + TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL_LEN\ + + TUD_AUDIO_DESC_STD_AS_INT_LEN\ + + TUD_AUDIO_DESC_STD_AS_INT_LEN\ + + TUD_AUDIO_DESC_CS_AS_INT_LEN\ + + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\ + + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\ + + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN) + +#define TUD_AUDIO_MIC_FOUR_CH_DESC_N_AS_INT 1 // Number of AS interfaces + +#define TUD_AUDIO_MIC_FOUR_CH_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epsize) \ + /* Standard Interface Association Descriptor (IAD) */\ + TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\ + /* Standard AC Interface Descriptor(4.7.1) */\ + TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\ + /* Class-Specific AC Interface Header Descriptor(4.7.2) */\ + TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_MICROPHONE, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN+TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS),\ + /* Clock Source Descriptor(4.7.2.1) */\ + TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x01, /*_stridx*/ 0x00),\ + /* Input Terminal Descriptor(4.7.2.4) */\ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x03, /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x04, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00),\ + /* Output Terminal Descriptor(4.7.2.5) */\ + TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ 0x03, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x01, /*_srcid*/ 0x02, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\ + /* Feature Unit Descriptor(4.7.2.8) */\ + TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch3*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch4*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00),\ + /* Standard AS Interface Descriptor(4.9.1) */\ + /* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */\ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\ + /* Standard AS Interface Descriptor(4.9.1) */\ + /* Interface 1, Alternate 1 - alternate interface for data streaming */\ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00),\ + /* Class-Specific AS Interface Descriptor(4.9.2) */\ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ 0x03, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x04, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ + /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\ + TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\ + /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\ + TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\ + /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\ + TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000) + +// AUDIO simple descriptor (UAC2) for mono speaker +// - 1 Input Terminal, 2 Feature Unit (Mute and Volume Control), 3 Output Terminal, 4 Clock Source + +#define TUD_AUDIO_SPEAKER_MONO_FB_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN\ + + TUD_AUDIO_DESC_STD_AC_LEN\ + + TUD_AUDIO_DESC_CS_AC_LEN\ + + TUD_AUDIO_DESC_CLK_SRC_LEN\ + + TUD_AUDIO_DESC_INPUT_TERM_LEN\ + + TUD_AUDIO_DESC_OUTPUT_TERM_LEN\ + + TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN\ + + TUD_AUDIO_DESC_STD_AS_INT_LEN\ + + TUD_AUDIO_DESC_STD_AS_INT_LEN\ + + TUD_AUDIO_DESC_CS_AS_INT_LEN\ + + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\ + + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\ + + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN\ + + TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN) + +#define TUD_AUDIO_SPEAKER_MONO_FB_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epsize, _epfb) \ + /* Standard Interface Association Descriptor (IAD) */\ + TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\ + /* Standard AC Interface Descriptor(4.7.1) */\ + TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\ + /* Class-Specific AC Interface Header Descriptor(4.7.2) */\ + TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_DESKTOP_SPEAKER, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN+TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS),\ + /* Clock Source Descriptor(4.7.2.1) */\ + TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x01, /*_stridx*/ 0x00),\ + /* Input Terminal Descriptor(4.7.2.4) */\ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00),\ + /* Output Terminal Descriptor(4.7.2.5) */\ + TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ 0x03, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, /*_assocTerm*/ 0x01, /*_srcid*/ 0x02, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\ + /* Feature Unit Descriptor(4.7.2.8) */\ + TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_ctrlch0master*/ 0 * (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_ctrlch1*/ 0 * (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_stridx*/ 0x00),\ + /* Standard AS Interface Descriptor(4.9.1) */\ + /* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */\ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\ + /* Standard AS Interface Descriptor(4.9.1) */\ + /* Interface 1, Alternate 1 - alternate interface for data streaming */\ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x02, /*_stridx*/ 0x00),\ + /* Class-Specific AS Interface Descriptor(4.9.2) */\ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ 0x01, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ + /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\ + TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\ + /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\ + TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\ + /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\ + TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000),\ + /* Standard AS Isochronous Feedback Endpoint Descriptor(4.10.2.1) */\ + TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(/*_ep*/ _epfb, /*_interval*/ 1)\ + +// Calculate wMaxPacketSize of Endpoints +#define TUD_AUDIO_EP_SIZE(_maxFrequency, _nBytesPerSample, _nChannels) \ + ((((_maxFrequency + (TUD_OPT_HIGH_SPEED ? 7999 : 999)) / (TUD_OPT_HIGH_SPEED ? 8000 : 1000)) + 1) * _nBytesPerSample * _nChannels) + + +//--------------------------------------------------------------------+ +// USBTMC/USB488 Descriptor Templates +//--------------------------------------------------------------------+ + +#define TUD_USBTMC_APP_CLASS (TUSB_CLASS_APPLICATION_SPECIFIC) +#define TUD_USBTMC_APP_SUBCLASS 0x03u + +#define TUD_USBTMC_PROTOCOL_STD 0x00u +#define TUD_USBTMC_PROTOCOL_USB488 0x01u + +// Interface number, number of endpoints, EP string index, USB_TMC_PROTOCOL*, bulk-out endpoint ID, +// bulk-in endpoint ID +#define TUD_USBTMC_IF_DESCRIPTOR(_itfnum, _bNumEndpoints, _stridx, _itfProtocol) \ + /* Interface */ \ + 0x09, TUSB_DESC_INTERFACE, _itfnum, 0x00, _bNumEndpoints, TUD_USBTMC_APP_CLASS, TUD_USBTMC_APP_SUBCLASS, _itfProtocol, _stridx + +#define TUD_USBTMC_IF_DESCRIPTOR_LEN 9u + +#define TUD_USBTMC_BULK_DESCRIPTORS(_epout, _epin, _bulk_epsize) \ + /* Endpoint Out */ \ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_bulk_epsize), 0u, \ + /* Endpoint In */ \ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_bulk_epsize), 0u + +#define TUD_USBTMC_BULK_DESCRIPTORS_LEN (7u+7u) + +/* optional interrupt endpoint */ \ +// _int_pollingInterval : for LS/FS, expressed in frames (1ms each). 16 may be a good number? +#define TUD_USBTMC_INT_DESCRIPTOR(_ep_interrupt, _ep_interrupt_size, _int_pollingInterval ) \ + 7, TUSB_DESC_ENDPOINT, _ep_interrupt, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_interrupt_size), _int_pollingInterval + +#define TUD_USBTMC_INT_DESCRIPTOR_LEN (7u) + +//--------------------------------------------------------------------+ +// Vendor Descriptor Templates +//--------------------------------------------------------------------+ + +#define TUD_VENDOR_DESC_LEN (9+7+7) + +// Interface number, string index, EP Out & IN address, EP size +#define TUD_VENDOR_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \ + /* Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_VENDOR_SPECIFIC, 0x00, 0x00, _stridx,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 + +//--------------------------------------------------------------------+ +// DFU Runtime Descriptor Templates +//--------------------------------------------------------------------+ + +#define TUD_DFU_APP_CLASS (TUSB_CLASS_APPLICATION_SPECIFIC) +#define TUD_DFU_APP_SUBCLASS (APP_SUBCLASS_DFU_RUNTIME) + +// Length of template descriptr: 18 bytes +#define TUD_DFU_RT_DESC_LEN (9 + 9) + +// DFU runtime descriptor +// Interface number, string index, attributes, detach timeout, transfer size +#define TUD_DFU_RT_DESCRIPTOR(_itfnum, _stridx, _attr, _timeout, _xfer_size) \ + /* Interface */ \ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUD_DFU_APP_CLASS, TUD_DFU_APP_SUBCLASS, DFU_PROTOCOL_RT, _stridx, \ + /* Function */ \ + 9, DFU_DESC_FUNCTIONAL, _attr, U16_TO_U8S_LE(_timeout), U16_TO_U8S_LE(_xfer_size), U16_TO_U8S_LE(0x0101) + +//--------------------------------------------------------------------+ +// DFU Descriptor Templates +//--------------------------------------------------------------------+ + +// Length of template descriptor: 9 bytes + number of alternatives * 9 +#define TUD_DFU_DESC_LEN(_alt_count) (9 + (_alt_count) * 9) + +// Interface number, Alternate count, starting string index, attributes, detach timeout, transfer size +// Note: Alternate count must be numeric or macro, string index is increased by one for each Alt interface +#define TUD_DFU_DESCRIPTOR(_itfnum, _alt_count, _stridx, _attr, _timeout, _xfer_size) \ + TU_XSTRCAT(_TUD_DFU_ALT_,_alt_count)(_itfnum, 0, _stridx), \ + /* Function */ \ + 9, DFU_DESC_FUNCTIONAL, _attr, U16_TO_U8S_LE(_timeout), U16_TO_U8S_LE(_xfer_size), U16_TO_U8S_LE(0x0101) + +#define _TUD_DFU_ALT(_itfnum, _alt, _stridx) \ + /* Interface */ \ + 9, TUSB_DESC_INTERFACE, _itfnum, _alt, 0, TUD_DFU_APP_CLASS, TUD_DFU_APP_SUBCLASS, DFU_PROTOCOL_DFU, _stridx + +#define _TUD_DFU_ALT_1(_itfnum, _alt_count, _stridx) \ + _TUD_DFU_ALT(_itfnum, _alt_count, _stridx) + +#define _TUD_DFU_ALT_2(_itfnum, _alt_count, _stridx) \ + _TUD_DFU_ALT(_itfnum, _alt_count, _stridx), \ + _TUD_DFU_ALT_1(_itfnum, _alt_count+1, _stridx+1) + +#define _TUD_DFU_ALT_3(_itfnum, _alt_count, _stridx) \ + _TUD_DFU_ALT(_itfnum, _alt_count, _stridx), \ + _TUD_DFU_ALT_2(_itfnum, _alt_count+1, _stridx+1) + +#define _TUD_DFU_ALT_4(_itfnum, _alt_count, _stridx) \ + _TUD_DFU_ALT(_itfnum, _alt_count, _stridx), \ + _TUD_DFU_ALT_3(_itfnum, _alt_count+1, _stridx+1) + +#define _TUD_DFU_ALT_5(_itfnum, _alt_count, _stridx) \ + _TUD_DFU_ALT(_itfnum, _alt_count, _stridx), \ + _TUD_DFU_ALT_4(_itfnum, _alt_count+1, _stridx+1) + +#define _TUD_DFU_ALT_6(_itfnum, _alt_count, _stridx) \ + _TUD_DFU_ALT(_itfnum, _alt_count, _stridx), \ + _TUD_DFU_ALT_5(_itfnum, _alt_count+1, _stridx+1) + +#define _TUD_DFU_ALT_7(_itfnum, _alt_count, _stridx) \ + _TUD_DFU_ALT(_itfnum, _alt_count, _stridx), \ + _TUD_DFU_ALT_6(_itfnum, _alt_count+1, _stridx+1) + +#define _TUD_DFU_ALT_8(_itfnum, _alt_count, _stridx) \ + _TUD_DFU_ALT(_itfnum, _alt_count, _stridx), \ + _TUD_DFU_ALT_7(_itfnum, _alt_count+1, _stridx+1) + +//--------------------------------------------------------------------+ +// CDC-ECM Descriptor Templates +//--------------------------------------------------------------------+ + +// Length of template descriptor: 71 bytes +#define TUD_CDC_ECM_DESC_LEN (8+9+5+5+13+7+9+9+7+7) + +// CDC-ECM Descriptor Template +// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size. +#define TUD_CDC_ECM_DESCRIPTOR(_itfnum, _desc_stridx, _mac_stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize, _maxsegmentsize) \ + /* Interface Association */\ + 8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_ETHERNET_CONTROL_MODEL, 0, 0,\ + /* CDC Control Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_ETHERNET_CONTROL_MODEL, 0, _desc_stridx,\ + /* CDC-ECM Header */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0120),\ + /* CDC-ECM Union */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\ + /* CDC-ECM Functional Descriptor */\ + 13, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ETHERNET_NETWORKING, _mac_stridx, 0, 0, 0, 0, U16_TO_U8S_LE(_maxsegmentsize), U16_TO_U8S_LE(0), 0,\ + /* Endpoint Notification */\ + 7, TUSB_DESC_ENDPOINT, _ep_notif, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_notif_size), 1,\ + /* CDC Data Interface (default inactive) */\ + 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 0, TUSB_CLASS_CDC_DATA, 0, 0, 0,\ + /* CDC Data Interface (alternative active) */\ + 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 1, 2, TUSB_CLASS_CDC_DATA, 0, 0, 0,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 + +//--------------------------------------------------------------------+ +// RNDIS Descriptor Templates +//--------------------------------------------------------------------+ + +#if 0 +/* Windows XP */ +#define TUD_RNDIS_ITF_CLASS TUSB_CLASS_CDC +#define TUD_RNDIS_ITF_SUBCLASS CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL +#define TUD_RNDIS_ITF_PROTOCOL 0xFF /* CDC_COMM_PROTOCOL_MICROSOFT_RNDIS */ +#else +/* Windows 7+ */ +#define TUD_RNDIS_ITF_CLASS TUSB_CLASS_WIRELESS_CONTROLLER +#define TUD_RNDIS_ITF_SUBCLASS 0x01 +#define TUD_RNDIS_ITF_PROTOCOL 0x03 +#endif + +// Length of template descriptor: 66 bytes +#define TUD_RNDIS_DESC_LEN (8+9+5+5+4+5+7+9+7+7) + +// RNDIS Descriptor Template +// Interface number, string index, EP notification address and size, EP data address (out, in) and size. +#define TUD_RNDIS_DESCRIPTOR(_itfnum, _stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize) \ + /* Interface Association */\ + 8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUD_RNDIS_ITF_CLASS, TUD_RNDIS_ITF_SUBCLASS, TUD_RNDIS_ITF_PROTOCOL, 0,\ + /* CDC Control Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUD_RNDIS_ITF_CLASS, TUD_RNDIS_ITF_SUBCLASS, TUD_RNDIS_ITF_PROTOCOL, _stridx,\ + /* CDC-ACM Header */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0110),\ + /* CDC Call Management */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_CALL_MANAGEMENT, 0, (uint8_t)((_itfnum) + 1),\ + /* ACM */\ + 4, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, 0,\ + /* CDC Union */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\ + /* Endpoint Notification */\ + 7, TUSB_DESC_ENDPOINT, _ep_notif, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_notif_size), 1,\ + /* CDC Data Interface */\ + 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 2, TUSB_CLASS_CDC_DATA, 0, 0, 0,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 + +//--------------------------------------------------------------------+ +// Bluetooth Radio Descriptor Templates +//--------------------------------------------------------------------+ + +#define TUD_BT_APP_CLASS (TUSB_CLASS_WIRELESS_CONTROLLER) +#define TUD_BT_APP_SUBCLASS 0x01 +#define TUD_BT_PROTOCOL_PRIMARY_CONTROLLER 0x01 +#define TUD_BT_PROTOCOL_AMP_CONTROLLER 0x02 + +// Length of template descriptor: 38 bytes + number of ISO alternatives * 23 +#define TUD_BTH_DESC_LEN (8 + 9 + 7 + 7 + 7 + (CFG_TUD_BTH_ISO_ALT_COUNT) * (9 + 7 + 7)) + +/* Primary Interface */ +#define TUD_BTH_PRI_ITF(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_interval, _ep_in, _ep_out, _ep_size) \ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 3, TUD_BT_APP_CLASS, TUD_BT_APP_SUBCLASS, TUD_BT_PROTOCOL_PRIMARY_CONTROLLER, _stridx, \ + /* Endpoint In for events */ \ + 7, TUSB_DESC_ENDPOINT, _ep_evt, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_evt_size), _ep_evt_interval, \ + /* Endpoint In for ACL data */ \ + 7, TUSB_DESC_ENDPOINT, _ep_in, TUSB_XFER_BULK, U16_TO_U8S_LE(_ep_size), 1, \ + /* Endpoint Out for ACL data */ \ + 7, TUSB_DESC_ENDPOINT, _ep_out, TUSB_XFER_BULK, U16_TO_U8S_LE(_ep_size), 1 + +#define TUD_BTH_ISO_ITF(_itfnum, _alt, _ep_in, _ep_out, _n) ,\ + /* Interface with 2 endpoints */ \ + 9, TUSB_DESC_INTERFACE, _itfnum, _alt, 2, TUD_BT_APP_CLASS, TUD_BT_APP_SUBCLASS, TUD_BT_PROTOCOL_PRIMARY_CONTROLLER, 0, \ + /* Isochronous endpoints */ \ + 7, TUSB_DESC_ENDPOINT, _ep_in, TUSB_XFER_ISOCHRONOUS, U16_TO_U8S_LE(_n), 1, \ + 7, TUSB_DESC_ENDPOINT, _ep_out, TUSB_XFER_ISOCHRONOUS, U16_TO_U8S_LE(_n), 1 + +#define _FIRST(a, ...) a +#define _REST(a, ...) __VA_ARGS__ + +#define TUD_BTH_ISO_ITF_0(_itfnum, ...) +#define TUD_BTH_ISO_ITF_1(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 1, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) +#define TUD_BTH_ISO_ITF_2(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 2, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \ + TUD_BTH_ISO_ITF_1(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__)) +#define TUD_BTH_ISO_ITF_3(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 3, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \ + TUD_BTH_ISO_ITF_2(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__)) +#define TUD_BTH_ISO_ITF_4(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 4, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \ + TUD_BTH_ISO_ITF_3(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__)) +#define TUD_BTH_ISO_ITF_5(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 5, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \ + TUD_BTH_ISO_ITF_4(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__)) +#define TUD_BTH_ISO_ITF_6(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 6, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \ + TUD_BTH_ISO_ITF_5(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__)) + +#define TUD_BTH_ISO_ITFS(_itfnum, _ep_in, _ep_out, ...) \ + TU_XSTRCAT(TUD_BTH_ISO_ITF_, CFG_TUD_BTH_ISO_ALT_COUNT)(_itfnum, _ep_in, _ep_out, __VA_ARGS__) + +// BT Primary controller descriptor +// Interface number, string index, attributes, event endpoint, event endpoint size, interval, data in, data out, data endpoint size, iso endpoint sizes +// TODO BTH should also use IAD like CDC for composite device +#define TUD_BTH_DESCRIPTOR(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_interval, _ep_in, _ep_out, _ep_size,...) \ + /* Interface Associate */\ + 8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUD_BT_APP_CLASS, TUD_BT_APP_SUBCLASS, TUD_BT_PROTOCOL_PRIMARY_CONTROLLER, 0,\ + TUD_BTH_PRI_ITF(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_interval, _ep_in, _ep_out, _ep_size) \ + TUD_BTH_ISO_ITFS(_itfnum + 1, _ep_in + 1, _ep_out + 1, __VA_ARGS__) + +//--------------------------------------------------------------------+ +// CDC-NCM Descriptor Templates +//--------------------------------------------------------------------+ + +// Length of template descriptor +#define TUD_CDC_NCM_DESC_LEN (8+9+5+5+13+6+7+9+9+7+7) + +// CDC-ECM Descriptor Template +// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size. +#define TUD_CDC_NCM_DESCRIPTOR(_itfnum, _desc_stridx, _mac_stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize, _maxsegmentsize) \ + /* Interface Association */\ + 8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_NETWORK_CONTROL_MODEL, 0, 0,\ + /* CDC Control Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_NETWORK_CONTROL_MODEL, 0, _desc_stridx,\ + /* CDC-NCM Header */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0110),\ + /* CDC-NCM Union */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\ + /* CDC-NCM Functional Descriptor */\ + 13, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ETHERNET_NETWORKING, _mac_stridx, 0, 0, 0, 0, U16_TO_U8S_LE(_maxsegmentsize), U16_TO_U8S_LE(0), 0, \ + /* CDC-NCM Functional Descriptor */\ + 6, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_NCM, U16_TO_U8S_LE(0x0100), 0, \ + /* Endpoint Notification */\ + 7, TUSB_DESC_ENDPOINT, _ep_notif, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_notif_size), 50,\ + /* CDC Data Interface (default inactive) */\ + 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 0, TUSB_CLASS_CDC_DATA, 0, NCM_DATA_PROTOCOL_NETWORK_TRANSFER_BLOCK, 0,\ + /* CDC Data Interface (alternative active) */\ + 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 1, 2, TUSB_CLASS_CDC_DATA, 0, NCM_DATA_PROTOCOL_NETWORK_TRANSFER_BLOCK, 0,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 + +#ifdef __cplusplus +} +#endif + +#endif /* _TUSB_USBD_H_ */ + +/** @} */ diff --git a/test-devices/composite-stm32/lib/tinyusb/device/usbd_control.c b/test-devices/composite-stm32/lib/tinyusb/device/usbd_control.c new file mode 100644 index 00000000..35cce1f7 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/device/usbd_control.c @@ -0,0 +1,222 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if CFG_TUD_ENABLED + +#include "dcd.h" +#include "tusb.h" +#include "device/usbd_pvt.h" + +//--------------------------------------------------------------------+ +// Callback weak stubs (called if application does not provide) +//--------------------------------------------------------------------+ +TU_ATTR_WEAK void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* request) { + (void) rhport; + (void) request; +} + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL +extern void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback); +#endif + +enum { + EDPT_CTRL_OUT = 0x00, + EDPT_CTRL_IN = 0x80 +}; + +typedef struct { + tusb_control_request_t request; + uint8_t* buffer; + uint16_t data_len; + uint16_t total_xferred; + usbd_control_xfer_cb_t complete_cb; +} usbd_control_xfer_t; + +tu_static usbd_control_xfer_t _ctrl_xfer; + +CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN +tu_static uint8_t _usbd_ctrl_buf[CFG_TUD_ENDPOINT0_SIZE]; + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ + +// Queue ZLP status transaction +static inline bool _status_stage_xact(uint8_t rhport, tusb_control_request_t const* request) { + // Opposite to endpoint in Data Phase + uint8_t const ep_addr = request->bmRequestType_bit.direction ? EDPT_CTRL_OUT : EDPT_CTRL_IN; + return usbd_edpt_xfer(rhport, ep_addr, NULL, 0); +} + +// Status phase +bool tud_control_status(uint8_t rhport, tusb_control_request_t const* request) { + _ctrl_xfer.request = (*request); + _ctrl_xfer.buffer = NULL; + _ctrl_xfer.total_xferred = 0; + _ctrl_xfer.data_len = 0; + + return _status_stage_xact(rhport, request); +} + +// Queue a transaction in Data Stage +// Each transaction has up to Endpoint0's max packet size. +// This function can also transfer an zero-length packet +static bool _data_stage_xact(uint8_t rhport) { + uint16_t const xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred, + CFG_TUD_ENDPOINT0_SIZE); + + uint8_t ep_addr = EDPT_CTRL_OUT; + + if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN) { + ep_addr = EDPT_CTRL_IN; + if (xact_len) { + TU_VERIFY(0 == tu_memcpy_s(_usbd_ctrl_buf, CFG_TUD_ENDPOINT0_SIZE, _ctrl_xfer.buffer, xact_len)); + } + } + + return usbd_edpt_xfer(rhport, ep_addr, xact_len ? _usbd_ctrl_buf : NULL, xact_len); +} + +// Transmit data to/from the control endpoint. +// If the request's wLength is zero, a status packet is sent instead. +bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const* request, void* buffer, uint16_t len) { + _ctrl_xfer.request = (*request); + _ctrl_xfer.buffer = (uint8_t*) buffer; + _ctrl_xfer.total_xferred = 0U; + _ctrl_xfer.data_len = tu_min16(len, request->wLength); + + if (request->wLength > 0U) { + if (_ctrl_xfer.data_len > 0U) { + TU_ASSERT(buffer); + } + +// TU_LOG2(" Control total data length is %u bytes\r\n", _ctrl_xfer.data_len); + + // Data stage + TU_ASSERT(_data_stage_xact(rhport)); + } else { + // Status stage + TU_ASSERT(_status_stage_xact(rhport, request)); + } + + return true; +} + +//--------------------------------------------------------------------+ +// USBD API +//--------------------------------------------------------------------+ +void usbd_control_reset(void); +void usbd_control_set_request(tusb_control_request_t const* request); +void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp); +bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); + +void usbd_control_reset(void) { + tu_varclr(&_ctrl_xfer); +} + +// Set complete callback +void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp) { + _ctrl_xfer.complete_cb = fp; +} + +// for dcd_set_address where DCD is responsible for status response +void usbd_control_set_request(tusb_control_request_t const* request) { + _ctrl_xfer.request = (*request); + _ctrl_xfer.buffer = NULL; + _ctrl_xfer.total_xferred = 0; + _ctrl_xfer.data_len = 0; +} + +// callback when a transaction complete on +// - DATA stage of control endpoint or +// - Status stage +bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { + (void) result; + + // Endpoint Address is opposite to direction bit, this is Status Stage complete event + if (tu_edpt_dir(ep_addr) != _ctrl_xfer.request.bmRequestType_bit.direction) { + TU_ASSERT(0 == xferred_bytes); + + // invoke optional dcd hook if available + dcd_edpt0_status_complete(rhport, &_ctrl_xfer.request); + + if (_ctrl_xfer.complete_cb) { + // TODO refactor with usbd_driver_print_control_complete_name + _ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_ACK, &_ctrl_xfer.request); + } + + return true; + } + + if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_OUT) { + TU_VERIFY(_ctrl_xfer.buffer); + memcpy(_ctrl_xfer.buffer, _usbd_ctrl_buf, xferred_bytes); + TU_LOG_MEM(CFG_TUD_LOG_LEVEL, _usbd_ctrl_buf, xferred_bytes, 2); + } + + _ctrl_xfer.total_xferred += (uint16_t) xferred_bytes; + _ctrl_xfer.buffer += xferred_bytes; + + // Data Stage is complete when all request's length are transferred or + // a short packet is sent including zero-length packet. + if ((_ctrl_xfer.request.wLength == _ctrl_xfer.total_xferred) || + (xferred_bytes < CFG_TUD_ENDPOINT0_SIZE)) { + // DATA stage is complete + bool is_ok = true; + + // invoke complete callback if set + // callback can still stall control in status phase e.g out data does not make sense + if (_ctrl_xfer.complete_cb) { + #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL + usbd_driver_print_control_complete_name(_ctrl_xfer.complete_cb); + #endif + + is_ok = _ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_DATA, &_ctrl_xfer.request); + } + + if (is_ok) { + // Send status + TU_ASSERT(_status_stage_xact(rhport, &_ctrl_xfer.request)); + } else { + // Stall both IN and OUT control endpoint + dcd_edpt_stall(rhport, EDPT_CTRL_OUT); + dcd_edpt_stall(rhport, EDPT_CTRL_IN); + } + } else { + // More data to transfer + TU_ASSERT(_data_stage_xact(rhport)); + } + + return true; +} + +#endif diff --git a/test-devices/composite-stm32/lib/tinyusb/device/usbd_pvt.h b/test-devices/composite-stm32/lib/tinyusb/device/usbd_pvt.h new file mode 100644 index 00000000..47752f32 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/device/usbd_pvt.h @@ -0,0 +1,127 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ +#ifndef _TUSB_USBD_PVT_H_ +#define _TUSB_USBD_PVT_H_ + +#include "osal/osal.h" +#include "common/tusb_fifo.h" + +#ifdef __cplusplus + extern "C" { +#endif + +#define TU_LOG_USBD(...) TU_LOG(CFG_TUD_LOG_LEVEL, __VA_ARGS__) + +//--------------------------------------------------------------------+ +// Class Driver API +//--------------------------------------------------------------------+ + +typedef struct { + #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL + char const* name; + #endif + + void (* init ) (void); + bool (* deinit ) (void); + void (* reset ) (uint8_t rhport); + uint16_t (* open ) (uint8_t rhport, tusb_desc_interface_t const * desc_intf, uint16_t max_len); + bool (* control_xfer_cb ) (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); + bool (* xfer_cb ) (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); + void (* sof ) (uint8_t rhport, uint32_t frame_count); // optional +} usbd_class_driver_t; + +// Invoked when initializing device stack to get additional class drivers. +// Can be implemented by application to extend/overwrite class driver support. +// Note: The drivers array must be accessible at all time when stack is active +usbd_class_driver_t const* usbd_app_driver_get_cb(uint8_t* driver_count) TU_ATTR_WEAK; + +typedef bool (*usbd_control_xfer_cb_t)(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); + +void usbd_int_set(bool enabled); + +//--------------------------------------------------------------------+ +// USBD Endpoint API +// Note: rhport should be 0 since device stack only support 1 rhport for now +//--------------------------------------------------------------------+ + +// Open an endpoint +bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep); + +// Close an endpoint +void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr); + +// Submit a usb transfer +bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes); + +// Submit a usb ISO transfer by use of a FIFO (ring buffer) - all bytes in FIFO get transmitted +bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes); + +// Claim an endpoint before submitting a transfer. +// If caller does not make any transfer, it must release endpoint for others. +bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr); + +// Release claimed endpoint without submitting a transfer +bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr); + +// Check if endpoint is busy transferring +bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr); + +// Stall endpoint +void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr); + +// Clear stalled endpoint +void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr); + +// Check if endpoint is stalled +bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr); + +// Allocate packet buffer used by ISO endpoints +bool usbd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size); + +// Configure and enable an ISO endpoint according to descriptor +bool usbd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc); + +// Check if endpoint is ready (not busy and not stalled) +TU_ATTR_ALWAYS_INLINE static inline +bool usbd_edpt_ready(uint8_t rhport, uint8_t ep_addr) { + return !usbd_edpt_busy(rhport, ep_addr) && !usbd_edpt_stalled(rhport, ep_addr); +} + +// Enable SOF interrupt +void usbd_sof_enable(uint8_t rhport, bool en); + +/*------------------------------------------------------------------*/ +/* Helper + *------------------------------------------------------------------*/ + +bool usbd_open_edpt_pair(uint8_t rhport, uint8_t const* p_desc, uint8_t ep_count, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in); +void usbd_defer_func(osal_task_func_t func, void *param, bool in_isr); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/test-devices/composite-stm32/lib/tinyusb/osal/osal.h b/test-devices/composite-stm32/lib/tinyusb/osal/osal.h new file mode 100644 index 00000000..8f45ea5c --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/osal/osal.h @@ -0,0 +1,99 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_OSAL_H_ +#define _TUSB_OSAL_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include "common/tusb_common.h" + +typedef void (*osal_task_func_t)( void * ); + +// Timeout +#define OSAL_TIMEOUT_NOTIMEOUT (0) // Return immediately +#define OSAL_TIMEOUT_NORMAL (10) // Default timeout +#define OSAL_TIMEOUT_WAIT_FOREVER (UINT32_MAX) // Wait forever +#define OSAL_TIMEOUT_CONTROL_XFER OSAL_TIMEOUT_WAIT_FOREVER + +// Mutex is required when using a preempted RTOS or MCU has multiple cores +#if (CFG_TUSB_OS == OPT_OS_NONE) && !TUP_MCU_MULTIPLE_CORE + #define OSAL_MUTEX_REQUIRED 0 + #define OSAL_MUTEX_DEF(_name) uint8_t :0 +#else + #define OSAL_MUTEX_REQUIRED 1 + #define OSAL_MUTEX_DEF(_name) osal_mutex_def_t _name +#endif + +// OS thin implementation +#if CFG_TUSB_OS == OPT_OS_NONE + #include "osal_none.h" +#elif CFG_TUSB_OS == OPT_OS_FREERTOS + #include "osal_freertos.h" +#elif CFG_TUSB_OS == OPT_OS_MYNEWT + #include "osal_mynewt.h" +#elif CFG_TUSB_OS == OPT_OS_PICO + #include "osal_pico.h" +#elif CFG_TUSB_OS == OPT_OS_RTTHREAD + #include "osal_rtthread.h" +#elif CFG_TUSB_OS == OPT_OS_RTX4 + #include "osal_rtx4.h" +#elif CFG_TUSB_OS == OPT_OS_CUSTOM + #include "tusb_os_custom.h" // implemented by application +#else + #error OS is not supported yet +#endif + +//--------------------------------------------------------------------+ +// OSAL Porting API +// Should be implemented as static inline function in osal_port.h header +/* + osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef); + bool osal_semaphore_delete(osal_semaphore_t semd_hdl); + bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr); + bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec); + void osal_semaphore_reset(osal_semaphore_t sem_hdl); // TODO removed + + osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef); + bool osal_mutex_delete(osal_mutex_t mutex_hdl) + bool osal_mutex_lock (osal_mutex_t sem_hdl, uint32_t msec); + bool osal_mutex_unlock(osal_mutex_t mutex_hdl); + + osal_queue_t osal_queue_create(osal_queue_def_t* qdef); + bool osal_queue_delete(osal_queue_t qhdl); + bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec); + bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr); + bool osal_queue_empty(osal_queue_t qhdl); +*/ +//--------------------------------------------------------------------+ + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_OSAL_H_ */ diff --git a/test-devices/composite-stm32/lib/tinyusb/osal/osal_none.h b/test-devices/composite-stm32/lib/tinyusb/osal/osal_none.h new file mode 100644 index 00000000..c93f7a86 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/osal/osal_none.h @@ -0,0 +1,196 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef TUSB_OSAL_NONE_H_ +#define TUSB_OSAL_NONE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +//--------------------------------------------------------------------+ +// TASK API +//--------------------------------------------------------------------+ + +#if CFG_TUH_ENABLED +// currently only needed/available in host mode +TU_ATTR_WEAK void osal_task_delay(uint32_t msec); +#endif + +//--------------------------------------------------------------------+ +// Binary Semaphore API +//--------------------------------------------------------------------+ +typedef struct { + volatile uint16_t count; +} osal_semaphore_def_t; + +typedef osal_semaphore_def_t* osal_semaphore_t; + +TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef) { + semdef->count = 0; + return semdef; +} + +TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_delete(osal_semaphore_t semd_hdl) { + (void) semd_hdl; + return true; // nothing to do +} + + +TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) { + (void) in_isr; + sem_hdl->count++; + return true; +} + +// TODO blocking for now +TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec) { + (void) msec; + + while (sem_hdl->count == 0) {} + sem_hdl->count--; + + return true; +} + +TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl) { + sem_hdl->count = 0; +} + +//--------------------------------------------------------------------+ +// MUTEX API +// Within tinyusb, mutex is never used in ISR context +//--------------------------------------------------------------------+ +typedef osal_semaphore_def_t osal_mutex_def_t; +typedef osal_semaphore_t osal_mutex_t; + +#if OSAL_MUTEX_REQUIRED +// Note: multiple cores MCUs usually do provide IPC API for mutex +// or we can use std atomic function + +TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef) { + mdef->count = 1; + return mdef; +} + +TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_delete(osal_mutex_t mutex_hdl) { + (void) mutex_hdl; + return true; // nothing to do +} + +TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock (osal_mutex_t mutex_hdl, uint32_t msec) { + return osal_semaphore_wait(mutex_hdl, msec); +} + +TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl) { + return osal_semaphore_post(mutex_hdl, false); +} + +#else + +#define osal_mutex_create(_mdef) (NULL) +#define osal_mutex_lock(_mutex_hdl, _ms) (true) +#define osal_mutex_unlock(_mutex_hdl) (true) + +#endif + +//--------------------------------------------------------------------+ +// QUEUE API +//--------------------------------------------------------------------+ +#include "common/tusb_fifo.h" + +typedef struct { + void (* interrupt_set)(bool); + tu_fifo_t ff; +} osal_queue_def_t; + +typedef osal_queue_def_t* osal_queue_t; + +// _int_set is used as mutex in OS NONE (disable/enable USB ISR) +#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \ + uint8_t _name##_buf[_depth*sizeof(_type)]; \ + osal_queue_def_t _name = { \ + .interrupt_set = _int_set, \ + .ff = TU_FIFO_INIT(_name##_buf, _depth, _type, false) \ + } + +// lock queue by disable USB interrupt +TU_ATTR_ALWAYS_INLINE static inline void _osal_q_lock(osal_queue_t qhdl) { + // disable dcd/hcd interrupt + qhdl->interrupt_set(false); +} + +// unlock queue +TU_ATTR_ALWAYS_INLINE static inline void _osal_q_unlock(osal_queue_t qhdl) { + // enable dcd/hcd interrupt + qhdl->interrupt_set(true); +} + +TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) { + tu_fifo_clear(&qdef->ff); + return (osal_queue_t) qdef; +} + +TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_delete(osal_queue_t qhdl) { + (void) qhdl; + return true; // nothing to do +} + +TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec) { + (void) msec; // not used, always behave as msec = 0 + + _osal_q_lock(qhdl); + bool success = tu_fifo_read(&qhdl->ff, data); + _osal_q_unlock(qhdl); + + return success; +} + +TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const* data, bool in_isr) { + if (!in_isr) { + _osal_q_lock(qhdl); + } + + bool success = tu_fifo_write(&qhdl->ff, data); + + if (!in_isr) { + _osal_q_unlock(qhdl); + } + + return success; +} + +TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl) { + // Skip queue lock/unlock since this function is primarily called + // with interrupt disabled before going into low power mode + return tu_fifo_empty(&qhdl->ff); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test-devices/composite-stm32/lib/tinyusb/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/test-devices/composite-stm32/lib/tinyusb/portable/st/stm32_fsdev/dcd_stm32_fsdev.c new file mode 100644 index 00000000..a26c6689 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/portable/st/stm32_fsdev/dcd_stm32_fsdev.c @@ -0,0 +1,1383 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Nathan Conrad + * + * Portions: + * Copyright (c) 2016 STMicroelectronics + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * Copyright (c) 2022 Simon Küppers (skuep) + * Copyright (c) 2022 HiFiPhile + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +/********************************************** + * This driver has been tested with the following MCUs: + * - F070, F072, L053, F042F6 + * + * It also should work with minimal changes for any ST MCU with an "USB A"/"PCD"/"HCD" peripheral. This + * covers: + * + * F04x, F072, F078, 070x6/B 1024 byte buffer + * F102, F103 512 byte buffer; no internal D+ pull-up (maybe many more changes?) + * F302xB/C, F303xB/C, F373 512 byte buffer; no internal D+ pull-up + * F302x6/8, F302xD/E2, F303xD/E 1024 byte buffer; no internal D+ pull-up + * L0x2, L0x3 1024 byte buffer + * L1 512 byte buffer + * L4x2, L4x3 1024 byte buffer + * G0 2048 byte buffer + * + * To use this driver, you must: + * - If you are using a device with crystal-less USB, set up the clock recovery system (CRS) + * - Remap pins to be D+/D- on devices that they are shared (for example: F042Fx) + * - This is different to the normal "alternate function" GPIO interface, needs to go through SYSCFG->CFGRx register + * - Enable USB clock; Perhaps use __HAL_RCC_USB_CLK_ENABLE(); + * - (Optionally configure GPIO HAL to tell it the USB driver is using the USB pins) + * - call tusb_init(); + * - periodically call tusb_task(); + * + * Assumptions of the driver: + * - You are not using CAN (it must share the packet buffer) + * - APB clock is >= 10 MHz + * - On some boards, series resistors are required, but not on others. + * - On some boards, D+ pull up resistor (1.5kohm) is required, but not on others. + * - You don't have long-running interrupts; some USB packets must be quickly responded to. + * - You have the ST CMSIS library linked into the project. HAL is not used. + * + * Current driver limitations (i.e., a list of features for you to add): + * - STALL handled, but not tested. + * - Does it work? No clue. + * - All EP BTABLE buffers are created based on max packet size of first EP opened with that address. + * - Packet buffer memory is copied in the interrupt. + * - This is better for performance, but means interrupts are disabled for longer + * - DMA may be the best choice, but it could also be pushed to the USBD task. + * - No double-buffering + * - No DMA + * - Minimal error handling + * - Perhaps error interrupts should be reported to the stack, or cause a device reset? + * - Assumes a single USB peripheral; I think that no hardware has multiple so this is fine. + * - Add a callback for enabling/disabling the D+ PU on devices without an internal PU. + * - F3 models use three separate interrupts. I think we could only use the LP interrupt for + * everything? However, the interrupts are configurable so the DisableInt and EnableInt + * below functions could be adjusting the wrong interrupts (if they had been reconfigured) + * - LPM is not used correctly, or at all? + * + * USB documentation and Reference implementations + * - STM32 Reference manuals + * - STM32 USB Hardware Guidelines AN4879 + * + * - STM32 HAL (much of this driver is based on this) + * - libopencm3/lib/stm32/common/st_usbfs_core.c + * - Keil USB Device http://www.keil.com/pack/doc/mw/USB/html/group__usbd.html + * + * - YouTube OpenTechLab 011; https://www.youtube.com/watch?v=4FOkJLp_PUw + * + * Advantages over HAL driver: + * - Tiny (saves RAM, assumes a single USB peripheral) + * + * Notes: + * - The buffer table is allocated as endpoints are opened. The allocation is only + * cleared when the device is reset. This may be bad if the USB device needs + * to be reconfigured. + */ + +#include "tusb_option.h" + +#if CFG_TUD_ENABLED && defined(TUP_USBIP_FSDEV) + +#include "device/dcd.h" + +#ifdef TUP_USBIP_FSDEV_STM32 +// Undefine to reduce the dependence on HAL +#undef USE_HAL_DRIVER +#include "portable/st/stm32_fsdev/dcd_stm32_fsdev.h" +#endif + +/***************************************************** + * Configuration + *****************************************************/ + +// HW supports max of 8 bidirectional endpoints, but this can be reduced to save RAM +// (8u here would mean 8 IN and 8 OUT) +#ifndef MAX_EP_COUNT +#define MAX_EP_COUNT 8U +#endif + +// If sharing with CAN, one can set this to be non-zero to give CAN space where it wants it +// Both of these MUST be a multiple of 2, and are in byte units. +#ifndef DCD_STM32_BTABLE_BASE +#define DCD_STM32_BTABLE_BASE 0U +#endif + +#ifndef DCD_STM32_BTABLE_SIZE +#define DCD_STM32_BTABLE_SIZE (FSDEV_PMA_SIZE - DCD_STM32_BTABLE_BASE) +#endif + +/*************************************************** + * Checks, structs, defines, function definitions, etc. + */ + +TU_VERIFY_STATIC((MAX_EP_COUNT) <= STFSDEV_EP_COUNT, "Only 8 endpoints supported on the hardware"); +TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) + (DCD_STM32_BTABLE_SIZE)) <= (FSDEV_PMA_SIZE), "BTABLE does not fit in PMA RAM"); +TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) % 8) == 0, "BTABLE base must be aligned to 8 bytes"); + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +// One of these for every EP IN & OUT, uses a bit of RAM.... +typedef struct { + uint8_t *buffer; + tu_fifo_t *ff; + uint16_t total_len; + uint16_t queued_len; + uint16_t max_packet_size; + uint8_t ep_idx; // index for USB_EPnR register + bool iso_in_sending; // Workaround for ISO IN EP doesn't have interrupt mask +} xfer_ctl_t; + +// EP allocator +typedef struct { + uint8_t ep_num; + uint8_t ep_type; + bool allocated[2]; +} ep_alloc_t; + +static xfer_ctl_t xfer_status[MAX_EP_COUNT][2]; + +static ep_alloc_t ep_alloc_status[STFSDEV_EP_COUNT]; + +static TU_ATTR_ALIGNED(4) uint32_t _setup_packet[6]; + +static uint8_t remoteWakeCountdown; // When wake is requested + +//--------------------------------------------------------------------+ +// Prototypes +//--------------------------------------------------------------------+ + +// into the stack. +static void dcd_handle_bus_reset(void); +static void dcd_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix); +static bool edpt_xfer(uint8_t rhport, uint8_t ep_addr); +static void dcd_ep_ctr_handler(void); + +// PMA allocation/access +static uint16_t ep_buf_ptr; ///< Points to first free memory location +static uint32_t dcd_pma_alloc(uint16_t length, bool dbuf); +static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type); +static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes); +static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes); + +static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNBytes); +static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBytes); + +//--------------------------------------------------------------------+ +// Inline helper +//--------------------------------------------------------------------+ + +TU_ATTR_ALWAYS_INLINE static inline xfer_ctl_t *xfer_ctl_ptr(uint32_t ep_addr) +{ + uint8_t epnum = tu_edpt_number(ep_addr); + uint8_t dir = tu_edpt_dir(ep_addr); + // Fix -Werror=null-dereference + TU_ASSERT(epnum < MAX_EP_COUNT, &xfer_status[0][0]); + + return &xfer_status[epnum][dir]; +} + +//--------------------------------------------------------------------+ +// Controller API +//--------------------------------------------------------------------+ + +void dcd_init(uint8_t rhport) +{ + /* Clocks should already be enabled */ + /* Use __HAL_RCC_USB_CLK_ENABLE(); to enable the clocks before calling this function */ + + /* The RM mentions to use a special ordering of PDWN and FRES, but this isn't done in HAL. + * Here, the RM is followed. */ + + for (uint32_t i = 0; i < 200; i++) { // should be a few us + asm("NOP"); + } + // Perform USB peripheral reset + USB->CNTR = USB_CNTR_FRES | USB_CNTR_PDWN; + for (uint32_t i = 0; i < 200; i++) { // should be a few us + asm("NOP"); + } + + USB->CNTR &= ~USB_CNTR_PDWN; + + // Wait startup time, for F042 and F070, this is <= 1 us. + for (uint32_t i = 0; i < 200; i++) { // should be a few us + asm("NOP"); + } + USB->CNTR = 0; // Enable USB + +#if !defined(STM32G0) && !defined(STM32H5) // BTABLE register does not exist any more on STM32G0, it is fixed to USB SRAM base address + USB->BTABLE = DCD_STM32_BTABLE_BASE; +#endif + USB->ISTR = 0; // Clear pending interrupts + + // Reset endpoints to disabled + for (uint32_t i = 0; i < STFSDEV_EP_COUNT; i++) { + // This doesn't clear all bits since some bits are "toggle", but does set the type to DISABLED. + pcd_set_endpoint(USB, i, 0u); + } + + USB->CNTR |= USB_CNTR_RESETM | USB_CNTR_ESOFM | USB_CNTR_CTRM | USB_CNTR_SUSPM | USB_CNTR_WKUPM; + dcd_handle_bus_reset(); + + // Enable pull-up if supported + if (dcd_connect) { + dcd_connect(rhport); + } +} + +// Define only on MCU with internal pull-up. BSP can define on MCU without internal PU. +#if defined(USB_BCDR_DPPU) + +// Disable internal D+ PU +void dcd_disconnect(uint8_t rhport) +{ + (void)rhport; + USB->BCDR &= ~(USB_BCDR_DPPU); +} + +// Enable internal D+ PU +void dcd_connect(uint8_t rhport) +{ + (void)rhport; + USB->BCDR |= USB_BCDR_DPPU; +} + +#elif defined(SYSCFG_PMC_USB_PU) // works e.g. on STM32L151 +// Disable internal D+ PU +void dcd_disconnect(uint8_t rhport) +{ + (void)rhport; + SYSCFG->PMC &= ~(SYSCFG_PMC_USB_PU); +} + +// Enable internal D+ PU +void dcd_connect(uint8_t rhport) +{ + (void)rhport; + SYSCFG->PMC |= SYSCFG_PMC_USB_PU; +} +#endif + +void dcd_sof_enable(uint8_t rhport, bool en) +{ + (void)rhport; + (void)en; + + if (en) { + USB->CNTR |= USB_CNTR_SOFM; + } else { + USB->CNTR &= ~USB_CNTR_SOFM; + } +} + +// Enable device interrupt +void dcd_int_enable(uint8_t rhport) +{ + (void)rhport; + // Member here forces write to RAM before allowing ISR to execute + __DSB(); + __ISB(); +#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0 || CFG_TUSB_MCU == OPT_MCU_STM32L4 + NVIC_EnableIRQ(USB_IRQn); + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L1 + NVIC_EnableIRQ(USB_LP_IRQn); + +#elif CFG_TUSB_MCU == OPT_MCU_STM32F3 +// Some STM32F302/F303 devices allow to remap the USB interrupt vectors from +// shared USB/CAN IRQs to separate CAN and USB IRQs. +// This dynamically checks if this remap is active to enable the right IRQs. +#ifdef SYSCFG_CFGR1_USB_IT_RMP + if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) { + NVIC_EnableIRQ(USB_HP_IRQn); + NVIC_EnableIRQ(USB_LP_IRQn); + NVIC_EnableIRQ(USBWakeUp_RMP_IRQn); + } else +#endif + { + NVIC_EnableIRQ(USB_HP_CAN_TX_IRQn); + NVIC_EnableIRQ(USB_LP_CAN_RX0_IRQn); + NVIC_EnableIRQ(USBWakeUp_IRQn); + } +#elif CFG_TUSB_MCU == OPT_MCU_STM32F1 + NVIC_EnableIRQ(USB_HP_CAN1_TX_IRQn); + NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn); + NVIC_EnableIRQ(USBWakeUp_IRQn); + +#elif CFG_TUSB_MCU == OPT_MCU_STM32G4 + NVIC_EnableIRQ(USB_HP_IRQn); + NVIC_EnableIRQ(USB_LP_IRQn); + NVIC_EnableIRQ(USBWakeUp_IRQn); + +#elif CFG_TUSB_MCU == OPT_MCU_STM32G0 +#ifdef STM32G0B0xx + NVIC_EnableIRQ(USB_IRQn); +#else + NVIC_EnableIRQ(USB_UCPD1_2_IRQn); +#endif + +#elif CFG_TUSB_MCU == OPT_MCU_STM32H5 + NVIC_EnableIRQ(USB_DRD_FS_IRQn); + +#elif CFG_TUSB_MCU == OPT_MCU_STM32WB + NVIC_EnableIRQ(USB_HP_IRQn); + NVIC_EnableIRQ(USB_LP_IRQn); + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L5 + NVIC_EnableIRQ(USB_FS_IRQn); + +#else +#error Unknown arch in USB driver +#endif +} + +// Disable device interrupt +void dcd_int_disable(uint8_t rhport) +{ + (void)rhport; + +#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0 || CFG_TUSB_MCU == OPT_MCU_STM32L4 + NVIC_DisableIRQ(USB_IRQn); +#elif CFG_TUSB_MCU == OPT_MCU_STM32L1 + NVIC_DisableIRQ(USB_LP_IRQn); +#elif CFG_TUSB_MCU == OPT_MCU_STM32F3 +// Some STM32F302/F303 devices allow to remap the USB interrupt vectors from +// shared USB/CAN IRQs to separate CAN and USB IRQs. +// This dynamically checks if this remap is active to disable the right IRQs. +#ifdef SYSCFG_CFGR1_USB_IT_RMP + if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) { + NVIC_DisableIRQ(USB_HP_IRQn); + NVIC_DisableIRQ(USB_LP_IRQn); + NVIC_DisableIRQ(USBWakeUp_RMP_IRQn); + } else +#endif + { + NVIC_DisableIRQ(USB_HP_CAN_TX_IRQn); + NVIC_DisableIRQ(USB_LP_CAN_RX0_IRQn); + NVIC_DisableIRQ(USBWakeUp_IRQn); + } +#elif CFG_TUSB_MCU == OPT_MCU_STM32F1 + NVIC_DisableIRQ(USB_HP_CAN1_TX_IRQn); + NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn); + NVIC_DisableIRQ(USBWakeUp_IRQn); + +#elif CFG_TUSB_MCU == OPT_MCU_STM32G4 + NVIC_DisableIRQ(USB_HP_IRQn); + NVIC_DisableIRQ(USB_LP_IRQn); + NVIC_DisableIRQ(USBWakeUp_IRQn); + +#elif CFG_TUSB_MCU == OPT_MCU_STM32G0 +#ifdef STM32G0B0xx + NVIC_DisableIRQ(USB_IRQn); +#else + NVIC_DisableIRQ(USB_UCPD1_2_IRQn); +#endif + +#elif CFG_TUSB_MCU == OPT_MCU_STM32H5 + NVIC_DisableIRQ(USB_DRD_FS_IRQn); + +#elif CFG_TUSB_MCU == OPT_MCU_STM32WB + NVIC_DisableIRQ(USB_HP_IRQn); + NVIC_DisableIRQ(USB_LP_IRQn); + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L5 + NVIC_DisableIRQ(USB_FS_IRQn); + +#else +#error Unknown arch in USB driver +#endif + + // CMSIS has a membar after disabling interrupts +} + +// Receive Set Address request, mcu port must also include status IN response +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) +{ + (void)rhport; + (void)dev_addr; + + // Respond with status + dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK | 0x00, NULL, 0); + + // DCD can only set address after status for this request is complete. + // do it at dcd_edpt0_status_complete() +} + +void dcd_remote_wakeup(uint8_t rhport) +{ + (void)rhport; + + USB->CNTR |= USB_CNTR_RESUME; + remoteWakeCountdown = 4u; // required to be 1 to 15 ms, ESOF should trigger every 1ms. +} + +static const tusb_desc_endpoint_t ep0OUT_desc = { + .bLength = sizeof(tusb_desc_endpoint_t), + .bDescriptorType = TUSB_DESC_ENDPOINT, + .bEndpointAddress = 0x00, + .bmAttributes = {.xfer = TUSB_XFER_CONTROL}, + .wMaxPacketSize = CFG_TUD_ENDPOINT0_SIZE, + .bInterval = 0 +}; + +static const tusb_desc_endpoint_t ep0IN_desc = { + .bLength = sizeof(tusb_desc_endpoint_t), + .bDescriptorType = TUSB_DESC_ENDPOINT, + .bEndpointAddress = 0x80, + .bmAttributes = {.xfer = TUSB_XFER_CONTROL}, + .wMaxPacketSize = CFG_TUD_ENDPOINT0_SIZE, + .bInterval = 0 +}; + +static void dcd_handle_bus_reset(void) +{ + USB->DADDR = 0u; // disable USB peripheral by clearing the EF flag + + for (uint32_t i = 0; i < STFSDEV_EP_COUNT; i++) { + // Clear EP allocation status + ep_alloc_status[i].ep_num = 0xFF; + ep_alloc_status[i].ep_type = 0xFF; + ep_alloc_status[i].allocated[0] = false; + ep_alloc_status[i].allocated[1] = false; + } + + // Reset PMA allocation + ep_buf_ptr = DCD_STM32_BTABLE_BASE + 8 * MAX_EP_COUNT; + + dcd_edpt_open(0, &ep0OUT_desc); + dcd_edpt_open(0, &ep0IN_desc); + + USB->DADDR = USB_DADDR_EF; // Set enable flag, and leaving the device address as zero. +} + +// Handle CTR interrupt for the TX/IN direction +// +// Upon call, (wIstr & USB_ISTR_DIR) == 0U +static void dcd_ep_ctr_tx_handler(uint32_t wIstr) +{ + uint32_t EPindex = wIstr & USB_ISTR_EP_ID; + uint32_t wEPRegVal = pcd_get_endpoint(USB, EPindex); + uint8_t ep_addr = (wEPRegVal & USB_EPADDR_FIELD) | TUSB_DIR_IN_MASK; + + // Verify the CTR_TX bit is set. This was in the ST Micro code, + // but I'm not sure it's actually necessary? + if ((wEPRegVal & USB_EP_CTR_TX) == 0U) { + return; + } + + /* clear int flag */ + pcd_clear_tx_ep_ctr(USB, EPindex); + + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + + if ((wEPRegVal & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS) { + // Ignore spurious interrupts that we don't schedule + // host can send IN token while there is no data to send, since ISO does not have NAK + // this will result to zero length packet --> trigger interrupt (which cannot be masked) + if (!xfer->iso_in_sending) { + return; + } + xfer->iso_in_sending = false; + + if (wEPRegVal & USB_EP_DTOG_TX) { + pcd_set_ep_tx_dbuf0_cnt(USB, EPindex, 0); + } else { + pcd_set_ep_tx_dbuf1_cnt(USB, EPindex, 0); + } + } + + if ((xfer->total_len != xfer->queued_len)) { + dcd_transmit_packet(xfer, EPindex); + } else { + dcd_event_xfer_complete(0, ep_addr, xfer->total_len, XFER_RESULT_SUCCESS, true); + } +} + +// Handle CTR interrupt for the RX/OUT direction +// Upon call, (wIstr & USB_ISTR_DIR) == 0U +static void dcd_ep_ctr_rx_handler(uint32_t wIstr) +{ +#ifdef FSDEV_BUS_32BIT + /* https://www.st.com/resource/en/errata_sheet/es0561-stm32h503cbebkbrb-device-errata-stmicroelectronics.pdf + * From STM32H503 errata 2.15.1: Buffer description table update completes after CTR interrupt triggers + * Description: + * - During OUT transfers, the correct transfer interrupt (CTR) is triggered a little before the last USB SRAM accesses + * have completed. If the software responds quickly to the interrupt, the full buffer contents may not be correct. + * Workaround: + * - Software should ensure that a small delay is included before accessing the SRAM contents. This delay + * should be 800 ns in Full Speed mode and 6.4 μs in Low Speed mode + * - Since H5 can run up to 250Mhz -> 1 cycle = 4ns. Per errata, we need to wait 200 cycles. Though executing code + * also takes time, so we'll wait 60 cycles (count = 20). + * - Since Low Speed mode is not supported/popular, we will ignore it for now. + * + * Note: this errata also seems to apply to G0, U5, H5 etc. + */ + volatile uint32_t cycle_count = 20; // defined as PCD_RX_PMA_CNT in stm32 hal_driver + while (cycle_count > 0U) { + cycle_count--; // each count take 3 cycles (1 for sub, jump, and compare) + } +#endif + + uint32_t EPindex = wIstr & USB_ISTR_EP_ID; + uint32_t wEPRegVal = pcd_get_endpoint(USB, EPindex); + uint8_t ep_addr = wEPRegVal & USB_EPADDR_FIELD; + + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + + // Verify the CTR_RX bit is set. This was in the ST Micro code, + // but I'm not sure it's actually necessary? + if ((wEPRegVal & USB_EP_CTR_RX) == 0U) { + return; + } + + if ((ep_addr == 0U) && ((wEPRegVal & USB_EP_SETUP) != 0U)) { + /* Setup packet */ + uint32_t count = pcd_get_ep_rx_cnt(USB, EPindex); + // Setup packet should always be 8 bytes. If not, ignore it, and try again. + if (count == 8) { + // Must reset EP to NAK (in case it had been stalling) (though, maybe too late here) + pcd_set_ep_rx_status(USB, 0u, USB_EP_RX_NAK); + pcd_set_ep_tx_status(USB, 0u, USB_EP_TX_NAK); +#ifdef FSDEV_BUS_32BIT + dcd_event_setup_received(0, (uint8_t *)(USB_PMAADDR + pcd_get_ep_rx_address(USB, EPindex)), true); +#else + // The setup_received function uses memcpy, so this must first copy the setup data into + // user memory, to allow for the 32-bit access that memcpy performs. + uint8_t userMemBuf[8]; + dcd_read_packet_memory(userMemBuf, pcd_get_ep_rx_address(USB, EPindex), 8); + dcd_event_setup_received(0, (uint8_t *)userMemBuf, true); +#endif + } + } else { + // Clear RX CTR interrupt flag + if (ep_addr != 0u) { + pcd_clear_rx_ep_ctr(USB, EPindex); + } + + uint32_t count; + uint16_t addr; + /* Read from correct register when ISOCHRONOUS (double buffered) */ + if ((wEPRegVal & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS) { + if (wEPRegVal & USB_EP_DTOG_RX) { + count = pcd_get_ep_dbuf0_cnt(USB, EPindex); + addr = pcd_get_ep_dbuf0_address(USB, EPindex); + } else { + count = pcd_get_ep_dbuf1_cnt(USB, EPindex); + addr = pcd_get_ep_dbuf1_address(USB, EPindex); + } + } else { + count = pcd_get_ep_rx_cnt(USB, EPindex); + addr = pcd_get_ep_rx_address(USB, EPindex); + } + + TU_ASSERT(count <= xfer->max_packet_size, /**/); + + if (count != 0U) { + if (xfer->ff) { + dcd_read_packet_memory_ff(xfer->ff, addr, count); + } else { + dcd_read_packet_memory(&(xfer->buffer[xfer->queued_len]), addr, count); + } + + xfer->queued_len = (uint16_t)(xfer->queued_len + count); + } + + if ((count < xfer->max_packet_size) || (xfer->queued_len == xfer->total_len)) { + // all bytes received or short packet + dcd_event_xfer_complete(0, ep_addr, xfer->queued_len, XFER_RESULT_SUCCESS, true); + } else { + /* Set endpoint active again for receiving more data. + * Note that isochronous endpoints stay active always */ + if ((wEPRegVal & USB_EP_TYPE_MASK) != USB_EP_ISOCHRONOUS) { + uint16_t remaining = xfer->total_len - xfer->queued_len; + uint16_t cnt = tu_min16(remaining, xfer->max_packet_size); + pcd_set_ep_rx_cnt(USB, EPindex, cnt); + } + pcd_set_ep_rx_status(USB, EPindex, USB_EP_RX_VALID); + } + } + + // For EP0, prepare to receive another SETUP packet. + // Clear CTR last so that a new packet does not overwrite the packing being read. + // (Based on the docs, it seems SETUP will always be accepted after CTR is cleared) + if (ep_addr == 0u) { + // Always be prepared for a status packet... + pcd_set_ep_rx_cnt(USB, EPindex, CFG_TUD_ENDPOINT0_SIZE); + pcd_clear_rx_ep_ctr(USB, EPindex); + } +} + +static void dcd_ep_ctr_handler(void) +{ + uint32_t wIstr; + + /* stay in loop while pending interrupts */ + while (((wIstr = USB->ISTR) & USB_ISTR_CTR) != 0U) { + if ((wIstr & USB_ISTR_DIR) == 0U) { + /* TX/IN */ + dcd_ep_ctr_tx_handler(wIstr); + } else { + /* RX/OUT*/ + dcd_ep_ctr_rx_handler(wIstr); + } + } +} + +void dcd_int_handler(uint8_t rhport) +{ + + (void)rhport; + + uint32_t int_status = USB->ISTR; + // const uint32_t handled_ints = USB_ISTR_CTR | USB_ISTR_RESET | USB_ISTR_WKUP + // | USB_ISTR_SUSP | USB_ISTR_SOF | USB_ISTR_ESOF; + // unused IRQs: (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_L1REQ ) + + // The ST driver loops here on the CTR bit, but that loop has been moved into the + // dcd_ep_ctr_handler(), so less need to loop here. The other interrupts shouldn't + // be triggered repeatedly. + + /* Put SOF flag at the beginning of ISR in case to get least amount of jitter if it is used for timing purposes */ + if (int_status & USB_ISTR_SOF) { + USB->ISTR = (fsdev_bus_t)~USB_ISTR_SOF; + dcd_event_sof(0, USB->FNR & USB_FNR_FN, true); + } + + if (int_status & USB_ISTR_RESET) { + // USBRST is start of reset. + USB->ISTR = (fsdev_bus_t)~USB_ISTR_RESET; + dcd_handle_bus_reset(); + dcd_event_bus_reset(0, TUSB_SPEED_FULL, true); + return; // Don't do the rest of the things here; perhaps they've been cleared? + } + + if (int_status & USB_ISTR_CTR) { + /* servicing of the endpoint correct transfer interrupt */ + /* clear of the CTR flag into the sub */ + dcd_ep_ctr_handler(); + } + + if (int_status & USB_ISTR_WKUP) { + USB->CNTR &= ~USB_CNTR_LPMODE; + USB->CNTR &= ~USB_CNTR_FSUSP; + + USB->ISTR = (fsdev_bus_t)~USB_ISTR_WKUP; + dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); + } + + if (int_status & USB_ISTR_SUSP) { + /* Suspend is asserted for both suspend and unplug events. without Vbus monitoring, + * these events cannot be differentiated, so we only trigger suspend. */ + + /* Force low-power mode in the macrocell */ + USB->CNTR |= USB_CNTR_FSUSP; + USB->CNTR |= USB_CNTR_LPMODE; + + /* clear of the ISTR bit must be done after setting of CNTR_FSUSP */ + USB->ISTR = (fsdev_bus_t)~USB_ISTR_SUSP; + dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true); + } + + if (int_status & USB_ISTR_ESOF) { + if (remoteWakeCountdown == 1u) { + USB->CNTR &= ~USB_CNTR_RESUME; + } + if (remoteWakeCountdown > 0u) { + remoteWakeCountdown--; + } + USB->ISTR = (fsdev_bus_t)~USB_ISTR_ESOF; + } +} + +//--------------------------------------------------------------------+ +// Endpoint API +//--------------------------------------------------------------------+ + +// Invoked when a control transfer's status stage is complete. +// May help DCD to prepare for next control transfer, this API is optional. +void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const *request) +{ + (void)rhport; + + if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && + request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && + request->bRequest == TUSB_REQ_SET_ADDRESS) { + uint8_t const dev_addr = (uint8_t)request->wValue; + + // Setting new address after the whole request is complete + USB->DADDR &= ~USB_DADDR_ADD; + USB->DADDR |= dev_addr; // leave the enable bit set + } +} + +/*** + * Allocate a section of PMA + * In case of double buffering, high 16bit is the address of 2nd buffer + * During failure, TU_ASSERT is used. If this happens, rework/reallocate memory manually. + */ +static uint32_t dcd_pma_alloc(uint16_t length, bool dbuf) +{ + // Ensure allocated buffer is aligned +#ifdef FSDEV_BUS_32BIT + length = (length + 3) & ~0x03; +#else + length = (length + 1) & ~0x01; +#endif + + uint32_t addr = ep_buf_ptr; + ep_buf_ptr = (uint16_t)(ep_buf_ptr + length); // increment buffer pointer + + if (dbuf) { + addr |= ((uint32_t)ep_buf_ptr) << 16; + ep_buf_ptr = (uint16_t)(ep_buf_ptr + length); // increment buffer pointer + } + + // Verify packet buffer is not overflowed + TU_ASSERT(ep_buf_ptr <= FSDEV_PMA_SIZE, 0xFFFF); + + return addr; +} + +/*** + * Allocate hardware endpoint + */ +static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type) +{ + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + for (uint8_t i = 0; i < STFSDEV_EP_COUNT; i++) { + // Check if already allocated + if (ep_alloc_status[i].allocated[dir] && + ep_alloc_status[i].ep_type == ep_type && + ep_alloc_status[i].ep_num == epnum) { + return i; + } + + // If EP of current direction is not allocated + // Except for ISO endpoint, both direction should be free + if (!ep_alloc_status[i].allocated[dir] && + (ep_type != TUSB_XFER_ISOCHRONOUS || !ep_alloc_status[i].allocated[dir ^ 1])) { + // Check if EP number is the same + if (ep_alloc_status[i].ep_num == 0xFF || ep_alloc_status[i].ep_num == epnum) { + // One EP pair has to be the same type + if (ep_alloc_status[i].ep_type == 0xFF || ep_alloc_status[i].ep_type == ep_type) { + ep_alloc_status[i].ep_num = epnum; + ep_alloc_status[i].ep_type = ep_type; + ep_alloc_status[i].allocated[dir] = true; + + return i; + } + } + } + } + + // Allocation failed + TU_ASSERT(0); +} + +// The STM32F0 doesn't seem to like |= or &= to manipulate the EP#R registers, +// so I'm using the #define from HAL here, instead. + +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *p_endpoint_desc) +{ + (void)rhport; + uint8_t const ep_addr = p_endpoint_desc->bEndpointAddress; + uint8_t const ep_idx = dcd_ep_alloc(ep_addr, p_endpoint_desc->bmAttributes.xfer); + uint8_t const dir = tu_edpt_dir(ep_addr); + const uint16_t packet_size = tu_edpt_packet_size(p_endpoint_desc); + const uint16_t buffer_size = pcd_aligned_buffer_size(packet_size); + uint16_t pma_addr; + uint32_t wType; + + TU_ASSERT(ep_idx < STFSDEV_EP_COUNT); + TU_ASSERT(buffer_size <= 64); + + // Set type + switch (p_endpoint_desc->bmAttributes.xfer) { + case TUSB_XFER_CONTROL: + wType = USB_EP_CONTROL; + break; + case TUSB_XFER_BULK: + wType = USB_EP_CONTROL; + break; + + case TUSB_XFER_INTERRUPT: + wType = USB_EP_INTERRUPT; + break; + + default: + // Note: ISO endpoint should use alloc / active functions + TU_ASSERT(false); + } + + pcd_set_eptype(USB, ep_idx, wType); + pcd_set_ep_address(USB, ep_idx, tu_edpt_number(ep_addr)); + + /* Create a packet memory buffer area. */ + pma_addr = dcd_pma_alloc(buffer_size, false); + + if (dir == TUSB_DIR_IN) { + pcd_set_ep_tx_address(USB, ep_idx, pma_addr); + pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_NAK); + pcd_clear_tx_dtog(USB, ep_idx); + } else { + pcd_set_ep_rx_address(USB, ep_idx, pma_addr); + pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_NAK); + pcd_clear_rx_dtog(USB, ep_idx); + } + + xfer_ctl_ptr(ep_addr)->max_packet_size = packet_size; + xfer_ctl_ptr(ep_addr)->ep_idx = ep_idx; + + return true; +} + +void dcd_edpt_close_all(uint8_t rhport) +{ + (void)rhport; + + for (uint32_t i = 1; i < STFSDEV_EP_COUNT; i++) { + // Reset endpoint + pcd_set_endpoint(USB, i, 0); + // Clear EP allocation status + ep_alloc_status[i].ep_num = 0xFF; + ep_alloc_status[i].ep_type = 0xFF; + ep_alloc_status[i].allocated[0] = false; + ep_alloc_status[i].allocated[1] = false; + } + + // Reset PMA allocation + ep_buf_ptr = DCD_STM32_BTABLE_BASE + 8 * MAX_EP_COUNT + 2 * CFG_TUD_ENDPOINT0_SIZE; +} + +/** + * Close an endpoint. + * + * This function may be called with interrupts enabled or disabled. + * + * This also clears transfers in progress, should there be any. + */ +void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) +{ + (void)rhport; + + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + uint8_t const ep_idx = xfer->ep_idx; + uint8_t const dir = tu_edpt_dir(ep_addr); + + if (dir == TUSB_DIR_IN) { + pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_DIS); + } else { + pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_DIS); + } +} + +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) +{ + (void)rhport; + + uint8_t const ep_idx = dcd_ep_alloc(ep_addr, TUSB_XFER_ISOCHRONOUS); + const uint16_t buffer_size = pcd_aligned_buffer_size(largest_packet_size); + + /* Create a packet memory buffer area. Enable double buffering for devices with 2048 bytes PMA, + for smaller devices double buffering occupy too much space. */ +#if FSDEV_PMA_SIZE > 1024u + uint32_t pma_addr = dcd_pma_alloc(buffer_size, true); + uint16_t pma_addr2 = pma_addr >> 16; +#else + uint32_t pma_addr = dcd_pma_alloc(buffer_size, true); + uint16_t pma_addr2 = pma_addr; +#endif + pcd_set_ep_tx_address(USB, ep_idx, pma_addr); + pcd_set_ep_rx_address(USB, ep_idx, pma_addr2); + + pcd_set_eptype(USB, ep_idx, USB_EP_ISOCHRONOUS); + + xfer_ctl_ptr(ep_addr)->ep_idx = ep_idx; + + return true; +} + +bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *p_endpoint_desc) +{ + (void)rhport; + uint8_t const ep_addr = p_endpoint_desc->bEndpointAddress; + uint8_t const ep_idx = xfer_ctl_ptr(ep_addr)->ep_idx; + uint8_t const dir = tu_edpt_dir(ep_addr); + const uint16_t packet_size = tu_edpt_packet_size(p_endpoint_desc); + + pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_DIS); + pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_DIS); + + pcd_set_ep_address(USB, ep_idx, tu_edpt_number(ep_addr)); + + pcd_clear_tx_dtog(USB, ep_idx); + pcd_clear_rx_dtog(USB, ep_idx); + + if (dir == TUSB_DIR_IN) { + pcd_rx_dtog(USB, ep_idx); + } else { + pcd_tx_dtog(USB, ep_idx); + } + + xfer_ctl_ptr(ep_addr)->max_packet_size = packet_size; + + return true; +} + +// Currently, single-buffered, and only 64 bytes at a time (max) + +static void dcd_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix) +{ + uint16_t len = (uint16_t)(xfer->total_len - xfer->queued_len); + if (len > xfer->max_packet_size) { + len = xfer->max_packet_size; + } + + uint16_t ep_reg = pcd_get_endpoint(USB, ep_ix); + bool const is_iso = (ep_reg & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS; + uint16_t addr_ptr; + + if (is_iso) { + if (ep_reg & USB_EP_DTOG_TX) { + addr_ptr = pcd_get_ep_dbuf1_address(USB, ep_ix); + pcd_set_ep_tx_dbuf1_cnt(USB, ep_ix, len); + } else { + addr_ptr = pcd_get_ep_dbuf0_address(USB, ep_ix); + pcd_set_ep_tx_dbuf0_cnt(USB, ep_ix, len); + } + } else { + addr_ptr = pcd_get_ep_tx_address(USB, ep_ix); + pcd_set_ep_tx_cnt(USB, ep_ix, len); + } + + if (xfer->ff) { + dcd_write_packet_memory_ff(xfer->ff, addr_ptr, len); + } else { + dcd_write_packet_memory(addr_ptr, &(xfer->buffer[xfer->queued_len]), len); + } + xfer->queued_len = (uint16_t)(xfer->queued_len + len); + + dcd_int_disable(0); + pcd_set_ep_tx_status(USB, ep_ix, USB_EP_TX_VALID); + if (is_iso) { + xfer->iso_in_sending = true; + } + dcd_int_enable(0); +} + +static bool edpt_xfer(uint8_t rhport, uint8_t ep_addr) +{ + (void)rhport; + + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + uint8_t const ep_idx = xfer->ep_idx; + uint8_t const dir = tu_edpt_dir(ep_addr); + + if (dir == TUSB_DIR_IN) { + dcd_transmit_packet(xfer, ep_idx); + } else { + // A setup token can occur immediately after an OUT STATUS packet so make sure we have a valid + // buffer for the control endpoint. + if (ep_idx == 0 && xfer->buffer == NULL) { + xfer->buffer = (uint8_t *)_setup_packet; + } + + uint32_t cnt = (uint32_t ) tu_min16(xfer->total_len, xfer->max_packet_size); + uint16_t ep_reg = pcd_get_endpoint(USB, ep_idx); + + if ((ep_reg & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS) { + pcd_set_ep_rx_dbuf0_cnt(USB, ep_idx, cnt); + pcd_set_ep_rx_dbuf1_cnt(USB, ep_idx, cnt); + } else { + pcd_set_ep_rx_cnt(USB, ep_idx, cnt); + } + + pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_VALID); + } + + return true; +} + +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) +{ + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + + xfer->buffer = buffer; + xfer->ff = NULL; + xfer->total_len = total_bytes; + xfer->queued_len = 0; + + return edpt_xfer(rhport, ep_addr); +} + +bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t total_bytes) +{ + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + xfer->buffer = NULL; + xfer->ff = ff; + xfer->total_len = total_bytes; + xfer->queued_len = 0; + + return edpt_xfer(rhport, ep_addr); +} + +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) +{ + (void)rhport; + + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + uint8_t const ep_idx = xfer->ep_idx; + uint8_t const dir = tu_edpt_dir(ep_addr); + + if (dir == TUSB_DIR_IN) { + pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_STALL); + } else { + pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_STALL); + } +} + +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) +{ + (void)rhport; + + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + uint8_t const ep_idx = xfer->ep_idx; + uint8_t const dir = tu_edpt_dir(ep_addr); + + if (dir == TUSB_DIR_IN) { // IN + if (pcd_get_eptype(USB, ep_idx) != USB_EP_ISOCHRONOUS) { + pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_NAK); + } + + /* Reset to DATA0 if clearing stall condition. */ + pcd_clear_tx_dtog(USB, ep_idx); + } else { // OUT + if (pcd_get_eptype(USB, ep_idx) != USB_EP_ISOCHRONOUS) { + pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_NAK); + } + /* Reset to DATA0 if clearing stall condition. */ + pcd_clear_rx_dtog(USB, ep_idx); + } +} + +#ifdef FSDEV_BUS_32BIT +static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes) +{ + const uint8_t *srcVal = src; + volatile uint32_t *dst32 = (volatile uint32_t *)(USB_PMAADDR + dst); + + for (uint32_t n = wNBytes / 4; n > 0; --n) { + *dst32++ = tu_unaligned_read32(srcVal); + srcVal += 4; + } + + wNBytes = wNBytes & 0x03; + if (wNBytes) { + uint32_t wrVal = *srcVal; + wNBytes--; + + if (wNBytes) { + wrVal |= *++srcVal << 8; + wNBytes--; + + if (wNBytes) { + wrVal |= *++srcVal << 16; + } + } + + *dst32 = wrVal; + } + + return true; +} +#else +// Packet buffer access can only be 8- or 16-bit. +/** + * @brief Copy a buffer from user memory area to packet memory area (PMA). + * This uses byte-access for user memory (so support non-aligned buffers) + * and 16-bit access for packet memory. + * @param dst, byte address in PMA; must be 16-bit aligned + * @param src pointer to user memory area. + * @param wPMABufAddr address into PMA. + * @param wNBytes no. of bytes to be copied. + * @retval None + */ +static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes) +{ + uint32_t n = (uint32_t)wNBytes >> 1U; + uint16_t temp1, temp2; + const uint8_t *srcVal; + + // The GCC optimizer will combine access to 32-bit sizes if we let it. Force + // it volatile so that it won't do that. + __IO uint16_t *pdwVal; + + srcVal = src; + pdwVal = &pma[FSDEV_PMA_STRIDE * (dst >> 1)]; + + while (n--) { + temp1 = (uint16_t)*srcVal; + srcVal++; + temp2 = temp1 | ((uint16_t)(((uint16_t)(*srcVal)) << 8U)); + *pdwVal = temp2; + pdwVal += FSDEV_PMA_STRIDE; + srcVal++; + } + + if (wNBytes) { + temp1 = *srcVal; + *pdwVal = temp1; + } + + return true; +} +#endif + +/** + * @brief Copy from FIFO to packet memory area (PMA). + * Uses byte-access of system memory and 16-bit access of packet memory + * @param wNBytes no. of bytes to be copied. + * @retval None + */ +static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNBytes) +{ + // Since we copy from a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies + tu_fifo_buffer_info_t info; + tu_fifo_get_read_info(ff, &info); + + uint16_t cnt_lin = TU_MIN(wNBytes, info.len_lin); + uint16_t cnt_wrap = TU_MIN(wNBytes - cnt_lin, info.len_wrap); + + // We want to read from the FIFO and write it into the PMA, if LIN part is ODD and has WRAPPED part, + // last lin byte will be combined with wrapped part + // To ensure PMA is always access aligned (dst aligned to 16 or 32 bit) +#ifdef FSDEV_BUS_32BIT + if ((cnt_lin & 0x03) && cnt_wrap) { + // Copy first linear part + dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin & ~0x03); + dst += cnt_lin & ~0x03; + + // Copy last linear bytes & first wrapped bytes to buffer + uint32_t i; + uint8_t tmp[4]; + for (i = 0; i < (cnt_lin & 0x03); i++) { + tmp[i] = ((uint8_t *)info.ptr_lin)[(cnt_lin & ~0x03) + i]; + } + uint32_t wCnt = cnt_wrap; + for (; i < 4 && wCnt > 0; i++, wCnt--) { + tmp[i] = *(uint8_t *)info.ptr_wrap; + info.ptr_wrap = (uint8_t *)info.ptr_wrap + 1; + } + + // Write unaligned buffer + dcd_write_packet_memory(dst, &tmp, 4); + dst += 4; + + // Copy rest of wrapped byte + if (wCnt) + dcd_write_packet_memory(dst, info.ptr_wrap, wCnt); + } +#else + if ((cnt_lin & 0x01) && cnt_wrap) { + // Copy first linear part + dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin & ~0x01); + dst += cnt_lin & ~0x01; + + // Copy last linear byte & first wrapped byte + uint16_t tmp = ((uint8_t *)info.ptr_lin)[cnt_lin - 1] | ((uint16_t)(((uint8_t *)info.ptr_wrap)[0]) << 8U); + dcd_write_packet_memory(dst, &tmp, 2); + dst += 2; + + // Copy rest of wrapped byte + dcd_write_packet_memory(dst, ((uint8_t *)info.ptr_wrap) + 1, cnt_wrap - 1); + } +#endif + else { + // Copy linear part + dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin); + dst += info.len_lin; + + if (info.len_wrap) { + // Copy wrapped byte + dcd_write_packet_memory(dst, info.ptr_wrap, cnt_wrap); + } + } + + tu_fifo_advance_read_pointer(ff, cnt_lin + cnt_wrap); + + return true; +} + +#ifdef FSDEV_BUS_32BIT +static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes) +{ + uint8_t *dstVal = dst; + volatile uint32_t *src32 = (volatile uint32_t *)(USB_PMAADDR + src); + + for (uint32_t n = wNBytes / 4; n > 0; --n) { + tu_unaligned_write32(dstVal, *src32++); + dstVal += 4; + } + + wNBytes = wNBytes & 0x03; + if (wNBytes) { + uint32_t rdVal = *src32; + + *dstVal = tu_u32_byte0(rdVal); + wNBytes--; + + if (wNBytes) { + *++dstVal = tu_u32_byte1(rdVal); + wNBytes--; + + if (wNBytes) { + *++dstVal = tu_u32_byte2(rdVal); + } + } + } + + return true; +} +#else +/** + * @brief Copy a buffer from packet memory area (PMA) to user memory area. + * Uses byte-access of system memory and 16-bit access of packet memory + * @param wNBytes no. of bytes to be copied. + * @retval None + */ +static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes) +{ + uint32_t n = (uint32_t)wNBytes >> 1U; + // The GCC optimizer will combine access to 32-bit sizes if we let it. Force + // it volatile so that it won't do that. + __IO const uint16_t *pdwVal; + uint32_t temp; + + pdwVal = &pma[FSDEV_PMA_STRIDE * (src >> 1)]; + uint8_t *dstVal = (uint8_t *)dst; + + while (n--) { + temp = *pdwVal; + pdwVal += FSDEV_PMA_STRIDE; + *dstVal++ = ((temp >> 0) & 0xFF); + *dstVal++ = ((temp >> 8) & 0xFF); + } + + if (wNBytes & 0x01) { + temp = *pdwVal; + pdwVal += FSDEV_PMA_STRIDE; + *dstVal++ = ((temp >> 0) & 0xFF); + } + return true; +} +#endif + +/** + * @brief Copy a buffer from user packet memory area (PMA) to FIFO. + * Uses byte-access of system memory and 16-bit access of packet memory + * @param wNBytes no. of bytes to be copied. + * @retval None + */ +static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBytes) +{ + // Since we copy into a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies + // Check for first linear part + tu_fifo_buffer_info_t info; + tu_fifo_get_write_info(ff, &info); // We want to read from the FIFO + + uint16_t cnt_lin = TU_MIN(wNBytes, info.len_lin); + uint16_t cnt_wrap = TU_MIN(wNBytes - cnt_lin, info.len_wrap); + + // We want to read from PMA and write it into the FIFO, if LIN part is ODD and has WRAPPED part, + // last lin byte will be combined with wrapped part + // To ensure PMA is always access aligned (src aligned to 16 or 32 bit) +#ifdef FSDEV_BUS_32BIT + if ((cnt_lin & 0x03) && cnt_wrap) { + // Copy first linear part + dcd_read_packet_memory(info.ptr_lin, src, cnt_lin & ~0x03); + src += cnt_lin & ~0x03; + + // Copy last linear bytes & first wrapped bytes + uint8_t tmp[4]; + dcd_read_packet_memory(tmp, src, 4); + src += 4; + + uint32_t i; + for (i = 0; i < (cnt_lin & 0x03); i++) { + ((uint8_t *)info.ptr_lin)[(cnt_lin & ~0x03) + i] = tmp[i]; + } + uint32_t wCnt = cnt_wrap; + for (; i < 4 && wCnt > 0; i++, wCnt--) { + *(uint8_t *)info.ptr_wrap = tmp[i]; + info.ptr_wrap = (uint8_t *)info.ptr_wrap + 1; + } + + // Copy rest of wrapped byte + if (wCnt) + dcd_read_packet_memory(info.ptr_wrap, src, wCnt); + } +#else + if ((cnt_lin & 0x01) && cnt_wrap) { + // Copy first linear part + dcd_read_packet_memory(info.ptr_lin, src, cnt_lin & ~0x01); + src += cnt_lin & ~0x01; + + // Copy last linear byte & first wrapped byte + uint8_t tmp[2]; + dcd_read_packet_memory(tmp, src, 2); + src += 2; + + ((uint8_t *)info.ptr_lin)[cnt_lin - 1] = tmp[0]; + ((uint8_t *)info.ptr_wrap)[0] = tmp[1]; + + // Copy rest of wrapped byte + dcd_read_packet_memory(((uint8_t *)info.ptr_wrap) + 1, src, cnt_wrap - 1); + } +#endif + else { + // Copy linear part + dcd_read_packet_memory(info.ptr_lin, src, cnt_lin); + src += cnt_lin; + + if (info.len_wrap) { + // Copy wrapped byte + dcd_read_packet_memory(info.ptr_wrap, src, cnt_wrap); + } + } + + tu_fifo_advance_write_pointer(ff, cnt_lin + cnt_wrap); + + return true; +} + +#endif diff --git a/test-devices/composite-stm32/lib/tinyusb/portable/st/stm32_fsdev/dcd_stm32_fsdev.h b/test-devices/composite-stm32/lib/tinyusb/portable/st/stm32_fsdev/dcd_stm32_fsdev.h new file mode 100644 index 00000000..7992f34a --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/portable/st/stm32_fsdev/dcd_stm32_fsdev.h @@ -0,0 +1,551 @@ +/* + * Copyright(c) 2016 STMicroelectronics + * Copyright(c) N Conrad + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the TinyUSB stack. + */ + +// This file contains source copied from ST's HAL, and thus should have their copyright statement. + +// FSDEV_PMA_SIZE is PMA buffer size in bytes. +// On 512-byte devices, access with a stride of two words (use every other 16-bit address) +// On 1024-byte devices, access with a stride of one word (use every 16-bit address) + +#ifndef PORTABLE_ST_STM32F0_DCD_STM32F0_FSDEV_PVT_ST_H_ +#define PORTABLE_ST_STM32F0_DCD_STM32F0_FSDEV_PVT_ST_H_ + +#if CFG_TUSB_MCU == OPT_MCU_STM32F0 + #include "stm32f0xx.h" + #define FSDEV_PMA_SIZE (1024u) + // F0x2 models are crystal-less + // All have internal D+ pull-up + // 070RB: 2 x 16 bits/word memory LPM Support, BCD Support + // PMA dedicated to USB (no sharing with CAN) + +#elif CFG_TUSB_MCU == OPT_MCU_STM32F1 + #include "stm32f1xx.h" + #define FSDEV_PMA_SIZE (512u) + // NO internal Pull-ups + // *B, and *C: 2 x 16 bits/word + + // F1 names this differently from the rest + #define USB_CNTR_LPMODE USB_CNTR_LP_MODE + +#elif defined(STM32F302xB) || defined(STM32F302xC) || \ + defined(STM32F303xB) || defined(STM32F303xC) || \ + defined(STM32F373xC) + #include "stm32f3xx.h" + #define FSDEV_PMA_SIZE (512u) + // NO internal Pull-ups + // *B, and *C: 1 x 16 bits/word + // PMA dedicated to USB (no sharing with CAN) + +#elif defined(STM32F302x6) || defined(STM32F302x8) || \ + defined(STM32F302xD) || defined(STM32F302xE) || \ + defined(STM32F303xD) || defined(STM32F303xE) + #include "stm32f3xx.h" + #define FSDEV_PMA_SIZE (1024u) + // NO internal Pull-ups + // *6, *8, *D, and *E: 2 x 16 bits/word LPM Support + // When CAN clock is enabled, USB can use first 768 bytes ONLY. + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L0 + #include "stm32l0xx.h" + #define FSDEV_PMA_SIZE (1024u) + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L1 + #include "stm32l1xx.h" + #define FSDEV_PMA_SIZE (512u) + +#elif CFG_TUSB_MCU == OPT_MCU_STM32G4 + #include "stm32g4xx.h" + #define FSDEV_PMA_SIZE (1024u) + +#elif CFG_TUSB_MCU == OPT_MCU_STM32G0 + #include "stm32g0xx.h" + #define FSDEV_BUS_32BIT + #define FSDEV_PMA_SIZE (2048u) + #undef USB_PMAADDR + #define USB_PMAADDR USB_DRD_PMAADDR + #define USB_TypeDef USB_DRD_TypeDef + #define EP0R CHEP0R + #define USB_EP_CTR_RX USB_EP_VTRX + #define USB_EP_CTR_TX USB_EP_VTTX + #define USB_EP_T_FIELD USB_CHEP_UTYPE + #define USB_EPREG_MASK USB_CHEP_REG_MASK + #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK + #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK + #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1 + #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2 + #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1 + #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2 + #define USB_EPRX_STAT USB_CH_RX_VALID + #define USB_EPKIND_MASK USB_EP_KIND_MASK + #define USB USB_DRD_FS + #define USB_CNTR_FRES USB_CNTR_USBRST + #define USB_CNTR_RESUME USB_CNTR_L2RES + #define USB_ISTR_EP_ID USB_ISTR_IDN + #define USB_EPADDR_FIELD USB_CHEP_ADDR + #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY + #define USB_CNTR_FSUSP USB_CNTR_SUSPEN + +#elif CFG_TUSB_MCU == OPT_MCU_STM32H5 + #include "stm32h5xx.h" + #define FSDEV_BUS_32BIT + + #if !defined(USB_DRD_BASE) && defined(USB_DRD_FS_BASE) + #define USB_DRD_BASE USB_DRD_FS_BASE + #endif + + #define FSDEV_PMA_SIZE (2048u) + #undef USB_PMAADDR + #define USB_PMAADDR USB_DRD_PMAADDR + #define USB_TypeDef USB_DRD_TypeDef + #define EP0R CHEP0R + #define USB_EP_CTR_RX USB_EP_VTRX + #define USB_EP_CTR_TX USB_EP_VTTX + #define USB_EP_T_FIELD USB_CHEP_UTYPE + #define USB_EPREG_MASK USB_CHEP_REG_MASK + #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK + #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK + #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1 + #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2 + #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1 + #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2 + #define USB_EPRX_STAT USB_CH_RX_VALID + #define USB_EPKIND_MASK USB_EP_KIND_MASK + #define USB USB_DRD_FS + #define USB_CNTR_FRES USB_CNTR_USBRST + #define USB_CNTR_RESUME USB_CNTR_L2RES + #define USB_ISTR_EP_ID USB_ISTR_IDN + #define USB_EPADDR_FIELD USB_CHEP_ADDR + #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY + #define USB_CNTR_FSUSP USB_CNTR_SUSPEN + +#elif CFG_TUSB_MCU == OPT_MCU_STM32WB + #include "stm32wbxx.h" + #define FSDEV_PMA_SIZE (1024u) + /* ST provided header has incorrect value */ + #undef USB_PMAADDR + #define USB_PMAADDR USB1_PMAADDR + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L4 + #include "stm32l4xx.h" + #define FSDEV_PMA_SIZE (1024u) + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L5 + #include "stm32l5xx.h" + #define FSDEV_PMA_SIZE (1024u) + + #ifndef USB_PMAADDR + #define USB_PMAADDR (USB_BASE + (USB_PMAADDR_NS - USB_BASE_NS)) + #endif + +#else + #error You are using an untested or unimplemented STM32 variant. Please update the driver. + // This includes L1x0, L1x1, L1x2, L4x2 and L4x3, G1x1, G1x3, and G1x4 +#endif + +// For purposes of accessing the packet +#if ((FSDEV_PMA_SIZE) == 512u) + #define FSDEV_PMA_STRIDE (2u) +#elif ((FSDEV_PMA_SIZE) == 1024u) + #define FSDEV_PMA_STRIDE (1u) +#endif + +// The fsdev_bus_t type can be used for both register and PMA access necessities +// For type-safety create a new macro for the volatile address of PMAADDR +// The compiler should warn us if we cast it to a non-volatile type? +#ifdef FSDEV_BUS_32BIT +typedef uint32_t fsdev_bus_t; +static __IO uint32_t * const pma32 = (__IO uint32_t*)USB_PMAADDR; + +#else +typedef uint16_t fsdev_bus_t; +// Volatile is also needed to prevent the optimizer from changing access to 32-bit (as 32-bit access is forbidden) +static __IO uint16_t * const pma = (__IO uint16_t*)USB_PMAADDR; + +TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t * pcd_btable_word_ptr(USB_TypeDef * USBx, size_t x) { + size_t total_word_offset = (((USBx)->BTABLE)>>1) + x; + total_word_offset *= FSDEV_PMA_STRIDE; + return &(pma[total_word_offset]); +} + +TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t* pcd_ep_tx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx) { + return pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 1u); +} + +TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t* pcd_ep_rx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx) { + return pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 3u); +} +#endif + +/* Aligned buffer size according to hardware */ +TU_ATTR_ALWAYS_INLINE static inline uint16_t pcd_aligned_buffer_size(uint16_t size) { + /* The STM32 full speed USB peripheral supports only a limited set of + * buffer sizes given by the RX buffer entry format in the USB_BTABLE. */ + uint16_t blocksize = (size > 62) ? 32 : 2; + + // Round up while dividing requested size by blocksize + uint16_t numblocks = (size + blocksize - 1) / blocksize ; + + return numblocks * blocksize; +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wRegValue) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + __O uint32_t *reg = (__O uint32_t *)(USB_DRD_BASE + bEpIdx*4); + *reg = wRegValue; +#else + __O uint16_t *reg = (__O uint16_t *)((&USBx->EP0R) + bEpIdx*2u); + *reg = (uint16_t)wRegValue; +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + __I uint32_t *reg = (__I uint32_t *)(USB_DRD_BASE + bEpIdx*4); +#else + __I uint16_t *reg = (__I uint16_t *)((&USBx->EP0R) + bEpIdx*2u); +#endif + return *reg; +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_eptype(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wType) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= (uint32_t)USB_EP_T_MASK; + regVal |= wType; + regVal |= USB_EP_CTR_RX | USB_EP_CTR_TX; // These clear on write0, so must set high + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_eptype(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EP_T_FIELD; + return regVal; +} + +/** + * @brief Clears bit CTR_RX / CTR_TX in the endpoint register. + * @param USBx USB peripheral instance register address. + * @param bEpIdx Endpoint Number. + * @retval None + */ +TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_rx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPREG_MASK; + regVal &= ~USB_EP_CTR_RX; + regVal |= USB_EP_CTR_TX; // preserve CTR_TX (clears on writing 0) + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPREG_MASK; + regVal &= ~USB_EP_CTR_TX; + regVal |= USB_EP_CTR_RX; // preserve CTR_RX (clears on writing 0) + pcd_set_endpoint(USBx, bEpIdx,regVal); +} + +/** + * @brief gets counter of the tx buffer. + * @param USBx USB peripheral instance register address. + * @param bEpIdx Endpoint Number. + * @retval Counter value + */ +TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + return (pma32[2*bEpIdx] & 0x03FF0000) >> 16; +#else + __I uint16_t *regPtr = pcd_ep_tx_cnt_ptr(USBx, bEpIdx); + return *regPtr & 0x3ffU; +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + return (pma32[2*bEpIdx + 1] & 0x03FF0000) >> 16; +#else + __I uint16_t *regPtr = pcd_ep_rx_cnt_ptr(USBx, bEpIdx); + return *regPtr & 0x3ffU; +#endif +} + +#define pcd_get_ep_dbuf0_cnt pcd_get_ep_tx_cnt +#define pcd_get_ep_dbuf1_cnt pcd_get_ep_rx_cnt + +/** + * @brief Sets address in an endpoint register. + * @param USBx USB peripheral instance register address. + * @param bEpIdx Endpoint Number. + * @param bAddr Address. + * @retval None + */ +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t bAddr) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPREG_MASK; + regVal |= bAddr; + regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; + pcd_set_endpoint(USBx, bEpIdx,regVal); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + return pma32[2*bEpIdx] & 0x0000FFFFu ; +#else + return *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 0u); +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + return pma32[2*bEpIdx + 1] & 0x0000FFFFu; +#else + return *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 2u); +#endif +} + +#define pcd_get_ep_dbuf0_address pcd_get_ep_tx_address +#define pcd_get_ep_dbuf1_address pcd_get_ep_rx_address + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + pma32[2*bEpIdx] = (pma32[2*bEpIdx] & 0xFFFF0000u) | (addr & 0x0000FFFCu); +#else + *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 0u) = addr; +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & 0xFFFF0000u) | (addr & 0x0000FFFCu); +#else + *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 2u) = addr; +#endif +} + +#define pcd_set_ep_dbuf0_address pcd_set_ep_tx_address +#define pcd_set_ep_dbuf1_address pcd_set_ep_rx_address + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + pma32[2*bEpIdx] = (pma32[2*bEpIdx] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16); +#else + __IO uint16_t * reg = pcd_ep_tx_cnt_ptr(USBx, bEpIdx); + *reg = (uint16_t) (*reg & (uint16_t) ~0x3FFU) | (wCount & 0x3FFU); +#endif +} + +#define pcd_set_ep_tx_dbuf0_cnt pcd_set_ep_tx_cnt + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_dbuf1_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16); +#else + __IO uint16_t * reg = pcd_ep_rx_cnt_ptr(USBx, bEpIdx); + *reg = (uint16_t) (*reg & (uint16_t) ~0x3FFU) | (wCount & 0x3FFU); +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_blsize_num_blocks(USB_TypeDef * USBx, uint32_t rxtx_idx, + uint32_t blocksize, uint32_t numblocks) { + /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */ +#ifdef FSDEV_BUS_32BIT + (void) USBx; + pma32[rxtx_idx] = (pma32[rxtx_idx] & 0x0000FFFFu) | (blocksize << 31) | ((numblocks - blocksize) << 26); +#else + __IO uint16_t *pdwReg = pcd_btable_word_ptr(USBx, rxtx_idx*2u + 1u); + *pdwReg = (blocksize << 15) | ((numblocks - blocksize) << 10); +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_bufsize(USB_TypeDef * USBx, uint32_t rxtx_idx, uint32_t wCount) { + wCount = pcd_aligned_buffer_size(wCount); + + /* We assume that the buffer size is already aligned to hardware requirements. */ + uint16_t blocksize = (wCount > 62) ? 1 : 0; + uint16_t numblocks = wCount / (blocksize ? 32 : 2); + + /* There should be no remainder in the above calculation */ + TU_ASSERT((wCount - (numblocks * (blocksize ? 32 : 2))) == 0, /**/); + + /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */ + pcd_set_ep_blsize_num_blocks(USBx, rxtx_idx, blocksize, numblocks); +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_dbuf0_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) { + pcd_set_ep_bufsize(USBx, 2*bEpIdx, wCount); +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) { + pcd_set_ep_bufsize(USBx, 2*bEpIdx + 1, wCount); +} + +#define pcd_set_ep_rx_dbuf1_cnt pcd_set_ep_rx_cnt + +/** + * @brief sets the status for tx transfer (bits STAT_TX[1:0]). + * @param USBx USB peripheral instance register address. + * @param bEpIdx Endpoint Number. + * @param wState new state + * @retval None + */ +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_status(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wState) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPTX_DTOGMASK; + + /* toggle first bit ? */ + if((USB_EPTX_DTOG1 & (wState))!= 0U) + { + regVal ^= USB_EPTX_DTOG1; + } + /* toggle second bit ? */ + if((USB_EPTX_DTOG2 & ((uint32_t)(wState)))!= 0U) + { + regVal ^= USB_EPTX_DTOG2; + } + + regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +/** + * @brief sets the status for rx transfer (bits STAT_TX[1:0]) + * @param USBx USB peripheral instance register address. + * @param bEpIdx Endpoint Number. + * @param wState new state + * @retval None + */ + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_status(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wState) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPRX_DTOGMASK; + + /* toggle first bit ? */ + if((USB_EPRX_DTOG1 & wState)!= 0U) { + regVal ^= USB_EPRX_DTOG1; + } + /* toggle second bit ? */ + if((USB_EPRX_DTOG2 & wState)!= 0U) { + regVal ^= USB_EPRX_DTOG2; + } + + regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_status(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + return (regVal & USB_EPRX_STAT) >> (12u); +} + + +/** + * @brief Toggles DTOG_RX / DTOG_TX bit in the endpoint register. + * @param USBx USB peripheral instance register address. + * @param bEpIdx Endpoint Number. + * @retval None + */ +TU_ATTR_ALWAYS_INLINE static inline void pcd_rx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPREG_MASK; + regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX|USB_EP_DTOG_RX; + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_tx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPREG_MASK; + regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX|USB_EP_DTOG_TX; + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +/** + * @brief Clears DTOG_RX / DTOG_TX bit in the endpoint register. + * @param USBx USB peripheral instance register address. + * @param bEpIdx Endpoint Number. + * @retval None + */ +TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_rx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + if((regVal & USB_EP_DTOG_RX) != 0) { + pcd_rx_dtog(USBx,bEpIdx); + } +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + if((regVal & USB_EP_DTOG_TX) != 0) { + pcd_tx_dtog(USBx,bEpIdx); + } +} + +/** + * @brief set & clear EP_KIND bit. + * @param USBx USB peripheral instance register address. + * @param bEpIdx Endpoint Number. + * @retval None + */ +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_kind(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal |= USB_EP_KIND; + regVal &= USB_EPREG_MASK; + regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_ep_kind(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPKIND_MASK; + regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +// This checks if the device has "LPM" +#if defined(USB_ISTR_L1REQ) +#define USB_ISTR_L1REQ_FORCED (USB_ISTR_L1REQ) +#else +#define USB_ISTR_L1REQ_FORCED ((uint16_t)0x0000U) +#endif + +#define USB_ISTR_ALL_EVENTS (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_WKUP | USB_ISTR_SUSP | \ + USB_ISTR_RESET | USB_ISTR_SOF | USB_ISTR_ESOF | USB_ISTR_L1REQ_FORCED ) + +// Number of endpoints in hardware +// TODO should use TUP_DCD_ENDPOINT_MAX +#define STFSDEV_EP_COUNT (8u) + +#endif /* PORTABLE_ST_STM32F0_DCD_STM32F0_FSDEV_PVT_ST_H_ */ diff --git a/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dcd_dwc2.c b/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dcd_dwc2.c new file mode 100644 index 00000000..692096fc --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dcd_dwc2.c @@ -0,0 +1,1198 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 William D. Jones + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * Copyright (c) 2020 Jan Duempelmann + * Copyright (c) 2020 Reinhard Panhuber + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if CFG_TUD_ENABLED && defined(TUP_USBIP_DWC2) + +#include "device/dcd.h" +#include "dwc2_type.h" + +// Following symbols must be defined by port header +// - _dwc2_controller[]: array of controllers +// - DWC2_EP_MAX: largest EP counts of all controllers +// - dwc2_phy_init/dwc2_phy_update: phy init called before and after core reset +// - dwc2_dcd_int_enable/dwc2_dcd_int_disable +// - dwc2_remote_wakeup_delay + +#if defined(TUP_USBIP_DWC2_STM32) + #include "dwc2_stm32.h" +#elif TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) + #include "dwc2_esp32.h" +#elif TU_CHECK_MCU(OPT_MCU_GD32VF103) + #include "dwc2_gd32.h" +#elif TU_CHECK_MCU(OPT_MCU_BCM2711, OPT_MCU_BCM2835, OPT_MCU_BCM2837) + #include "dwc2_bcm.h" +#elif TU_CHECK_MCU(OPT_MCU_EFM32GG) + #include "dwc2_efm32.h" +#elif TU_CHECK_MCU(OPT_MCU_XMC4000) + #include "dwc2_xmc.h" +#else + #error "Unsupported MCUs" +#endif + +//--------------------------------------------------------------------+ +// MACRO TYPEDEF CONSTANT ENUM +//--------------------------------------------------------------------+ + +// DWC2 registers +#define DWC2_REG(_port) ((dwc2_regs_t*) _dwc2_controller[_port].reg_base) + +// Debug level for DWC2 +#define DWC2_DEBUG 2 + +#ifndef dcache_clean +#define dcache_clean(_addr, _size) +#endif + +#ifndef dcache_invalidate +#define dcache_invalidate(_addr, _size) +#endif + +#ifndef dcache_clean_invalidate +#define dcache_clean_invalidate(_addr, _size) +#endif + +static TU_ATTR_ALIGNED(4) uint32_t _setup_packet[2]; + +typedef struct { + uint8_t* buffer; + tu_fifo_t* ff; + uint16_t total_len; + uint16_t max_size; + uint8_t interval; +} xfer_ctl_t; + +static xfer_ctl_t xfer_status[DWC2_EP_MAX][2]; +#define XFER_CTL_BASE(_ep, _dir) (&xfer_status[_ep][_dir]) + +// EP0 transfers are limited to 1 packet - larger sizes has to be split +static uint16_t ep0_pending[2]; // Index determines direction as tusb_dir_t type + +// TX FIFO RAM allocation so far in words - RX FIFO size is readily available from dwc2->grxfsiz +static uint16_t _allocated_fifo_words_tx; // TX FIFO size in words (IN EPs) + +// SOF enabling flag - required for SOF to not get disabled in ISR when SOF was enabled by +static bool _sof_en; + +// Calculate the RX FIFO size according to minimum recommendations from reference manual +// RxFIFO = (5 * number of control endpoints + 8) + +// ((largest USB packet used / 4) + 1 for status information) + +// (2 * number of OUT endpoints) + 1 for Global NAK +// with number of control endpoints = 1 we have +// RxFIFO = 15 + (largest USB packet used / 4) + 2 * number of OUT endpoints +// we double the largest USB packet size to be able to hold up to 2 packets +static inline uint16_t calc_grxfsiz(uint16_t max_ep_size, uint8_t ep_count) { + return 15 + 2 * (max_ep_size / 4) + 2 * ep_count; +} + +TU_ATTR_ALWAYS_INLINE static inline void fifo_flush_tx(dwc2_regs_t* dwc2, uint8_t epnum) { + // flush TX fifo and wait for it cleared + dwc2->grstctl = GRSTCTL_TXFFLSH | (epnum << GRSTCTL_TXFNUM_Pos); + while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {} +} +TU_ATTR_ALWAYS_INLINE static inline void fifo_flush_rx(dwc2_regs_t* dwc2) { + // flush RX fifo and wait for it cleared + dwc2->grstctl = GRSTCTL_RXFFLSH; + while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {} +} + +static bool fifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + uint8_t const ep_count = _dwc2_controller[rhport].ep_count; + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + TU_ASSERT(epnum < ep_count); + + uint16_t fifo_size = tu_div_ceil(packet_size, 4); + + // "USB Data FIFOs" section in reference manual + // Peripheral FIFO architecture + // + // --------------- 320 or 1024 ( 1280 or 4096 bytes ) + // | IN FIFO 0 | + // --------------- (320 or 1024) - 16 + // | IN FIFO 1 | + // --------------- (320 or 1024) - 16 - x + // | . . . . | + // --------------- (320 or 1024) - 16 - x - y - ... - z + // | IN FIFO MAX | + // --------------- + // | FREE | + // --------------- GRXFSIZ + // | OUT FIFO | + // | ( Shared ) | + // --------------- 0 + // + // In FIFO is allocated by following rules: + // - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n". + if (dir == TUSB_DIR_OUT) { + // Calculate required size of RX FIFO + uint16_t const sz = calc_grxfsiz(4 * fifo_size, ep_count); + + // If size_rx needs to be extended check if possible and if so enlarge it + if (dwc2->grxfsiz < sz) { + TU_ASSERT(sz + _allocated_fifo_words_tx <= _dwc2_controller[rhport].ep_fifo_size / 4); + + // Enlarge RX FIFO + dwc2->grxfsiz = sz; + } + } else { + // Note if The TXFELVL is configured as half empty. In order + // to be able to write a packet at that point, the fifo must be twice the max_size. + if ((dwc2->gahbcfg & GAHBCFG_TXFELVL) == 0) { + fifo_size *= 2; + } + + // Check if free space is available + TU_ASSERT(_allocated_fifo_words_tx + fifo_size + dwc2->grxfsiz <= _dwc2_controller[rhport].ep_fifo_size / 4); + _allocated_fifo_words_tx += fifo_size; + TU_LOG(DWC2_DEBUG, " Allocated %u bytes at offset %" PRIu32, fifo_size * 4, + _dwc2_controller[rhport].ep_fifo_size - _allocated_fifo_words_tx * 4); + + // DIEPTXF starts at FIFO #1. + // Both TXFD and TXSA are in unit of 32-bit words. + dwc2->dieptxf[epnum - 1] = (fifo_size << DIEPTXF_INEPTXFD_Pos) | + (_dwc2_controller[rhport].ep_fifo_size / 4 - _allocated_fifo_words_tx); + } + + return true; +} + +static void edpt_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress); + uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); + + xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir); + xfer->max_size = tu_edpt_packet_size(p_endpoint_desc); + xfer->interval = p_endpoint_desc->bInterval; + + // USBAEP, EPTYP, SD0PID_SEVNFRM, MPSIZ are the same for IN and OUT endpoints. + uint32_t const dxepctl = (1 << DOEPCTL_USBAEP_Pos) | + (p_endpoint_desc->bmAttributes.xfer << DOEPCTL_EPTYP_Pos) | + (p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? DOEPCTL_SD0PID_SEVNFRM : 0) | + (xfer->max_size << DOEPCTL_MPSIZ_Pos); + + if (dir == TUSB_DIR_OUT) { + dwc2->epout[epnum].doepctl = dxepctl; + dwc2->daintmsk |= TU_BIT(DAINTMSK_OEPM_Pos + epnum); + } else { + dwc2->epin[epnum].diepctl = dxepctl | (epnum << DIEPCTL_TXFNUM_Pos); + dwc2->daintmsk |= (1 << (DAINTMSK_IEPM_Pos + epnum)); + } +} + +static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) { + (void) rhport; + + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + if (dir == TUSB_DIR_IN) { + dwc2_epin_t* epin = dwc2->epin; + + // Only disable currently enabled non-control endpoint + if ((epnum == 0) || !(epin[epnum].diepctl & DIEPCTL_EPENA)) { + epin[epnum].diepctl |= DIEPCTL_SNAK | (stall ? DIEPCTL_STALL : 0); + } else { + // Stop transmitting packets and NAK IN xfers. + epin[epnum].diepctl |= DIEPCTL_SNAK; + while ((epin[epnum].diepint & DIEPINT_INEPNE) == 0) {} + + // Disable the endpoint. + epin[epnum].diepctl |= DIEPCTL_EPDIS | (stall ? DIEPCTL_STALL : 0); + while ((epin[epnum].diepint & DIEPINT_EPDISD_Msk) == 0) {} + + epin[epnum].diepint = DIEPINT_EPDISD; + } + + // Flush the FIFO, and wait until we have confirmed it cleared. + fifo_flush_tx(dwc2, epnum); + } else { + dwc2_epout_t* epout = dwc2->epout; + + // Only disable currently enabled non-control endpoint + if ((epnum == 0) || !(epout[epnum].doepctl & DOEPCTL_EPENA)) { + epout[epnum].doepctl |= stall ? DOEPCTL_STALL : 0; + } else { + // Asserting GONAK is required to STALL an OUT endpoint. + // Simpler to use polling here, we don't use the "B"OUTNAKEFF interrupt + // anyway, and it can't be cleared by user code. If this while loop never + // finishes, we have bigger problems than just the stack. + dwc2->dctl |= DCTL_SGONAK; + while ((dwc2->gintsts & GINTSTS_BOUTNAKEFF_Msk) == 0) {} + + // Ditto here- disable the endpoint. + epout[epnum].doepctl |= DOEPCTL_EPDIS | (stall ? DOEPCTL_STALL : 0); + while ((epout[epnum].doepint & DOEPINT_EPDISD_Msk) == 0) {} + + epout[epnum].doepint = DOEPINT_EPDISD; + + // Allow other OUT endpoints to keep receiving. + dwc2->dctl |= DCTL_CGONAK; + } + } +} + +// Start of Bus Reset +static void bus_reset(uint8_t rhport) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + uint8_t const ep_count = _dwc2_controller[rhport].ep_count; + + tu_memclr(xfer_status, sizeof(xfer_status)); + + _sof_en = false; + + // clear device address + dwc2->dcfg &= ~DCFG_DAD_Msk; + + // 1. NAK for all OUT endpoints + for (uint8_t n = 0; n < ep_count; n++) { + dwc2->epout[n].doepctl |= DOEPCTL_SNAK; + } + + // 2. Disable all IN endpoints + for (uint8_t n = 0; n < ep_count; n++) { + if (dwc2->epin[n].diepctl & DIEPCTL_EPENA) { + dwc2->epin[n].diepctl |= DIEPCTL_SNAK | DIEPCTL_EPDIS; + } + } + + fifo_flush_tx(dwc2, 0x10); // all tx fifo + fifo_flush_rx(dwc2); + + // 3. Set up interrupt mask + dwc2->daintmsk = TU_BIT(DAINTMSK_OEPM_Pos) | TU_BIT(DAINTMSK_IEPM_Pos); + dwc2->doepmsk = DOEPMSK_STUPM | DOEPMSK_XFRCM; + dwc2->diepmsk = DIEPMSK_TOM | DIEPMSK_XFRCM; + + // "USB Data FIFOs" section in reference manual + // Peripheral FIFO architecture + // + // The FIFO is split up in a lower part where the RX FIFO is located and an upper part where the TX FIFOs start. + // We do this to allow the RX FIFO to grow dynamically which is possible since the free space is located + // between the RX and TX FIFOs. This is required by ISO OUT EPs which need a bigger FIFO than the standard + // configuration done below. + // + // Dynamically FIFO sizes are of interest only for ISO EPs since all others are usually not opened and closed. + // All EPs other than ISO are opened as soon as the driver starts up i.e. when the host sends a + // configure interface command. Hence, all IN EPs other the ISO will be located at the top. IN ISO EPs are usually + // opened when the host sends an additional command: setInterface. At this point in time + // the ISO EP will be located next to the free space and can change its size. In case more IN EPs change its size + // an additional memory + // + // --------------- 320 or 1024 ( 1280 or 4096 bytes ) + // | IN FIFO 0 | + // --------------- (320 or 1024) - 16 + // | IN FIFO 1 | + // --------------- (320 or 1024) - 16 - x + // | . . . . | + // --------------- (320 or 1024) - 16 - x - y - ... - z + // | IN FIFO MAX | + // --------------- + // | FREE | + // --------------- GRXFSIZ + // | OUT FIFO | + // | ( Shared ) | + // --------------- 0 + // + // According to "FIFO RAM allocation" section in RM, FIFO RAM are allocated as follows (each word 32-bits): + // - Each EP IN needs at least max packet size, 16 words is sufficient for EP0 IN + // + // - All EP OUT shared a unique OUT FIFO which uses + // - 13 for setup packets + control words (up to 3 setup packets). + // - 1 for global NAK (not required/used here). + // - Largest-EPsize / 4 + 1. ( FS: 64 bytes, HS: 512 bytes). Recommended is "2 x (Largest-EPsize/4) + 1" + // - 2 for each used OUT endpoint + // + // Therefore GRXFSIZ = 13 + 1 + 1 + 2 x (Largest-EPsize/4) + 2 x EPOUTnum + // - FullSpeed (64 Bytes ): GRXFSIZ = 15 + 2 x 16 + 2 x ep_count = 47 + 2 x ep_count + // - Highspeed (512 bytes): GRXFSIZ = 15 + 2 x 128 + 2 x ep_count = 271 + 2 x ep_count + // + // NOTE: Largest-EPsize & EPOUTnum is actual used endpoints in configuration. Since DCD has no knowledge + // of the overall picture yet. We will use the worst scenario: largest possible + ep_count + // + // For Isochronous, largest EP size can be 1023/1024 for FS/HS respectively. In addition if multiple ISO + // are enabled at least "2 x (Largest-EPsize/4) + 1" are recommended. Maybe provide a macro for application to + // overwrite this. + + // EP0 out max is 64 + dwc2->grxfsiz = calc_grxfsiz(64, ep_count); + + // Setup the control endpoint 0 + _allocated_fifo_words_tx = 16; + + // Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word ) + dwc2->dieptxf0 = (16 << DIEPTXF0_TX0FD_Pos) | (_dwc2_controller[rhport].ep_fifo_size / 4 - _allocated_fifo_words_tx); + + // Fixed control EP0 size to 64 bytes + dwc2->epin[0].diepctl &= ~(0x03 << DIEPCTL_MPSIZ_Pos); + xfer_status[0][TUSB_DIR_OUT].max_size = 64; + xfer_status[0][TUSB_DIR_IN].max_size = 64; + + dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos); + + dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT; +} + +static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t const dir, uint16_t const num_packets, + uint16_t total_bytes) { + (void) rhport; + + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + + // EP0 is limited to one packet each xfer + // We use multiple transaction of xfer->max_size length to get a whole transfer done + if (epnum == 0) { + xfer_ctl_t* const xfer = XFER_CTL_BASE(epnum, dir); + total_bytes = tu_min16(ep0_pending[dir], xfer->max_size); + ep0_pending[dir] -= total_bytes; + } + + // IN and OUT endpoint xfers are interrupt-driven, we just schedule them here. + if (dir == TUSB_DIR_IN) { + dwc2_epin_t* epin = dwc2->epin; + + // A full IN transfer (multiple packets, possibly) triggers XFRC. + epin[epnum].dieptsiz = (num_packets << DIEPTSIZ_PKTCNT_Pos) | + ((total_bytes << DIEPTSIZ_XFRSIZ_Pos) & DIEPTSIZ_XFRSIZ_Msk); + + epin[epnum].diepctl |= DIEPCTL_EPENA | DIEPCTL_CNAK; + + // For ISO endpoint set correct odd/even bit for next frame. + if ((epin[epnum].diepctl & DIEPCTL_EPTYP) == DIEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1) { + // Take odd/even bit from frame counter. + uint32_t const odd_frame_now = (dwc2->dsts & (1u << DSTS_FNSOF_Pos)); + epin[epnum].diepctl |= (odd_frame_now ? DIEPCTL_SD0PID_SEVNFRM_Msk : DIEPCTL_SODDFRM_Msk); + } + // Enable fifo empty interrupt only if there are something to put in the fifo. + if (total_bytes != 0) { + dwc2->diepempmsk |= (1 << epnum); + } + } else { + dwc2_epout_t* epout = dwc2->epout; + + // A full OUT transfer (multiple packets, possibly) triggers XFRC. + epout[epnum].doeptsiz &= ~(DOEPTSIZ_PKTCNT_Msk | DOEPTSIZ_XFRSIZ); + epout[epnum].doeptsiz |= (num_packets << DOEPTSIZ_PKTCNT_Pos) | + ((total_bytes << DOEPTSIZ_XFRSIZ_Pos) & DOEPTSIZ_XFRSIZ_Msk); + + epout[epnum].doepctl |= DOEPCTL_EPENA | DOEPCTL_CNAK; + if ((epout[epnum].doepctl & DOEPCTL_EPTYP) == DOEPCTL_EPTYP_0 && + XFER_CTL_BASE(epnum, dir)->interval == 1) { + // Take odd/even bit from frame counter. + uint32_t const odd_frame_now = (dwc2->dsts & (1u << DSTS_FNSOF_Pos)); + epout[epnum].doepctl |= (odd_frame_now ? DOEPCTL_SD0PID_SEVNFRM_Msk : DOEPCTL_SODDFRM_Msk); + } + } +} + +/*------------------------------------------------------------------*/ +/* Controller API + *------------------------------------------------------------------*/ +#if CFG_TUSB_DEBUG >= DWC2_DEBUG +void print_dwc2_info(dwc2_regs_t* dwc2) { + // print guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4 + // use dwc2_info.py/md for bit-field value and comparison with other ports + volatile uint32_t const* p = (volatile uint32_t const*) &dwc2->guid; + TU_LOG(DWC2_DEBUG, "guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4\r\n"); + for (size_t i = 0; i < 5; i++) { + TU_LOG(DWC2_DEBUG, "0x%08" PRIX32 ", ", p[i]); + } + TU_LOG(DWC2_DEBUG, "0x%08" PRIX32 "\r\n", p[5]); +} +#endif + +static void reset_core(dwc2_regs_t* dwc2) { + // reset core + dwc2->grstctl |= GRSTCTL_CSRST; + + // wait for reset bit is cleared + // TODO version 4.20a should wait for RESET DONE mask + while (dwc2->grstctl & GRSTCTL_CSRST) {} + + // wait for AHB master IDLE + while (!(dwc2->grstctl & GRSTCTL_AHBIDL)) {} + + // wait for device mode ? +} + +static bool phy_hs_supported(dwc2_regs_t* dwc2) { + (void) dwc2; + +#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) + // note: esp32 incorrect report its hs_phy_type as utmi + return false; +#elif !TUD_OPT_HIGH_SPEED + return false; +#else + return dwc2->ghwcfg2_bm.hs_phy_type != HS_PHY_TYPE_NONE; +#endif +} + +static void phy_fs_init(dwc2_regs_t* dwc2) { + TU_LOG(DWC2_DEBUG, "Fullspeed PHY init\r\n"); + + // Select FS PHY + dwc2->gusbcfg |= GUSBCFG_PHYSEL; + + // MCU specific PHY init before reset + dwc2_phy_init(dwc2, HS_PHY_TYPE_NONE); + + // Reset core after selecting PHY + reset_core(dwc2); + + // USB turnaround time is critical for certification where long cables and 5-Hubs are used. + // So if you need the AHB to run at less than 30 MHz, and if USB turnaround time is not critical, + // these bits can be programmed to a larger value. Default is 5 + dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_TRDT_Msk) | (5u << GUSBCFG_TRDT_Pos); + + // MCU specific PHY update post reset + dwc2_phy_update(dwc2, HS_PHY_TYPE_NONE); + + // set max speed + dwc2->dcfg = (dwc2->dcfg & ~DCFG_DSPD_Msk) | (DCFG_DSPD_FS << DCFG_DSPD_Pos); +} + +static void phy_hs_init(dwc2_regs_t* dwc2) { + uint32_t gusbcfg = dwc2->gusbcfg; + + // De-select FS PHY + gusbcfg &= ~GUSBCFG_PHYSEL; + + if (dwc2->ghwcfg2_bm.hs_phy_type == HS_PHY_TYPE_ULPI) { + TU_LOG(DWC2_DEBUG, "Highspeed ULPI PHY init\r\n"); + + // Select ULPI + gusbcfg |= GUSBCFG_ULPI_UTMI_SEL; + + // ULPI 8-bit interface, single data rate + gusbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL); + + // default internal VBUS Indicator and Drive + gusbcfg &= ~(GUSBCFG_ULPIEVBUSD | GUSBCFG_ULPIEVBUSI); + + // Disable FS/LS ULPI + gusbcfg &= ~(GUSBCFG_ULPIFSLS | GUSBCFG_ULPICSM); + } else { + TU_LOG(DWC2_DEBUG, "Highspeed UTMI+ PHY init\r\n"); + + // Select UTMI+ with 8-bit interface + gusbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16); + + // Set 16-bit interface if supported + if (dwc2->ghwcfg4_bm.utmi_phy_data_width) gusbcfg |= GUSBCFG_PHYIF16; + } + + // Apply config + dwc2->gusbcfg = gusbcfg; + + // mcu specific phy init + dwc2_phy_init(dwc2, dwc2->ghwcfg2_bm.hs_phy_type); + + // Reset core after selecting PHY + reset_core(dwc2); + + // Set turn-around, must after core reset otherwise it will be clear + // - 9 if using 8-bit PHY interface + // - 5 if using 16-bit PHY interface + gusbcfg &= ~GUSBCFG_TRDT_Msk; + gusbcfg |= (dwc2->ghwcfg4_bm.utmi_phy_data_width ? 5u : 9u) << GUSBCFG_TRDT_Pos; + dwc2->gusbcfg = gusbcfg; + + // MCU specific PHY update post reset + dwc2_phy_update(dwc2, dwc2->ghwcfg2_bm.hs_phy_type); + + // Set max speed + uint32_t dcfg = dwc2->dcfg; + dcfg &= ~DCFG_DSPD_Msk; + dcfg |= DCFG_DSPD_HS << DCFG_DSPD_Pos; + + // XCVRDLY: transceiver delay between xcvr_sel and txvalid during device chirp is required + // when using with some PHYs such as USB334x (USB3341, USB3343, USB3346, USB3347) + if (dwc2->ghwcfg2_bm.hs_phy_type == HS_PHY_TYPE_ULPI) dcfg |= DCFG_XCVRDLY; + + dwc2->dcfg = dcfg; +} + +static bool check_dwc2(dwc2_regs_t* dwc2) { +#if CFG_TUSB_DEBUG >= DWC2_DEBUG + print_dwc2_info(dwc2); +#endif + + // For some reasons: GD32VF103 snpsid and all hwcfg register are always zero (skip it) + (void) dwc2; +#if !TU_CHECK_MCU(OPT_MCU_GD32VF103) + uint32_t const gsnpsid = dwc2->gsnpsid & GSNPSID_ID_MASK; + TU_ASSERT(gsnpsid == DWC2_OTG_ID || gsnpsid == DWC2_FS_IOT_ID || gsnpsid == DWC2_HS_IOT_ID); +#endif + + return true; +} + +void dcd_init(uint8_t rhport) { + // Programming model begins in the last section of the chapter on the USB + // peripheral in each Reference Manual. + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + + // Check Synopsys ID register, failed if controller clock/power is not enabled + if (!check_dwc2(dwc2)) return; + dcd_disconnect(rhport); + + // max number of endpoints & total_fifo_size are: + // hw_cfg2->num_dev_ep, hw_cfg2->total_fifo_size + + if (phy_hs_supported(dwc2)) { + phy_hs_init(dwc2); // Highspeed + } else { + phy_fs_init(dwc2); // core does not support highspeed or hs phy is not present + } + + // Restart PHY clock + dwc2->pcgctl &= ~(PCGCTL_STOPPCLK | PCGCTL_GATEHCLK | PCGCTL_PWRCLMP | PCGCTL_RSTPDWNMODULE); + + /* Set HS/FS Timeout Calibration to 7 (max available value). + * The number of PHY clocks that the application programs in + * this field is added to the high/full speed interpacket timeout + * duration in the core to account for any additional delays + * introduced by the PHY. This can be required, because the delay + * introduced by the PHY in generating the linestate condition + * can vary from one PHY to another. + */ + dwc2->gusbcfg |= (7ul << GUSBCFG_TOCAL_Pos); + + // Force device mode + dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FHMOD) | GUSBCFG_FDMOD; + + // Clear A override, force B Valid + dwc2->gotgctl = (dwc2->gotgctl & ~GOTGCTL_AVALOEN) | GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL; + + // If USB host misbehaves during status portion of control xfer + // (non zero-length packet), send STALL back and discard. + dwc2->dcfg |= DCFG_NZLSOHSK; + + fifo_flush_tx(dwc2, 0x10); // all tx fifo + fifo_flush_rx(dwc2); + + // Clear all interrupts + uint32_t int_mask = dwc2->gintsts; + dwc2->gintsts |= int_mask; + int_mask = dwc2->gotgint; + dwc2->gotgint |= int_mask; + + // Required as part of core initialization. + dwc2->gintmsk = GINTMSK_OTGINT | GINTMSK_RXFLVLM | + GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM; + + // Configure TX FIFO empty level for interrupt. Default is complete empty + dwc2->gahbcfg |= GAHBCFG_TXFELVL; + + // Enable global interrupt + dwc2->gahbcfg |= GAHBCFG_GINT; + + // make sure we are in device mode +// TU_ASSERT(!(dwc2->gintsts & GINTSTS_CMOD), ); + +// TU_LOG_HEX(DWC2_DEBUG, dwc2->gotgctl); +// TU_LOG_HEX(DWC2_DEBUG, dwc2->gusbcfg); +// TU_LOG_HEX(DWC2_DEBUG, dwc2->dcfg); +// TU_LOG_HEX(DWC2_DEBUG, dwc2->gahbcfg); + + dcd_connect(rhport); +} + +void dcd_int_enable(uint8_t rhport) { + dwc2_dcd_int_enable(rhport); +} + +void dcd_int_disable(uint8_t rhport) { + dwc2_dcd_int_disable(rhport); +} + +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + dwc2->dcfg = (dwc2->dcfg & ~DCFG_DAD_Msk) | (dev_addr << DCFG_DAD_Pos); + + // Response with status after changing device address + dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); +} + +void dcd_remote_wakeup(uint8_t rhport) { + (void) rhport; + + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + + // set remote wakeup + dwc2->dctl |= DCTL_RWUSIG; + + // enable SOF to detect bus resume + dwc2->gintsts = GINTSTS_SOF; + dwc2->gintmsk |= GINTMSK_SOFM; + + // Per specs: remote wakeup signal bit must be clear within 1-15ms + dwc2_remote_wakeup_delay(); + + dwc2->dctl &= ~DCTL_RWUSIG; +} + +void dcd_connect(uint8_t rhport) { + (void) rhport; + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + dwc2->dctl &= ~DCTL_SDIS; +} + +void dcd_disconnect(uint8_t rhport) { + (void) rhport; + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + dwc2->dctl |= DCTL_SDIS; +} + +// Be advised: audio, video and possibly other iso-ep classes use dcd_sof_enable() to enable/disable its corresponding ISR on purpose! +void dcd_sof_enable(uint8_t rhport, bool en) { + (void) rhport; + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + + _sof_en = en; + + if (en) { + dwc2->gintsts = GINTSTS_SOF; + dwc2->gintmsk |= GINTMSK_SOFM; + } else { + dwc2->gintmsk &= ~GINTMSK_SOFM; + } +} + +/*------------------------------------------------------------------*/ +/* DCD Endpoint port + *------------------------------------------------------------------*/ + +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) { + TU_ASSERT(fifo_alloc(rhport, desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt))); + edpt_activate(rhport, desc_edpt); + return true; +} + +// Close all non-control endpoints, cancel all pending transfers if any. +void dcd_edpt_close_all(uint8_t rhport) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + uint8_t const ep_count = _dwc2_controller[rhport].ep_count; + + // Disable non-control interrupt + dwc2->daintmsk = (1 << DAINTMSK_OEPM_Pos) | (1 << DAINTMSK_IEPM_Pos); + + for (uint8_t n = 1; n < ep_count; n++) { + // disable OUT endpoint + if (dwc2->epout[n].doepctl & DOEPCTL_EPENA) { + dwc2->epout[n].doepctl |= DOEPCTL_SNAK | DOEPCTL_EPDIS; + } + xfer_status[n][TUSB_DIR_OUT].max_size = 0; + + // disable IN endpoint + if (dwc2->epin[n].diepctl & DIEPCTL_EPENA) { + dwc2->epin[n].diepctl |= DIEPCTL_SNAK | DIEPCTL_EPDIS; + } + xfer_status[n][TUSB_DIR_IN].max_size = 0; + } + + // reset allocated fifo OUT + dwc2->grxfsiz = calc_grxfsiz(64, ep_count); + // reset allocated fifo IN + _allocated_fifo_words_tx = 16; + + fifo_flush_tx(dwc2, 0x10); // all tx fifo + fifo_flush_rx(dwc2); +} + +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + TU_ASSERT(fifo_alloc(rhport, ep_addr, largest_packet_size)); + return true; +} + +bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) { + // Disable EP to clear potential incomplete transfers + edpt_disable(rhport, p_endpoint_desc->bEndpointAddress, false); + + edpt_activate(rhport, p_endpoint_desc); + + return true; +} + +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir); + xfer->buffer = buffer; + xfer->ff = NULL; + xfer->total_len = total_bytes; + + // EP0 can only handle one packet + if (epnum == 0) { + ep0_pending[dir] = total_bytes; + + // Schedule the first transaction for EP0 transfer + edpt_schedule_packets(rhport, epnum, dir, 1, ep0_pending[dir]); + } else { + uint16_t num_packets = (total_bytes / xfer->max_size); + uint16_t const short_packet_size = total_bytes % xfer->max_size; + + // Zero-size packet is special case. + if ((short_packet_size > 0) || (total_bytes == 0)) num_packets++; + + // Schedule packets to be sent within interrupt + edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes); + } + + return true; +} + +// The number of bytes has to be given explicitly to allow more flexible control of how many +// bytes should be written and second to keep the return value free to give back a boolean +// success message. If total_bytes is too big, the FIFO will copy only what is available +// into the USB buffer! +bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t total_bytes) { + // USB buffers always work in bytes so to avoid unnecessary divisions we demand item_size = 1 + TU_ASSERT(ff->item_size == 1); + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir); + xfer->buffer = NULL; + xfer->ff = ff; + xfer->total_len = total_bytes; + + uint16_t num_packets = (total_bytes / xfer->max_size); + uint16_t const short_packet_size = total_bytes % xfer->max_size; + + // Zero-size packet is special case. + if (short_packet_size > 0 || (total_bytes == 0)) num_packets++; + + // Schedule packets to be sent within interrupt + edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes); + + return true; +} + +void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { + edpt_disable(rhport, ep_addr, false); +} + +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { + edpt_disable(rhport, ep_addr, true); +} + +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { + (void) rhport; + + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + // Clear stall and reset data toggle + if (dir == TUSB_DIR_IN) { + dwc2->epin[epnum].diepctl &= ~DIEPCTL_STALL; + dwc2->epin[epnum].diepctl |= DIEPCTL_SD0PID_SEVNFRM; + } else { + dwc2->epout[epnum].doepctl &= ~DOEPCTL_STALL; + dwc2->epout[epnum].doepctl |= DOEPCTL_SD0PID_SEVNFRM; + } +} + +/*------------------------------------------------------------------*/ + +// Read a single data packet from receive FIFO +static void read_fifo_packet(uint8_t rhport, uint8_t* dst, uint16_t len) { + (void) rhport; + + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + volatile const uint32_t* rx_fifo = dwc2->fifo[0]; + + // Reading full available 32 bit words from fifo + uint16_t full_words = len >> 2; + while (full_words--) { + tu_unaligned_write32(dst, *rx_fifo); + dst += 4; + } + + // Read the remaining 1-3 bytes from fifo + uint8_t const bytes_rem = len & 0x03; + if (bytes_rem != 0) { + uint32_t const tmp = *rx_fifo; + dst[0] = tu_u32_byte0(tmp); + if (bytes_rem > 1) dst[1] = tu_u32_byte1(tmp); + if (bytes_rem > 2) dst[2] = tu_u32_byte2(tmp); + } +} + +// Write a single data packet to EPIN FIFO +static void write_fifo_packet(uint8_t rhport, uint8_t fifo_num, uint8_t const* src, uint16_t len) { + (void) rhport; + + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + volatile uint32_t* tx_fifo = dwc2->fifo[fifo_num]; + + // Pushing full available 32 bit words to fifo + uint16_t full_words = len >> 2; + while (full_words--) { + *tx_fifo = tu_unaligned_read32(src); + src += 4; + } + + // Write the remaining 1-3 bytes into fifo + uint8_t const bytes_rem = len & 0x03; + if (bytes_rem) { + uint32_t tmp_word = src[0]; + if (bytes_rem > 1) tmp_word |= (src[1] << 8); + if (bytes_rem > 2) tmp_word |= (src[2] << 16); + + *tx_fifo = tmp_word; + } +} + +static void handle_rxflvl_irq(uint8_t rhport) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + volatile uint32_t const* rx_fifo = dwc2->fifo[0]; + + // Pop control word off FIFO + uint32_t const ctl_word = dwc2->grxstsp; + uint8_t const pktsts = (ctl_word & GRXSTSP_PKTSTS_Msk) >> GRXSTSP_PKTSTS_Pos; + uint8_t const epnum = (ctl_word & GRXSTSP_EPNUM_Msk) >> GRXSTSP_EPNUM_Pos; + uint16_t const bcnt = (ctl_word & GRXSTSP_BCNT_Msk) >> GRXSTSP_BCNT_Pos; + + dwc2_epout_t* epout = &dwc2->epout[epnum]; + +//#if CFG_TUSB_DEBUG >= DWC2_DEBUG +// const char * pktsts_str[] = +// { +// "ASSERT", "Global NAK (ISR)", "Out Data Received", "Out Transfer Complete (ISR)", +// "Setup Complete (ISR)", "ASSERT", "Setup Data Received" +// }; +// TU_LOG_LOCATION(); +// TU_LOG(DWC2_DEBUG, " EP %02X, Byte Count %u, %s\r\n", epnum, bcnt, pktsts_str[pktsts]); +// TU_LOG(DWC2_DEBUG, " daint = %08lX, doepint = %04X\r\n", (unsigned long) dwc2->daint, (unsigned int) epout->doepint); +//#endif + + switch (pktsts) { + // Global OUT NAK: do nothing + case GRXSTS_PKTSTS_GLOBALOUTNAK: + break; + + case GRXSTS_PKTSTS_SETUPRX: + // Setup packet received + + // We can receive up to three setup packets in succession, but + // only the last one is valid. + _setup_packet[0] = (*rx_fifo); + _setup_packet[1] = (*rx_fifo); + break; + + case GRXSTS_PKTSTS_SETUPDONE: + // Setup packet done (Interrupt) + epout->doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos); + break; + + case GRXSTS_PKTSTS_OUTRX: { + // Out packet received + xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT); + + // Read packet off RxFIFO + if (xfer->ff) { + // Ring buffer + tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void*) (uintptr_t) rx_fifo, bcnt); + } else { + // Linear buffer + read_fifo_packet(rhport, xfer->buffer, bcnt); + + // Increment pointer to xfer data + xfer->buffer += bcnt; + } + + // Truncate transfer length in case of short packet + if (bcnt < xfer->max_size) { + xfer->total_len -= (epout->doeptsiz & DOEPTSIZ_XFRSIZ_Msk) >> DOEPTSIZ_XFRSIZ_Pos; + if (epnum == 0) { + xfer->total_len -= ep0_pending[TUSB_DIR_OUT]; + ep0_pending[TUSB_DIR_OUT] = 0; + } + } + } + break; + + // Out packet done (Interrupt) + case GRXSTS_PKTSTS_OUTDONE: + // Occurred on STM32L47 with dwc2 version 3.10a but not found on other version like 2.80a or 3.30a + // May (or not) be 3.10a specific feature/bug or depending on MCU configuration + // XFRC complete is additionally generated when + // - setup packet is received + // - complete the data stage of control write is complete + if ((epnum == 0) && (bcnt == 0) && (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a)) { + uint32_t doepint = epout->doepint; + + if (doepint & (DOEPINT_STPKTRX | DOEPINT_OTEPSPR)) { + // skip this "no-data" transfer complete event + // Note: STPKTRX will be clear later by setup received handler + uint32_t clear_flags = DOEPINT_XFRC; + + if (doepint & DOEPINT_OTEPSPR) clear_flags |= DOEPINT_OTEPSPR; + + epout->doepint = clear_flags; + + // TU_LOG(DWC2_DEBUG, " FIX extra transfer complete on setup/data compete\r\n"); + } + } + break; + + default: // Invalid + TU_BREAKPOINT(); + break; + } +} + +static void handle_epout_irq(uint8_t rhport) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + uint8_t const ep_count = _dwc2_controller[rhport].ep_count; + + // DAINT for a given EP clears when DOEPINTx is cleared. + // OEPINT will be cleared when DAINT's out bits are cleared. + for (uint8_t n = 0; n < ep_count; n++) { + if (dwc2->daint & TU_BIT(DAINT_OEPINT_Pos + n)) { + dwc2_epout_t* epout = &dwc2->epout[n]; + + uint32_t const doepint = epout->doepint; + + // SETUP packet Setup Phase done. + if (doepint & DOEPINT_STUP) { + uint32_t clear_flag = DOEPINT_STUP; + + // STPKTRX is only available for version from 3_00a + if ((doepint & DOEPINT_STPKTRX) && (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a)) { + clear_flag |= DOEPINT_STPKTRX; + } + + epout->doepint = clear_flag; + dcd_event_setup_received(rhport, (uint8_t*) _setup_packet, true); + } + + // OUT XFER complete + if (epout->doepint & DOEPINT_XFRC) { + epout->doepint = DOEPINT_XFRC; + + xfer_ctl_t* xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT); + + // EP0 can only handle one packet + if ((n == 0) && ep0_pending[TUSB_DIR_OUT]) { + // Schedule another packet to be received. + edpt_schedule_packets(rhport, n, TUSB_DIR_OUT, 1, ep0_pending[TUSB_DIR_OUT]); + } else { + dcd_event_xfer_complete(rhport, n, xfer->total_len, XFER_RESULT_SUCCESS, true); + } + } + } + } +} + +static void handle_epin_irq(uint8_t rhport) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + uint8_t const ep_count = _dwc2_controller[rhport].ep_count; + dwc2_epin_t* epin = dwc2->epin; + + // DAINT for a given EP clears when DIEPINTx is cleared. + // IEPINT will be cleared when DAINT's out bits are cleared. + for (uint8_t n = 0; n < ep_count; n++) { + if (dwc2->daint & TU_BIT(DAINT_IEPINT_Pos + n)) { + // IN XFER complete (entire xfer). + xfer_ctl_t* xfer = XFER_CTL_BASE(n, TUSB_DIR_IN); + + if (epin[n].diepint & DIEPINT_XFRC) { + epin[n].diepint = DIEPINT_XFRC; + + // EP0 can only handle one packet + if ((n == 0) && ep0_pending[TUSB_DIR_IN]) { + // Schedule another packet to be transmitted. + edpt_schedule_packets(rhport, n, TUSB_DIR_IN, 1, ep0_pending[TUSB_DIR_IN]); + } else { + dcd_event_xfer_complete(rhport, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true); + } + } + + // XFER FIFO empty + if ((epin[n].diepint & DIEPINT_TXFE) && (dwc2->diepempmsk & (1 << n))) { + // diepint's TXFE bit is read-only, software cannot clear it. + // It will only be cleared by hardware when written bytes is more than + // - 64 bytes or + // - Half of TX FIFO size (configured by DIEPTXF) + + uint16_t remaining_packets = (epin[n].dieptsiz & DIEPTSIZ_PKTCNT_Msk) >> DIEPTSIZ_PKTCNT_Pos; + + // Process every single packet (only whole packets can be written to fifo) + for (uint16_t i = 0; i < remaining_packets; i++) { + uint16_t const remaining_bytes = (epin[n].dieptsiz & DIEPTSIZ_XFRSIZ_Msk) >> DIEPTSIZ_XFRSIZ_Pos; + + // Packet can not be larger than ep max size + uint16_t const packet_size = tu_min16(remaining_bytes, xfer->max_size); + + // It's only possible to write full packets into FIFO. Therefore DTXFSTS register of current + // EP has to be checked if the buffer can take another WHOLE packet + if (packet_size > ((epin[n].dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) break; + + // Push packet to Tx-FIFO + if (xfer->ff) { + volatile uint32_t* tx_fifo = dwc2->fifo[n]; + tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*) (uintptr_t) tx_fifo, packet_size); + } else { + write_fifo_packet(rhport, n, xfer->buffer, packet_size); + + // Increment pointer to xfer data + xfer->buffer += packet_size; + } + } + + // Turn off TXFE if all bytes are written. + if (((epin[n].dieptsiz & DIEPTSIZ_XFRSIZ_Msk) >> DIEPTSIZ_XFRSIZ_Pos) == 0) { + dwc2->diepempmsk &= ~(1 << n); + } + } + } + } +} + +void dcd_int_handler(uint8_t rhport) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + + uint32_t const int_mask = dwc2->gintmsk; + uint32_t const int_status = dwc2->gintsts & int_mask; + + if (int_status & GINTSTS_USBRST) { + // USBRST is start of reset. + dwc2->gintsts = GINTSTS_USBRST; + bus_reset(rhport); + } + + if (int_status & GINTSTS_ENUMDNE) { + // ENUMDNE is the end of reset where speed of the link is detected + dwc2->gintsts = GINTSTS_ENUMDNE; + + tusb_speed_t speed; + switch ((dwc2->dsts & DSTS_ENUMSPD_Msk) >> DSTS_ENUMSPD_Pos) { + case DSTS_ENUMSPD_HS: + speed = TUSB_SPEED_HIGH; + break; + + case DSTS_ENUMSPD_LS: + speed = TUSB_SPEED_LOW; + break; + + case DSTS_ENUMSPD_FS_HSPHY: + case DSTS_ENUMSPD_FS: + default: + speed = TUSB_SPEED_FULL; + break; + } + + // TODO must update GUSBCFG_TRDT according to link speed + + dcd_event_bus_reset(rhport, speed, true); + } + + if (int_status & GINTSTS_USBSUSP) { + dwc2->gintsts = GINTSTS_USBSUSP; + dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); + } + + if (int_status & GINTSTS_WKUINT) { + dwc2->gintsts = GINTSTS_WKUINT; + dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); + } + + // TODO check GINTSTS_DISCINT for disconnect detection + // if(int_status & GINTSTS_DISCINT) + + if (int_status & GINTSTS_OTGINT) { + // OTG INT bit is read-only + uint32_t const otg_int = dwc2->gotgint; + + if (otg_int & GOTGINT_SEDET) { + dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true); + } + + dwc2->gotgint = otg_int; + } + + if(int_status & GINTSTS_SOF) { + dwc2->gintsts = GINTSTS_SOF; + const uint32_t frame = (dwc2->dsts & DSTS_FNSOF) >> DSTS_FNSOF_Pos; + + // Disable SOF interrupt if SOF was not explicitly enabled since SOF was used for remote wakeup detection + if (!_sof_en) { + dwc2->gintmsk &= ~GINTMSK_SOFM; + } + + dcd_event_sof(rhport, frame, true); + } + + // RxFIFO non-empty interrupt handling. + if (int_status & GINTSTS_RXFLVL) { + // RXFLVL bit is read-only + + // Mask out RXFLVL while reading data from FIFO + dwc2->gintmsk &= ~GINTMSK_RXFLVLM; + + // Loop until all available packets were handled + do { + handle_rxflvl_irq(rhport); + } while(dwc2->gintsts & GINTSTS_RXFLVL); + + dwc2->gintmsk |= GINTMSK_RXFLVLM; + } + + // OUT endpoint interrupt handling. + if (int_status & GINTSTS_OEPINT) { + // OEPINT is read-only, clear using DOEPINTn + handle_epout_irq(rhport); + } + + // IN endpoint interrupt handling. + if (int_status & GINTSTS_IEPINT) { + // IEPINT bit read-only, clear using DIEPINTn + handle_epin_irq(rhport); + } + + // // Check for Incomplete isochronous IN transfer + // if(int_status & GINTSTS_IISOIXFR) { + // printf(" IISOIXFR!\r\n"); + //// TU_LOG(DWC2_DEBUG, " IISOIXFR!\r\n"); + // } +} + +#endif diff --git a/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_bcm.h b/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_bcm.h new file mode 100644 index 00000000..732d96ae --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_bcm.h @@ -0,0 +1,89 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021, Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_DWC2_BCM_H_ +#define _TUSB_DWC2_BCM_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include "broadcom/defines.h" +#include "broadcom/interrupts.h" +#include "broadcom/caches.h" + +#define DWC2_EP_MAX 8 + +static const dwc2_controller_t _dwc2_controller[] = +{ + { .reg_base = USB_OTG_GLOBAL_BASE, .irqnum = USB_IRQn, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 4096 } +}; + +#define dcache_clean(_addr, _size) data_clean(_addr, _size) +#define dcache_invalidate(_addr, _size) data_invalidate(_addr, _size) +#define dcache_clean_invalidate(_addr, _size) data_clean_and_invalidate(_addr, _size) + +TU_ATTR_ALWAYS_INLINE +static inline void dwc2_dcd_int_enable(uint8_t rhport) +{ + BP_EnableIRQ(_dwc2_controller[rhport].irqnum); +} + +TU_ATTR_ALWAYS_INLINE +static inline void dwc2_dcd_int_disable (uint8_t rhport) +{ + BP_DisableIRQ(_dwc2_controller[rhport].irqnum); +} + +static inline void dwc2_remote_wakeup_delay(void) +{ + // try to delay for 1 ms + // TODO implement later +} + +// MCU specific PHY init, called BEFORE core reset +static inline void dwc2_phy_init(dwc2_regs_t * dwc2, uint8_t hs_phy_type) +{ + (void) dwc2; + (void) hs_phy_type; + + // nothing to do +} + +// MCU specific PHY update, it is called AFTER init() and core reset +static inline void dwc2_phy_update(dwc2_regs_t * dwc2, uint8_t hs_phy_type) +{ + (void) dwc2; + (void) hs_phy_type; + + // nothing to do +} + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_efm32.h b/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_efm32.h new file mode 100644 index 00000000..0e3570cb --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_efm32.h @@ -0,0 +1,89 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 Rafael Silva (@perigoso) + * Copyright (c) 2021, Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _DWC2_EFM32_H_ +#define _DWC2_EFM32_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include "em_device.h" + +// EFM32 has custom control register before DWC registers +#define DWC2_REG_BASE (USB_BASE + offsetof(USB_TypeDef, GOTGCTL)) +#define DWC2_EP_MAX 7 + +static const dwc2_controller_t _dwc2_controller[] = +{ + { .reg_base = DWC2_REG_BASE, .irqnum = USB_IRQn, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 2048 } +}; + +TU_ATTR_ALWAYS_INLINE +static inline void dwc2_dcd_int_enable(uint8_t rhport) +{ + NVIC_EnableIRQ(_dwc2_controller[rhport].irqnum); +} + +TU_ATTR_ALWAYS_INLINE +static inline void dwc2_dcd_int_disable (uint8_t rhport) +{ + NVIC_DisableIRQ(_dwc2_controller[rhport].irqnum); +} + +static inline void dwc2_remote_wakeup_delay(void) +{ + // try to delay for 1 ms +// uint32_t count = SystemCoreClock / 1000; +// while ( count-- ) __NOP(); +} + +// MCU specific PHY init, called BEFORE core reset +static inline void dwc2_phy_init(dwc2_regs_t * dwc2, uint8_t hs_phy_type) +{ + (void) dwc2; + (void) hs_phy_type; + + // Enable PHY + USB->ROUTE = USB_ROUTE_PHYPEN; +} + +// MCU specific PHY update, it is called AFTER init() and core reset +static inline void dwc2_phy_update(dwc2_regs_t * dwc2, uint8_t hs_phy_type) +{ + (void) dwc2; + (void) hs_phy_type; + + // EFM32 Manual: turn around must be 5 (reset & default value) + // dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_TRDT_Msk) | (5u << GUSBCFG_TRDT_Pos); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_esp32.h b/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_esp32.h new file mode 100644 index 00000000..c50dd66b --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_esp32.h @@ -0,0 +1,96 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021, Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + + +#ifndef _DWC2_ESP32_H_ +#define _DWC2_ESP32_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include "esp_intr_alloc.h" +#include "soc/periph_defs.h" +//#include "soc/usb_periph.h" + +#define DWC2_REG_BASE 0x60080000UL +#define DWC2_EP_MAX 6 // USB_OUT_EP_NUM. TODO ESP32Sx only has 5 tx fifo (5 endpoint IN) + +static const dwc2_controller_t _dwc2_controller[] = +{ + { .reg_base = DWC2_REG_BASE, .irqnum = 0, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 1024 } +}; + +static intr_handle_t usb_ih; + +static void dcd_int_handler_wrap(void* arg) +{ + (void) arg; + dcd_int_handler(0); +} + +TU_ATTR_ALWAYS_INLINE +static inline void dwc2_dcd_int_enable (uint8_t rhport) +{ + (void) rhport; + esp_intr_alloc(ETS_USB_INTR_SOURCE, ESP_INTR_FLAG_LOWMED, dcd_int_handler_wrap, NULL, &usb_ih); +} + +TU_ATTR_ALWAYS_INLINE +static inline void dwc2_dcd_int_disable (uint8_t rhport) +{ + (void) rhport; + esp_intr_free(usb_ih); +} + +static inline void dwc2_remote_wakeup_delay(void) +{ + vTaskDelay(pdMS_TO_TICKS(1)); +} + +// MCU specific PHY init, called BEFORE core reset +static inline void dwc2_phy_init(dwc2_regs_t * dwc2, uint8_t hs_phy_type) +{ + (void) dwc2; + (void) hs_phy_type; + + // nothing to do +} + +// MCU specific PHY update, it is called AFTER init() and core reset +static inline void dwc2_phy_update(dwc2_regs_t * dwc2, uint8_t hs_phy_type) +{ + (void) dwc2; + (void) hs_phy_type; + + // nothing to do +} + +#ifdef __cplusplus +} +#endif + +#endif /* _DWC2_ESP32_H_ */ diff --git a/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_gd32.h b/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_gd32.h new file mode 100644 index 00000000..0375fffe --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_gd32.h @@ -0,0 +1,101 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021, Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + + +#ifndef DWC2_GD32_H_ +#define DWC2_GD32_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#define DWC2_REG_BASE 0x50000000UL +#define DWC2_EP_MAX 4 + +static const dwc2_controller_t _dwc2_controller[] = +{ + { .reg_base = DWC2_REG_BASE, .irqnum = 86, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 1280 } +}; + +extern uint32_t SystemCoreClock; + +// The GD32VF103 is a RISC-V MCU, which implements the ECLIC Core-Local +// Interrupt Controller by Nuclei. It is nearly API compatible to the +// NVIC used by ARM MCUs. +#define ECLIC_INTERRUPT_ENABLE_BASE 0xD2001001UL + +TU_ATTR_ALWAYS_INLINE +static inline void __eclic_enable_interrupt (uint32_t irq) { + *(volatile uint8_t*)(ECLIC_INTERRUPT_ENABLE_BASE + (irq * 4)) = 1; +} + +TU_ATTR_ALWAYS_INLINE +static inline void __eclic_disable_interrupt (uint32_t irq){ + *(volatile uint8_t*)(ECLIC_INTERRUPT_ENABLE_BASE + (irq * 4)) = 0; +} + +TU_ATTR_ALWAYS_INLINE +static inline void dwc2_dcd_int_enable(uint8_t rhport) +{ + __eclic_enable_interrupt(_dwc2_controller[rhport].irqnum); +} + +TU_ATTR_ALWAYS_INLINE +static inline void dwc2_dcd_int_disable (uint8_t rhport) +{ + __eclic_disable_interrupt(_dwc2_controller[rhport].irqnum); +} + +static inline void dwc2_remote_wakeup_delay(void) +{ + // try to delay for 1 ms + uint32_t count = SystemCoreClock / 1000; + while ( count-- ) __asm volatile ("nop"); +} + +// MCU specific PHY init, called BEFORE core reset +static inline void dwc2_phy_init(dwc2_regs_t * dwc2, uint8_t hs_phy_type) +{ + (void) dwc2; + (void) hs_phy_type; + + // nothing to do +} + +// MCU specific PHY update, it is called AFTER init() and core reset +static inline void dwc2_phy_update(dwc2_regs_t * dwc2, uint8_t hs_phy_type) +{ + (void) dwc2; + (void) hs_phy_type; + + // nothing to do +} + +#ifdef __cplusplus +} +#endif + +#endif /* DWC2_GD32_H_ */ diff --git a/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_info.md b/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_info.md new file mode 100644 index 00000000..8690a075 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_info.md @@ -0,0 +1,55 @@ +| | BCM2711 (Pi4) | EFM32GG FullSpeed | ESP32-S2 | STM32F407 Fullspeed | STM32F407 Highspeed | STM32F411 Fullspeed | STM32F412 Fullspeed | STM32F429 Fullspeed | STM32F429 Highspeed | STM32F723 Fullspeed | STM32F723 HighSpeed | STM32F767 Fullspeed | STM32H743 Highspeed | STM32L476 Fullspeed | STM32U5A5 Highspeed | GD32VF103 Fullspeed | XMC4500 | +|:----------------------------|:----------------|:--------------------|:-----------|:----------------------|:----------------------|:----------------------|:----------------------|:----------------------|:----------------------|:----------------------|:----------------------|:----------------------|:----------------------|:----------------------|:----------------------|:----------------------|:-----------| +| guid | 0x2708A000 | 0x00000000 | 0x00000000 | 0x00001200 | 0x00001100 | 0x00001200 | 0x00002000 | 0x00001200 | 0x00001100 | 0x00003000 | 0x00003100 | 0x00002000 | 0x00002300 | 0x00002000 | 0x00005000 | 0x00001000 | 0x00AEC000 | +| gsnpsid | 0x4F54280A | 0x4F54330A | 0x4F54400A | 0x4F54281A | 0x4F54281A | 0x4F54281A | 0x4F54320A | 0x4F54281A | 0x4F54281A | 0x4F54330A | 0x4F54330A | 0x4F54320A | 0x4F54330A | 0x4F54310A | 0x4F54411A | 0x00000000 | 0x4F54292A | +| - specs version | 2.80a | 3.30a | 4.00a | 2.81a | 2.81a | 2.81a | 3.20a | 2.81a | 2.81a | 3.30a | 3.30a | 3.20a | 3.30a | 3.10a | 4.11a | 0.00W | 2.92a | +| ghwcfg1 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | +| ghwcfg2 | 0x228DDD50 | 0x228F5910 | 0x224DD930 | 0x229DCD20 | 0x229ED590 | 0x229DCD20 | 0x229ED520 | 0x229DCD20 | 0x229ED590 | 0x229ED520 | 0x229FE1D0 | 0x229ED520 | 0x229FE190 | 0x229ED520 | 0x228FE052 | 0x00000000 | 0x228F5930 | +| - op_mode | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 2 | 0 | 0 | +| - arch | 2 | 2 | 2 | 0 | 2 | 0 | 0 | 0 | 2 | 0 | 2 | 0 | 2 | 0 | 2 | 0 | 2 | +| - point2point | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | +| - hs_phy_type | 1 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | 2 | 0 | 3 | 0 | 2 | 0 | 1 | 0 | 0 | +| - fs_phy_type | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | +| - num_dev_ep | 7 | 6 | 6 | 3 | 5 | 3 | 5 | 3 | 5 | 5 | 8 | 5 | 8 | 5 | 8 | 0 | 6 | +| - num_host_ch | 7 | 13 | 7 | 7 | 11 | 7 | 11 | 7 | 11 | 11 | 15 | 11 | 15 | 11 | 15 | 0 | 13 | +| - period_channel_support | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | +| - enable_dynamic_fifo | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | +| - mul_cpu_int | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | +| - reserved21 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - nperiod_tx_q_depth | 2 | 2 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 0 | 2 | +| - host_period_tx_q_depth | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 0 | 2 | +| - dev_token_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 | 8 | +| - otg_enable_ic_usb | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| ghwcfg3 | 0x0FF000E8 | 0x01F204E8 | 0x00C804B5 | 0x020001E8 | 0x03F403E8 | 0x020001E8 | 0x0200D1E8 | 0x020001E8 | 0x03F403E8 | 0x0200D1E8 | 0x03EED2E8 | 0x0200D1E8 | 0x03B8D2E8 | 0x0200D1E8 | 0x03B882E8 | 0x00000000 | 0x027A01E5 | +| - xfer_size_width | 8 | 8 | 5 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 | 5 | +| - packet_size_width | 6 | 6 | 3 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 0 | 6 | +| - otg_enable | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | +| - i2c_enable | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | +| - vendor_ctrl_itf | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | +| - optional_feature_removed | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - synch_reset | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - otg_adp_support | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | +| - otg_enable_hsic | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - battery_charger_support | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | +| - lpm_mode | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | +| - total_fifo_size | 4080 | 498 | 200 | 512 | 1012 | 512 | 512 | 512 | 1012 | 512 | 1006 | 512 | 952 | 512 | 952 | 0 | 634 | +| ghwcfg4 | 0x1FF00020 | 0x1BF08030 | 0xD3F0A030 | 0x0FF08030 | 0x17F00030 | 0x0FF08030 | 0x17F08030 | 0x0FF08030 | 0x17F00030 | 0x17F08030 | 0x23F00030 | 0x17F08030 | 0xE3F00030 | 0x17F08030 | 0xE2103E30 | 0x00000000 | 0xDBF08030 | +| - num_dev_period_in_ep | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - power_optimized | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | +| - ahb_freq_min | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | +| - hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - reserved7 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 4 | 0 | 0 | +| - service_interval_mode | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +| - ipg_isoc_en | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +| - acg_enable | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +| - reserved13 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +| - utmi_phy_data_width | 0 | 2 | 2 | 2 | 0 | 2 | 2 | 2 | 0 | 2 | 0 | 2 | 0 | 2 | 0 | 0 | 2 | +| - dev_ctrl_ep_num | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - iddg_filter_enabled | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | +| - vbus_valid_filter_enabled | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | +| - a_valid_filter_enabled | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | +| - b_valid_filter_enabled | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | +| - dedicated_fifos | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | +| - num_dev_in_eps | 15 | 13 | 9 | 7 | 11 | 7 | 11 | 7 | 11 | 11 | 1 | 11 | 1 | 11 | 1 | 0 | 13 | +| - dma_desc_enable | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | +| - dma_dynamic | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | diff --git a/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_info.py b/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_info.py new file mode 100644 index 00000000..55bec3d2 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_info.py @@ -0,0 +1,169 @@ +import click +import ctypes +import pandas as pd + +# hex value for register: guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4 +dwc2_reg_list = ['guid', 'gsnpsid', 'ghwcfg1', 'ghwcfg2', 'ghwcfg3', 'ghwcfg4'] +dwc2_reg_value = { + 'BCM2711 (Pi4)': [0x2708A000, 0x4F54280A, 0, 0x228DDD50, 0xFF000E8, 0x1FF00020], + 'EFM32GG FullSpeed': [0, 0x4F54330A, 0, 0x228F5910, 0x1F204E8, 0x1BF08030], + 'ESP32-S2': [0, 0x4F54400A, 0, 0x224DD930, 0xC804B5, 0xD3F0A030], + 'STM32F407 Fullspeed': [0x1200, 0x4F54281A, 0, 0x229DCD20, 0x20001E8, 0xFF08030], + 'STM32F407 Highspeed': [0x1100, 0x4F54281A, 0, 0x229ED590, 0x3F403E8, 0x17F00030], + 'STM32F411 Fullspeed': [0x1200, 0x4F54281A, 0, 0x229DCD20, 0x20001E8, 0xFF08030], + 'STM32F412 Fullspeed': [0x2000, 0x4F54320A, 0, 0x229ED520, 0x200D1E8, 0x17F08030], + 'STM32F429 Fullspeed': [0x1200, 0x4F54281A, 0, 0x229DCD20, 0x20001E8, 0xFF08030], + 'STM32F429 Highspeed': [0x1100, 0x4F54281A, 0, 0x229ED590, 0x3F403E8, 0x17F00030], + 'STM32F723 Fullspeed': [0x3000, 0x4F54330A, 0, 0x229ED520, 0x200D1E8, 0x17F08030], + 'STM32F723 HighSpeed': [0x3100, 0x4F54330A, 0, 0x229FE1D0, 0x3EED2E8, 0x23F00030], + 'STM32F767 Fullspeed': [0x2000, 0x4F54320A, 0, 0x229ED520, 0x200D1E8, 0x17F08030], + 'STM32H743 Highspeed': [0x2300, 0x4F54330A, 0, 0x229FE190, 0x3B8D2E8, 0xE3F00030], # both HS cores + 'STM32L476 Fullspeed': [0x2000, 0x4F54310A, 0, 0x229ED520, 0x200D1E8, 0x17F08030], + 'STM32U5A5 Highspeed': [0x00005000, 0x4F54411A, 0x00000000, 0x228FE052, 0x03B882E8, 0xE2103E30], + 'GD32VF103 Fullspeed': [0x1000, 0, 0, 0, 0, 0], + 'XMC4500': [0xAEC000, 0x4F54292A, 0, 0x228F5930, 0x27A01E5, 0xDBF08030] +} + +# Combine dwc2_info with dwc2_reg_list +# dwc2_info = { +# 'BCM2711 (Pi4)': { +# 'guid': 0x2708A000, +# 'gsnpsid': 0x4F54280A, +# 'ghwcfg1': 0, +# 'ghwcfg2': 0x228DDD50, +# 'ghwcfg3': 0xFF000E8, +# 'ghwcfg4': 0x1FF00020 +# }, +dwc2_info = {key: {field: value for field, value in zip(dwc2_reg_list, values)} for key, values in dwc2_reg_value.items()} + + +class GHWCFG2(ctypes.LittleEndianStructure): + _fields_ = [ + ("op_mode", ctypes.c_uint32, 3), + ("arch", ctypes.c_uint32, 2), + ("point2point", ctypes.c_uint32, 1), + ("hs_phy_type", ctypes.c_uint32, 2), + ("fs_phy_type", ctypes.c_uint32, 2), + ("num_dev_ep", ctypes.c_uint32, 4), + ("num_host_ch", ctypes.c_uint32, 4), + ("period_channel_support", ctypes.c_uint32, 1), + ("enable_dynamic_fifo", ctypes.c_uint32, 1), + ("mul_cpu_int", ctypes.c_uint32, 1), + ("reserved21", ctypes.c_uint32, 1), + ("nperiod_tx_q_depth", ctypes.c_uint32, 2), + ("host_period_tx_q_depth", ctypes.c_uint32, 2), + ("dev_token_q_depth", ctypes.c_uint32, 5), + ("otg_enable_ic_usb", ctypes.c_uint32, 1) + ] + + +class GHWCFG3(ctypes.LittleEndianStructure): + _fields_ = [ + ("xfer_size_width", ctypes.c_uint32, 4), + ("packet_size_width", ctypes.c_uint32, 3), + ("otg_enable", ctypes.c_uint32, 1), + ("i2c_enable", ctypes.c_uint32, 1), + ("vendor_ctrl_itf", ctypes.c_uint32, 1), + ("optional_feature_removed", ctypes.c_uint32, 1), + ("synch_reset", ctypes.c_uint32, 1), + ("otg_adp_support", ctypes.c_uint32, 1), + ("otg_enable_hsic", ctypes.c_uint32, 1), + ("battery_charger_support", ctypes.c_uint32, 1), + ("lpm_mode", ctypes.c_uint32, 1), + ("total_fifo_size", ctypes.c_uint32, 16) + ] + + +class GHWCFG4(ctypes.LittleEndianStructure): + _fields_ = [ + ("num_dev_period_in_ep", ctypes.c_uint32, 4), + ("power_optimized", ctypes.c_uint32, 1), + ("ahb_freq_min", ctypes.c_uint32, 1), + ("hibernation", ctypes.c_uint32, 1), + ("reserved7", ctypes.c_uint32, 3), + ("service_interval_mode", ctypes.c_uint32, 1), + ("ipg_isoc_en", ctypes.c_uint32, 1), + ("acg_enable", ctypes.c_uint32, 1), + ("reserved13", ctypes.c_uint32, 1), + ("utmi_phy_data_width", ctypes.c_uint32, 2), + ("dev_ctrl_ep_num", ctypes.c_uint32, 4), + ("iddg_filter_enabled", ctypes.c_uint32, 1), + ("vbus_valid_filter_enabled", ctypes.c_uint32, 1), + ("a_valid_filter_enabled", ctypes.c_uint32, 1), + ("b_valid_filter_enabled", ctypes.c_uint32, 1), + ("dedicated_fifos", ctypes.c_uint32, 1), + ("num_dev_in_eps", ctypes.c_uint32, 4), + ("dma_desc_enable", ctypes.c_uint32, 1), + ("dma_dynamic", ctypes.c_uint32, 1) + ] + + +@click.group() +def cli(): + pass + + +@cli.command() +@click.argument('mcus', nargs=-1) +@click.option('-a', '--all', is_flag=True, help='Print all bit-field values') +def info(mcus, all): + """Print DWC2 register values for given MCU(s)""" + if len(mcus) == 0: + mcus = dwc2_info + + for mcu in mcus: + for entry in dwc2_info: + if mcu.lower() in entry.lower(): + print(f"## {entry}") + for r_name, r_value in dwc2_info[entry].items(): + print(f"{r_name} = 0x{r_value:08X}") + # Print bit-field values + if all and r_name.upper() in globals(): + class_name = globals()[r_name.upper()] + ghwcfg = class_name.from_buffer_copy(r_value.to_bytes(4, byteorder='little')) + for field_name, field_type, _ in class_name._fields_: + print(f" {field_name} = {getattr(ghwcfg, field_name)}") + + +@cli.command() +def render_md(): + """Render dwc2_info to Markdown table""" + # Create an empty list to hold the dictionaries + dwc2_info_list = [] + + # Iterate over the dwc2_info dictionary and extract fields + for device, reg_values in dwc2_info.items(): + entry_dict = {"Device": device} + for r_name, r_value in reg_values.items(): + entry_dict[r_name] = f"0x{r_value:08X}" + + if r_name == 'gsnpsid': + # Get dwc2 specs version + major = ((r_value >> 8) >> 4) & 0x0F + minor = (r_value >> 4) & 0xFF + patch = chr((r_value & 0x0F) + ord('a') - 0xA) + entry_dict[f' - specs version'] = f"{major:X}.{minor:02X}{patch}" + elif r_name.upper() in globals(): + # Get bit-field values which exist as ctypes structures + class_name = globals()[r_name.upper()] + ghwcfg = class_name.from_buffer_copy(r_value.to_bytes(4, byteorder='little')) + for field_name, field_type, _ in class_name._fields_: + entry_dict[f' - {field_name}'] = getattr(ghwcfg, field_name) + + dwc2_info_list.append(entry_dict) + + # Create a Pandas DataFrame from the list of dictionaries + df = pd.DataFrame(dwc2_info_list).set_index('Device') + + # Transpose the DataFrame to switch rows and columns + df = df.T + #print(df) + + # Write the Markdown table to a file + with open('dwc2_info.md', 'w') as md_file: + md_file.write(df.to_markdown()) + md_file.write('\n') + + +if __name__ == '__main__': + cli() diff --git a/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_stm32.h b/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_stm32.h new file mode 100644 index 00000000..3237a50f --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_stm32.h @@ -0,0 +1,261 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021, Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef DWC2_STM32_H_ +#define DWC2_STM32_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// EP_MAX : Max number of bi-directional endpoints including EP0 +// EP_FIFO_SIZE : Size of dedicated USB SRAM +#if CFG_TUSB_MCU == OPT_MCU_STM32F1 + #include "stm32f1xx.h" + #define EP_MAX_FS 4 + #define EP_FIFO_SIZE_FS 1280 + +#elif CFG_TUSB_MCU == OPT_MCU_STM32F2 + #include "stm32f2xx.h" + #define EP_MAX_FS USB_OTG_FS_MAX_IN_ENDPOINTS + #define EP_FIFO_SIZE_FS USB_OTG_FS_TOTAL_FIFO_SIZE + + #define EP_MAX_HS USB_OTG_HS_MAX_IN_ENDPOINTS + #define EP_FIFO_SIZE_HS USB_OTG_HS_TOTAL_FIFO_SIZE + +#elif CFG_TUSB_MCU == OPT_MCU_STM32F4 + #include "stm32f4xx.h" + #define EP_MAX_FS USB_OTG_FS_MAX_IN_ENDPOINTS + #define EP_FIFO_SIZE_FS USB_OTG_FS_TOTAL_FIFO_SIZE + + #define EP_MAX_HS USB_OTG_HS_MAX_IN_ENDPOINTS + #define EP_FIFO_SIZE_HS USB_OTG_HS_TOTAL_FIFO_SIZE + +#elif CFG_TUSB_MCU == OPT_MCU_STM32H7 + #include "stm32h7xx.h" + #define EP_MAX_FS 9 + #define EP_FIFO_SIZE_FS 4096 + + #define EP_MAX_HS 9 + #define EP_FIFO_SIZE_HS 4096 + + // NOTE: H7 with only 1 USB port: H72x / H73x / H7Ax / H7Bx + // USB_OTG_FS_PERIPH_BASE and OTG_FS_IRQn not defined + #if (! defined USB2_OTG_FS) + #define USB_OTG_FS_PERIPH_BASE USB1_OTG_HS_PERIPH_BASE + #define OTG_FS_IRQn OTG_HS_IRQn + #endif + +#elif CFG_TUSB_MCU == OPT_MCU_STM32F7 + #include "stm32f7xx.h" + #define EP_MAX_FS 6 + #define EP_FIFO_SIZE_FS 1280 + + #define EP_MAX_HS 9 + #define EP_FIFO_SIZE_HS 4096 + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L4 + #include "stm32l4xx.h" + #define EP_MAX_FS 6 + #define EP_FIFO_SIZE_FS 1280 + +#elif CFG_TUSB_MCU == OPT_MCU_STM32U5 + #include "stm32u5xx.h" + // U59x/5Ax/5Fx/5Gx are highspeed with built-in HS PHY + #ifdef USB_OTG_FS + #define USB_OTG_FS_PERIPH_BASE USB_OTG_FS_BASE + #define EP_MAX_FS 6 + #define EP_FIFO_SIZE_FS 1280 + #else + #define USB_OTG_HS_PERIPH_BASE USB_OTG_HS_BASE + #define EP_MAX_HS 9 + #define EP_FIFO_SIZE_HS 4096 + #endif +#else + #error "Unsupported MCUs" +#endif + +// OTG HS always has higher number of endpoints than FS +#ifdef USB_OTG_HS_PERIPH_BASE + #define DWC2_EP_MAX EP_MAX_HS +#else + #define DWC2_EP_MAX EP_MAX_FS +#endif + +// On STM32 for consistency we associate +// - Port0 to OTG_FS, and Port1 to OTG_HS +static const dwc2_controller_t _dwc2_controller[] = { + #ifdef USB_OTG_FS_PERIPH_BASE + { .reg_base = USB_OTG_FS_PERIPH_BASE, .irqnum = OTG_FS_IRQn, .ep_count = EP_MAX_FS, .ep_fifo_size = EP_FIFO_SIZE_FS }, + #endif + + #ifdef USB_OTG_HS_PERIPH_BASE + { .reg_base = USB_OTG_HS_PERIPH_BASE, .irqnum = OTG_HS_IRQn, .ep_count = EP_MAX_HS, .ep_fifo_size = EP_FIFO_SIZE_HS }, + #endif +}; + +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ + +// SystemCoreClock is already included by family header +// extern uint32_t SystemCoreClock; + +TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_enable(uint8_t rhport) { + NVIC_EnableIRQ((IRQn_Type) _dwc2_controller[rhport].irqnum); +} + +TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_disable(uint8_t rhport) { + NVIC_DisableIRQ((IRQn_Type) _dwc2_controller[rhport].irqnum); +} + +TU_ATTR_ALWAYS_INLINE static inline void dwc2_remote_wakeup_delay(void) { + // try to delay for 1 ms + uint32_t count = SystemCoreClock / 1000; + while (count--) __NOP(); +} + +// MCU specific PHY init, called BEFORE core reset +// - dwc2 3.30a (H5) use USB_HS_PHYC +// - dwc2 4.11a (U5) use femtoPHY +static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) { + if (hs_phy_type == HS_PHY_TYPE_NONE) { + // Enable on-chip FS PHY + dwc2->stm32_gccfg |= STM32_GCCFG_PWRDWN; + + // https://community.st.com/t5/stm32cubemx-mcus/why-stm32h743-usb-fs-doesn-t-work-if-freertos-tickless-idle/m-p/349480#M18867 + // H7 running on full-speed phy need to disable ULPI clock in sleep mode. + // Otherwise, USB won't work when mcu executing WFI/WFE instruction i.e tick-less RTOS. + // Note: there may be other family that is affected by this, but only H7 and F7 is tested so far + #if defined(USB_OTG_FS_PERIPH_BASE) && defined(RCC_AHB1LPENR_USB2OTGFSULPILPEN) + if ( USB_OTG_FS_PERIPH_BASE == (uint32_t) dwc2 ) { + RCC->AHB1LPENR &= ~RCC_AHB1LPENR_USB2OTGFSULPILPEN; + } + #endif + + #if defined(USB_OTG_HS_PERIPH_BASE) && defined(RCC_AHB1LPENR_USB1OTGHSULPILPEN) + if ( USB_OTG_HS_PERIPH_BASE == (uint32_t) dwc2 ) { + RCC->AHB1LPENR &= ~RCC_AHB1LPENR_USB1OTGHSULPILPEN; + } + #endif + + #if defined(USB_OTG_HS_PERIPH_BASE) && defined(RCC_AHB1LPENR_OTGHSULPILPEN) + if ( USB_OTG_HS_PERIPH_BASE == (uint32_t) dwc2 ) { + RCC->AHB1LPENR &= ~RCC_AHB1LPENR_OTGHSULPILPEN; + } + #endif + + } else { +#if CFG_TUSB_MCU != OPT_MCU_STM32U5 + // Disable FS PHY, TODO on U5A5 (dwc2 4.11a) 16th bit is 'Host CDP behavior enable' + dwc2->stm32_gccfg &= ~STM32_GCCFG_PWRDWN; +#endif + + // Enable on-chip HS PHY + if (hs_phy_type == HS_PHY_TYPE_UTMI || hs_phy_type == HS_PHY_TYPE_UTMI_ULPI) { + #ifdef USB_HS_PHYC + // Enable UTMI HS PHY + dwc2->stm32_gccfg |= STM32_GCCFG_PHYHSEN; + + // Enable LDO + USB_HS_PHYC->USB_HS_PHYC_LDO |= USB_HS_PHYC_LDO_ENABLE; + + // Wait until LDO ready + while ( 0 == (USB_HS_PHYC->USB_HS_PHYC_LDO & USB_HS_PHYC_LDO_STATUS) ) {} + + uint32_t phyc_pll = 0; + + // TODO Try to get HSE_VALUE from registers instead of depending CFLAGS + switch ( HSE_VALUE ) + { + case 12000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_12MHZ ; break; + case 12500000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_12_5MHZ ; break; + case 16000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_16MHZ ; break; + case 24000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_24MHZ ; break; + case 25000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_25MHZ ; break; + case 32000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_Msk ; break; // Value not defined in header + default: + TU_ASSERT(false, ); + } + USB_HS_PHYC->USB_HS_PHYC_PLL = phyc_pll; + + // Control the tuning interface of the High Speed PHY + // Use magic value (USB_HS_PHYC_TUNE_VALUE) from ST driver for F7 + USB_HS_PHYC->USB_HS_PHYC_TUNE |= 0x00000F13U; + + // Enable PLL internal PHY + USB_HS_PHYC->USB_HS_PHYC_PLL |= USB_HS_PHYC_PLL_PLLEN; + #else + + #endif + } + } +} + +// MCU specific PHY update, it is called AFTER init() and core reset +static inline void dwc2_phy_update(dwc2_regs_t* dwc2, uint8_t hs_phy_type) { + // used to set turnaround time for fullspeed, nothing to do in highspeed mode + if (hs_phy_type == HS_PHY_TYPE_NONE) { + // Turnaround timeout depends on the AHB clock dictated by STM32 Reference Manual + uint32_t turnaround; + + if (SystemCoreClock >= 32000000u) { + turnaround = 0x6u; + } else if (SystemCoreClock >= 27500000u) { + turnaround = 0x7u; + } else if (SystemCoreClock >= 24000000u) { + turnaround = 0x8u; + } else if (SystemCoreClock >= 21800000u) { + turnaround = 0x9u; + } + else if (SystemCoreClock >= 20000000u) { + turnaround = 0xAu; + } + else if (SystemCoreClock >= 18500000u) { + turnaround = 0xBu; + } + else if (SystemCoreClock >= 17200000u) { + turnaround = 0xCu; + } + else if (SystemCoreClock >= 16000000u) { + turnaround = 0xDu; + } + else if (SystemCoreClock >= 15000000u) { + turnaround = 0xEu; + } + else { + turnaround = 0xFu; + } + + dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_TRDT_Msk) | (turnaround << GUSBCFG_TRDT_Pos); + } +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_type.h b/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_type.h new file mode 100644 index 00000000..c1577123 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_type.h @@ -0,0 +1,1770 @@ +/** + * @author MCD Application Team + * Ha Thach (tinyusb.org) + * + * @attention + * + *

© Copyright (c) 2019 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + */ + +#ifndef _TUSB_DWC2_TYPES_H_ +#define _TUSB_DWC2_TYPES_H_ + +#include "stdint.h" + +#ifdef __cplusplus + extern "C" { +#endif + +// Controller +typedef struct +{ + uintptr_t reg_base; + uint32_t irqnum; + uint8_t ep_count; + uint32_t ep_fifo_size; +}dwc2_controller_t; + +// DWC OTG HW Release versions +#define DWC2_CORE_REV_2_71a 0x4f54271a +#define DWC2_CORE_REV_2_72a 0x4f54272a +#define DWC2_CORE_REV_2_80a 0x4f54280a +#define DWC2_CORE_REV_2_90a 0x4f54290a +#define DWC2_CORE_REV_2_91a 0x4f54291a +#define DWC2_CORE_REV_2_92a 0x4f54292a +#define DWC2_CORE_REV_2_94a 0x4f54294a +#define DWC2_CORE_REV_3_00a 0x4f54300a +#define DWC2_CORE_REV_3_10a 0x4f54310a +#define DWC2_CORE_REV_4_00a 0x4f54400a +#define DWC2_CORE_REV_4_11a 0x4f54411a +#define DWC2_CORE_REV_4_20a 0x4f54420a +#define DWC2_FS_IOT_REV_1_00a 0x5531100a +#define DWC2_HS_IOT_REV_1_00a 0x5532100a +#define DWC2_CORE_REV_MASK 0x0000ffff + +// DWC OTG HW Core ID +#define DWC2_OTG_ID 0x4f540000 +#define DWC2_FS_IOT_ID 0x55310000 +#define DWC2_HS_IOT_ID 0x55320000 + +#if 0 +// HS PHY +typedef struct +{ + volatile uint32_t HS_PHYC_PLL; // 000h This register is used to control the PLL of the HS PHY. + volatile uint32_t Reserved04; // 004h Reserved + volatile uint32_t Reserved08; // 008h Reserved + volatile uint32_t HS_PHYC_TUNE; // 00Ch This register is used to control the tuning interface of the High Speed PHY. + volatile uint32_t Reserved10; // 010h Reserved + volatile uint32_t Reserved14; // 014h Reserved + volatile uint32_t HS_PHYC_LDO; // 018h This register is used to control the regulator (LDO). +} HS_PHYC_GlobalTypeDef; +#endif + +enum { + HS_PHY_TYPE_NONE = 0 , // not supported + HS_PHY_TYPE_UTMI , // internal PHY (mostly) + HS_PHY_TYPE_ULPI , // external PHY + HS_PHY_TYPE_UTMI_ULPI , +}; + +enum { + FS_PHY_TYPE_NONE = 0, // not supported + FS_PHY_TYPE_DEDICATED, + FS_PHY_TYPE_UTMI, + FS_PHY_TYPE_ULPI, +}; + +typedef struct TU_ATTR_PACKED +{ + uint32_t op_mode : 3; // 0: HNP and SRP | 1: SRP | 2: non-HNP, non-SRP + uint32_t arch : 2; // 0: slave-only | 1: External DMA | 2: Internal DMA | 3: others + uint32_t point2point : 1; // 0: support hub and split | 1: no hub, no split + uint32_t hs_phy_type : 2; // 0: not supported | 1: UTMI+ | 2: ULPI | 3: UTMI+ and ULPI + uint32_t fs_phy_type : 2; // 0: not supported | 1: dedicated | 2: UTMI+ | 3: ULPI + uint32_t num_dev_ep : 4; // Number of device endpoints (not including EP0) + uint32_t num_host_ch : 4; // Number of host channel + uint32_t period_channel_support : 1; // Support Periodic OUT Host Channel + uint32_t enable_dynamic_fifo : 1; // Dynamic FIFO Sizing Enabled + uint32_t mul_cpu_int : 1; // Multi-Processor Interrupt Enabled + uint32_t reserved21 : 1; + uint32_t nperiod_tx_q_depth : 2; // Non-periodic request queue depth: 0 = 2. 1 = 4, 2 = 8 + uint32_t host_period_tx_q_depth : 2; // Host periodic request queue depth: 0 = 2. 1 = 4, 2 = 8 + uint32_t dev_token_q_depth : 5; // Device IN token sequence learning queue depth: 0-30 + uint32_t otg_enable_ic_usb : 1; // IC_USB mode specified for mode of operation +} dwc2_ghwcfg2_t; + +TU_VERIFY_STATIC(sizeof(dwc2_ghwcfg2_t) == 4, "incorrect size"); + +typedef struct TU_ATTR_PACKED +{ + uint32_t xfer_size_width : 4; // Transfer size counter in bits = 11 + n (max 19 bits) + uint32_t packet_size_width : 3; // Packet size counter in bits = 4 + n (max 10 bits) + uint32_t otg_enable : 1; // 1 is OTG capable + uint32_t i2c_enable : 1; // I2C interface is available + uint32_t vendor_ctrl_itf : 1; // Vendor control interface is available + uint32_t optional_feature_removed : 1; // remove User ID, GPIO, SOF toggle & counter + uint32_t synch_reset : 1; // 0: async reset | 1: synch reset + uint32_t otg_adp_support : 1; // ADP logic is present along with HSOTG controller + uint32_t otg_enable_hsic : 1; // 1: HSIC-capable with shared UTMI PHY interface | 0: non-HSIC + uint32_t battery_charger_support : 1; // support battery charger + uint32_t lpm_mode : 1; // LPC mode + uint32_t total_fifo_size : 16; // DFIFO depth value in terms of 32-bit words +}dwc2_ghwcfg3_t; + +TU_VERIFY_STATIC(sizeof(dwc2_ghwcfg3_t) == 4, "incorrect size"); + +typedef struct TU_ATTR_PACKED +{ + uint32_t num_dev_period_in_ep : 4; // Number of Device Periodic IN Endpoints + uint32_t power_optimized : 1; // Partial Power Down Enabled + uint32_t ahb_freq_min : 1; // 1: minimum of AHB frequency is less than 60 MHz + uint32_t hibernation : 1; // Hibernation feature is enabled + uint32_t reserved7 : 3; + uint32_t service_interval_mode : 1; // Service Interval supported + uint32_t ipg_isoc_en : 1; // IPG ISOC supported + uint32_t acg_enable : 1; // ACG enabled + uint32_t reserved13 : 1; + uint32_t utmi_phy_data_width : 2; // 0: 8 bits | 1: 16 bits | 2: 8/16 software selectable + uint32_t dev_ctrl_ep_num : 4; // Number of Device control endpoints in addition to EP0 + uint32_t iddg_filter_enabled : 1; + uint32_t vbus_valid_filter_enabled : 1; + uint32_t a_valid_filter_enabled : 1; + uint32_t b_valid_filter_enabled : 1; + uint32_t dedicated_fifos : 1; // Dedicated tx fifo for device IN Endpoint is enabled + uint32_t num_dev_in_eps : 4; // Number of Device IN Endpoints including EP0 + uint32_t dma_desc_enable : 1; // scatter/gather DMA configuration + uint32_t dma_dynamic : 1; // Dynamic scatter/gather DMA +}dwc2_ghwcfg4_t; + +TU_VERIFY_STATIC(sizeof(dwc2_ghwcfg4_t) == 4, "incorrect size"); + +// Host Channel +typedef struct +{ + volatile uint32_t hcchar; // 500 + 20*ch Host Channel Characteristics + volatile uint32_t hcsplt; // 504 + 20*ch Host Channel Split Control + volatile uint32_t hcint; // 508 + 20*ch Host Channel Interrupt + volatile uint32_t hcintmsk; // 50C + 20*ch Host Channel Interrupt Mask + volatile uint32_t hctsiz; // 510 + 20*ch Host Channel Transfer Size + volatile uint32_t hcdma; // 514 + 20*ch Host Channel DMA Address + uint32_t reserved518; // 518 + 20*ch + volatile uint32_t hcdmab; // 51C + 20*ch Host Channel DMA Address +} dwc2_channel_t; + +// Endpoint IN +typedef struct +{ + volatile uint32_t diepctl; // 900 + 20*ep Device IN Endpoint Control + uint32_t reserved04; // 904 + volatile uint32_t diepint; // 908 + 20*ep Device IN Endpoint Interrupt + uint32_t reserved0c; // 90C + volatile uint32_t dieptsiz; // 910 + 20*ep Device IN Endpoint Transfer Size + volatile uint32_t diepdma; // 914 + 20*ep Device IN Endpoint DMA Address + volatile uint32_t dtxfsts; // 918 + 20*ep Device IN Endpoint Tx FIFO Status + uint32_t reserved1c; // 91C +} dwc2_epin_t; + +// Endpoint OUT +typedef struct +{ + volatile uint32_t doepctl; // B00 + 20*ep Device OUT Endpoint Control + uint32_t reserved04; // B04 + volatile uint32_t doepint; // B08 + 20*ep Device OUT Endpoint Interrupt + uint32_t reserved0c; // B0C + volatile uint32_t doeptsiz; // B10 + 20*ep Device OUT Endpoint Transfer Size + volatile uint32_t doepdma; // B14 + 20*ep Device OUT Endpoint DMA Address + uint32_t reserved18[2]; // B18..B1C +} dwc2_epout_t; + +typedef struct +{ + //------------- Core Global -------------// + volatile uint32_t gotgctl; // 000 OTG Control and Status + volatile uint32_t gotgint; // 004 OTG Interrupt + volatile uint32_t gahbcfg; // 008 AHB Configuration + volatile uint32_t gusbcfg; // 00c USB Configuration + volatile uint32_t grstctl; // 010 Reset + volatile uint32_t gintsts; // 014 Interrupt + volatile uint32_t gintmsk; // 018 Interrupt Mask + volatile uint32_t grxstsr; // 01c Receive Status Debug Read + volatile uint32_t grxstsp; // 020 Receive Status Read/Pop + volatile uint32_t grxfsiz; // 024 Receive FIFO Size +union { + volatile uint32_t dieptxf0; // 028 EP0 Tx FIFO Size + volatile uint32_t gnptxfsiz; // 028 Non-periodic Transmit FIFO Size +}; + volatile uint32_t gnptxsts; // 02c Non-periodic Transmit FIFO/Queue Status + volatile uint32_t gi2cctl; // 030 I2C Address + volatile uint32_t gpvndctl; // 034 PHY Vendor Control +union { + volatile uint32_t ggpio; // 038 General Purpose IO + volatile uint32_t stm32_gccfg; // 038 STM32 General Core Configuration +}; + volatile uint32_t guid; // 03C User (Application programmable) ID + volatile uint32_t gsnpsid; // 040 Synopsys ID + Release version + volatile uint32_t ghwcfg1; // 044 User Hardware Configuration1: endpoint dir (2 bit per ep) +union { + volatile uint32_t ghwcfg2; // 048 User Hardware Configuration2 + dwc2_ghwcfg2_t ghwcfg2_bm; +}; +union { + volatile uint32_t ghwcfg3; // 04C User Hardware Configuration3 + dwc2_ghwcfg3_t ghwcfg3_bm; +}; +union { + volatile uint32_t ghwcfg4; // 050 User Hardware Configuration4 + dwc2_ghwcfg4_t ghwcfg4_bm; +}; + volatile uint32_t glpmcfg; // 054 Core LPM Configuration + volatile uint32_t gpwrdn; // 058 Power Down + volatile uint32_t gdfifocfg; // 05C DFIFO Software Configuration + volatile uint32_t gadpctl; // 060 ADP Timer, Control and Status + uint32_t reserved64[39]; // 064..0FF + volatile uint32_t hptxfsiz; // 100 Host Periodic Tx FIFO Size + volatile uint32_t dieptxf[15]; // 104..13C Device Periodic Transmit FIFO Size + uint32_t reserved140[176]; // 140..3FF + + //------------- Host -------------// + volatile uint32_t hcfg; // 400 Host Configuration + volatile uint32_t hfir; // 404 Host Frame Interval + volatile uint32_t hfnum; // 408 Host Frame Number / Frame Remaining + uint32_t reserved40c; // 40C + volatile uint32_t hptxsts; // 410 Host Periodic TX FIFO / Queue Status + volatile uint32_t haint; // 414 Host All Channels Interrupt + volatile uint32_t haintmsk; // 418 Host All Channels Interrupt Mask + volatile uint32_t hflbaddr; // 41C Host Frame List Base Address + uint32_t reserved420[8]; // 420..43F + volatile uint32_t hprt; // 440 Host Port Control and Status + uint32_t reserved444[47]; // 444..4FF + + //------------- Host Channel -------------// + dwc2_channel_t channel[16]; // 500..6FF Host Channels 0-15 + uint32_t reserved700[64]; // 700..7FF + + //------------- Device -------------// + volatile uint32_t dcfg; // 800 Device Configuration + volatile uint32_t dctl; // 804 Device Control + volatile uint32_t dsts; // 808 Device Status (RO) + uint32_t reserved80c; // 80C + volatile uint32_t diepmsk; // 810 Device IN Endpoint Interrupt Mask + volatile uint32_t doepmsk; // 814 Device OUT Endpoint Interrupt Mask + volatile uint32_t daint; // 818 Device All Endpoints Interrupt + volatile uint32_t daintmsk; // 81C Device All Endpoints Interrupt Mask + volatile uint32_t dtknqr1; // 820 Device IN token sequence learning queue read1 + volatile uint32_t dtknqr2; // 824 Device IN token sequence learning queue read2 + volatile uint32_t dvbusdis; // 828 Device VBUS Discharge Time + volatile uint32_t dvbuspulse; // 82C Device VBUS Pulsing Time + volatile uint32_t dthrctl; // 830 Device threshold Control + volatile uint32_t diepempmsk; // 834 Device IN Endpoint FIFO Empty Interrupt Mask + volatile uint32_t deachint; // 838 Device Each Endpoint Interrupt + volatile uint32_t deachmsk; // 83C Device Each Endpoint Interrupt msk + volatile uint32_t diepeachmsk[16]; // 840..87C Device Each IN Endpoint mask + volatile uint32_t doepeachmsk[16]; // 880..8BF Device Each OUT Endpoint mask + uint32_t reserved8c0[16]; // 8C0..8FF + + //------------- Device Endpoint -------------// + dwc2_epin_t epin[16]; // 900..AFF IN Endpoints + dwc2_epout_t epout[16]; // B00..CFF OUT Endpoints + uint32_t reservedd00[64]; // D00..DFF + + //------------- Power Clock -------------// + volatile uint32_t pcgctl; // E00 Power and Clock Gating Control + volatile uint32_t pcgctl1; // E04 + uint32_t reservede08[126]; // E08..FFF + + //------------- FIFOs -------------// + // Word-accessed only using first pointer since it auto shift + volatile uint32_t fifo[16][0x400]; // 1000..FFFF Endpoint FIFO +} dwc2_regs_t; + +TU_VERIFY_STATIC(offsetof(dwc2_regs_t, hcfg ) == 0x0400, "incorrect size"); +TU_VERIFY_STATIC(offsetof(dwc2_regs_t, channel) == 0x0500, "incorrect size"); +TU_VERIFY_STATIC(offsetof(dwc2_regs_t, dcfg ) == 0x0800, "incorrect size"); +TU_VERIFY_STATIC(offsetof(dwc2_regs_t, epin ) == 0x0900, "incorrect size"); +TU_VERIFY_STATIC(offsetof(dwc2_regs_t, epout ) == 0x0B00, "incorrect size"); +TU_VERIFY_STATIC(offsetof(dwc2_regs_t, pcgctl ) == 0x0E00, "incorrect size"); +TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); + +//--------------------------------------------------------------------+ +// Register Bit Definitions +//--------------------------------------------------------------------+ + +/******************** Bit definition for GOTGCTL register ********************/ +#define GOTGCTL_SRQSCS_Pos (0U) +#define GOTGCTL_SRQSCS_Msk (0x1UL << GOTGCTL_SRQSCS_Pos) // 0x00000001 +#define GOTGCTL_SRQSCS GOTGCTL_SRQSCS_Msk // Session request success +#define GOTGCTL_SRQ_Pos (1U) +#define GOTGCTL_SRQ_Msk (0x1UL << GOTGCTL_SRQ_Pos) // 0x00000002 +#define GOTGCTL_SRQ GOTGCTL_SRQ_Msk // Session request +#define GOTGCTL_VBVALOEN_Pos (2U) +#define GOTGCTL_VBVALOEN_Msk (0x1UL << GOTGCTL_VBVALOEN_Pos) // 0x00000004 +#define GOTGCTL_VBVALOEN GOTGCTL_VBVALOEN_Msk // VBUS valid override enable +#define GOTGCTL_VBVALOVAL_Pos (3U) +#define GOTGCTL_VBVALOVAL_Msk (0x1UL << GOTGCTL_VBVALOVAL_Pos) // 0x00000008 +#define GOTGCTL_VBVALOVAL GOTGCTL_VBVALOVAL_Msk // VBUS valid override value +#define GOTGCTL_AVALOEN_Pos (4U) +#define GOTGCTL_AVALOEN_Msk (0x1UL << GOTGCTL_AVALOEN_Pos) // 0x00000010 +#define GOTGCTL_AVALOEN GOTGCTL_AVALOEN_Msk // A-peripheral session valid override enable +#define GOTGCTL_AVALOVAL_Pos (5U) +#define GOTGCTL_AVALOVAL_Msk (0x1UL << GOTGCTL_AVALOVAL_Pos) // 0x00000020 +#define GOTGCTL_AVALOVAL GOTGCTL_AVALOVAL_Msk // A-peripheral session valid override value +#define GOTGCTL_BVALOEN_Pos (6U) +#define GOTGCTL_BVALOEN_Msk (0x1UL << GOTGCTL_BVALOEN_Pos) // 0x00000040 +#define GOTGCTL_BVALOEN GOTGCTL_BVALOEN_Msk // B-peripheral session valid override enable +#define GOTGCTL_BVALOVAL_Pos (7U) +#define GOTGCTL_BVALOVAL_Msk (0x1UL << GOTGCTL_BVALOVAL_Pos) // 0x00000080 +#define GOTGCTL_BVALOVAL GOTGCTL_BVALOVAL_Msk // B-peripheral session valid override value +#define GOTGCTL_HNGSCS_Pos (8U) +#define GOTGCTL_HNGSCS_Msk (0x1UL << GOTGCTL_HNGSCS_Pos) // 0x00000100 +#define GOTGCTL_HNGSCS GOTGCTL_HNGSCS_Msk // Host set HNP enable +#define GOTGCTL_HNPRQ_Pos (9U) +#define GOTGCTL_HNPRQ_Msk (0x1UL << GOTGCTL_HNPRQ_Pos) // 0x00000200 +#define GOTGCTL_HNPRQ GOTGCTL_HNPRQ_Msk // HNP request +#define GOTGCTL_HSHNPEN_Pos (10U) +#define GOTGCTL_HSHNPEN_Msk (0x1UL << GOTGCTL_HSHNPEN_Pos) // 0x00000400 +#define GOTGCTL_HSHNPEN GOTGCTL_HSHNPEN_Msk // Host set HNP enable +#define GOTGCTL_DHNPEN_Pos (11U) +#define GOTGCTL_DHNPEN_Msk (0x1UL << GOTGCTL_DHNPEN_Pos) // 0x00000800 +#define GOTGCTL_DHNPEN GOTGCTL_DHNPEN_Msk // Device HNP enabled +#define GOTGCTL_EHEN_Pos (12U) +#define GOTGCTL_EHEN_Msk (0x1UL << GOTGCTL_EHEN_Pos) // 0x00001000 +#define GOTGCTL_EHEN GOTGCTL_EHEN_Msk // Embedded host enable +#define GOTGCTL_CIDSTS_Pos (16U) +#define GOTGCTL_CIDSTS_Msk (0x1UL << GOTGCTL_CIDSTS_Pos) // 0x00010000 +#define GOTGCTL_CIDSTS GOTGCTL_CIDSTS_Msk // Connector ID status +#define GOTGCTL_DBCT_Pos (17U) +#define GOTGCTL_DBCT_Msk (0x1UL << GOTGCTL_DBCT_Pos) // 0x00020000 +#define GOTGCTL_DBCT GOTGCTL_DBCT_Msk // Long/short debounce time +#define GOTGCTL_ASVLD_Pos (18U) +#define GOTGCTL_ASVLD_Msk (0x1UL << GOTGCTL_ASVLD_Pos) // 0x00040000 +#define GOTGCTL_ASVLD GOTGCTL_ASVLD_Msk // A-session valid +#define GOTGCTL_BSESVLD_Pos (19U) +#define GOTGCTL_BSESVLD_Msk (0x1UL << GOTGCTL_BSESVLD_Pos) // 0x00080000 +#define GOTGCTL_BSESVLD GOTGCTL_BSESVLD_Msk // B-session valid +#define GOTGCTL_OTGVER_Pos (20U) +#define GOTGCTL_OTGVER_Msk (0x1UL << GOTGCTL_OTGVER_Pos) // 0x00100000 +#define GOTGCTL_OTGVER GOTGCTL_OTGVER_Msk // OTG version + +/******************** Bit definition for HCFG register ********************/ +#define HCFG_FSLSPCS_Pos (0U) +#define HCFG_FSLSPCS_Msk (0x3UL << HCFG_FSLSPCS_Pos) // 0x00000003 +#define HCFG_FSLSPCS HCFG_FSLSPCS_Msk // FS/LS PHY clock select +#define HCFG_FSLSPCS_0 (0x1UL << HCFG_FSLSPCS_Pos) // 0x00000001 +#define HCFG_FSLSPCS_1 (0x2UL << HCFG_FSLSPCS_Pos) // 0x00000002 +#define HCFG_FSLSS_Pos (2U) +#define HCFG_FSLSS_Msk (0x1UL << HCFG_FSLSS_Pos) // 0x00000004 +#define HCFG_FSLSS HCFG_FSLSS_Msk // FS- and LS-only support + +/******************** Bit definition for PCGCR register ********************/ +#define PCGCR_STPPCLK_Pos (0U) +#define PCGCR_STPPCLK_Msk (0x1UL << PCGCR_STPPCLK_Pos) // 0x00000001 +#define PCGCR_STPPCLK PCGCR_STPPCLK_Msk // Stop PHY clock +#define PCGCR_GATEHCLK_Pos (1U) +#define PCGCR_GATEHCLK_Msk (0x1UL << PCGCR_GATEHCLK_Pos) // 0x00000002 +#define PCGCR_GATEHCLK PCGCR_GATEHCLK_Msk // Gate HCLK +#define PCGCR_PHYSUSP_Pos (4U) +#define PCGCR_PHYSUSP_Msk (0x1UL << PCGCR_PHYSUSP_Pos) // 0x00000010 +#define PCGCR_PHYSUSP PCGCR_PHYSUSP_Msk // PHY suspended + +/******************** Bit definition for GOTGINT register ********************/ +#define GOTGINT_SEDET_Pos (2U) +#define GOTGINT_SEDET_Msk (0x1UL << GOTGINT_SEDET_Pos) // 0x00000004 +#define GOTGINT_SEDET GOTGINT_SEDET_Msk // Session end detected +#define GOTGINT_SRSSCHG_Pos (8U) +#define GOTGINT_SRSSCHG_Msk (0x1UL << GOTGINT_SRSSCHG_Pos) // 0x00000100 +#define GOTGINT_SRSSCHG GOTGINT_SRSSCHG_Msk // Session request success status change +#define GOTGINT_HNSSCHG_Pos (9U) +#define GOTGINT_HNSSCHG_Msk (0x1UL << GOTGINT_HNSSCHG_Pos) // 0x00000200 +#define GOTGINT_HNSSCHG GOTGINT_HNSSCHG_Msk // Host negotiation success status change +#define GOTGINT_HNGDET_Pos (17U) +#define GOTGINT_HNGDET_Msk (0x1UL << GOTGINT_HNGDET_Pos) // 0x00020000 +#define GOTGINT_HNGDET GOTGINT_HNGDET_Msk // Host negotiation detected +#define GOTGINT_ADTOCHG_Pos (18U) +#define GOTGINT_ADTOCHG_Msk (0x1UL << GOTGINT_ADTOCHG_Pos) // 0x00040000 +#define GOTGINT_ADTOCHG GOTGINT_ADTOCHG_Msk // A-device timeout change +#define GOTGINT_DBCDNE_Pos (19U) +#define GOTGINT_DBCDNE_Msk (0x1UL << GOTGINT_DBCDNE_Pos) // 0x00080000 +#define GOTGINT_DBCDNE GOTGINT_DBCDNE_Msk // Debounce done +#define GOTGINT_IDCHNG_Pos (20U) +#define GOTGINT_IDCHNG_Msk (0x1UL << GOTGINT_IDCHNG_Pos) // 0x00100000 +#define GOTGINT_IDCHNG GOTGINT_IDCHNG_Msk // Change in ID pin input value + +/******************** Bit definition for DCFG register ********************/ +#define DCFG_DSPD_Pos (0U) +#define DCFG_DSPD_Msk (0x3UL << DCFG_DSPD_Pos) // 0x00000003 +#define DCFG_DSPD_HS 0 // Highspeed +#define DCFG_DSPD_FS_HSPHY 1 // Fullspeed on HS PHY +#define DCFG_DSPD_LS 2 // Lowspeed +#define DCFG_DSPD_FS 3 // Fullspeed on FS PHY + +#define DCFG_NZLSOHSK_Pos (2U) +#define DCFG_NZLSOHSK_Msk (0x1UL << DCFG_NZLSOHSK_Pos) // 0x00000004 +#define DCFG_NZLSOHSK DCFG_NZLSOHSK_Msk // Nonzero-length status OUT handshake + +#define DCFG_DAD_Pos (4U) +#define DCFG_DAD_Msk (0x7FUL << DCFG_DAD_Pos) // 0x000007F0 +#define DCFG_DAD DCFG_DAD_Msk // Device address +#define DCFG_DAD_0 (0x01UL << DCFG_DAD_Pos) // 0x00000010 +#define DCFG_DAD_1 (0x02UL << DCFG_DAD_Pos) // 0x00000020 +#define DCFG_DAD_2 (0x04UL << DCFG_DAD_Pos) // 0x00000040 +#define DCFG_DAD_3 (0x08UL << DCFG_DAD_Pos) // 0x00000080 +#define DCFG_DAD_4 (0x10UL << DCFG_DAD_Pos) // 0x00000100 +#define DCFG_DAD_5 (0x20UL << DCFG_DAD_Pos) // 0x00000200 +#define DCFG_DAD_6 (0x40UL << DCFG_DAD_Pos) // 0x00000400 + +#define DCFG_PFIVL_Pos (11U) +#define DCFG_PFIVL_Msk (0x3UL << DCFG_PFIVL_Pos) // 0x00001800 +#define DCFG_PFIVL DCFG_PFIVL_Msk // Periodic (micro)frame interval +#define DCFG_PFIVL_0 (0x1UL << DCFG_PFIVL_Pos) // 0x00000800 +#define DCFG_PFIVL_1 (0x2UL << DCFG_PFIVL_Pos) // 0x00001000 + +#define DCFG_XCVRDLY_Pos (14U) +#define DCFG_XCVRDLY_Msk (0x1UL << DCFG_XCVRDLY_Pos) // 0x00004000 +#define DCFG_XCVRDLY DCFG_XCVRDLY_Msk // Enables delay between xcvr_sel and txvalid during device chirp + +#define DCFG_PERSCHIVL_Pos (24U) +#define DCFG_PERSCHIVL_Msk (0x3UL << DCFG_PERSCHIVL_Pos) // 0x03000000 +#define DCFG_PERSCHIVL DCFG_PERSCHIVL_Msk // Periodic scheduling interval +#define DCFG_PERSCHIVL_0 (0x1UL << DCFG_PERSCHIVL_Pos) // 0x01000000 +#define DCFG_PERSCHIVL_1 (0x2UL << DCFG_PERSCHIVL_Pos) // 0x02000000 + +/******************** Bit definition for DCTL register ********************/ +#define DCTL_RWUSIG_Pos (0U) +#define DCTL_RWUSIG_Msk (0x1UL << DCTL_RWUSIG_Pos) // 0x00000001 +#define DCTL_RWUSIG DCTL_RWUSIG_Msk // Remote wakeup signaling +#define DCTL_SDIS_Pos (1U) +#define DCTL_SDIS_Msk (0x1UL << DCTL_SDIS_Pos) // 0x00000002 +#define DCTL_SDIS DCTL_SDIS_Msk // Soft disconnect +#define DCTL_GINSTS_Pos (2U) +#define DCTL_GINSTS_Msk (0x1UL << DCTL_GINSTS_Pos) // 0x00000004 +#define DCTL_GINSTS DCTL_GINSTS_Msk // Global IN NAK status +#define DCTL_GONSTS_Pos (3U) +#define DCTL_GONSTS_Msk (0x1UL << DCTL_GONSTS_Pos) // 0x00000008 +#define DCTL_GONSTS DCTL_GONSTS_Msk // Global OUT NAK status + +#define DCTL_TCTL_Pos (4U) +#define DCTL_TCTL_Msk (0x7UL << DCTL_TCTL_Pos) // 0x00000070 +#define DCTL_TCTL DCTL_TCTL_Msk // Test control +#define DCTL_TCTL_0 (0x1UL << DCTL_TCTL_Pos) // 0x00000010 +#define DCTL_TCTL_1 (0x2UL << DCTL_TCTL_Pos) // 0x00000020 +#define DCTL_TCTL_2 (0x4UL << DCTL_TCTL_Pos) // 0x00000040 +#define DCTL_SGINAK_Pos (7U) +#define DCTL_SGINAK_Msk (0x1UL << DCTL_SGINAK_Pos) // 0x00000080 +#define DCTL_SGINAK DCTL_SGINAK_Msk // Set global IN NAK +#define DCTL_CGINAK_Pos (8U) +#define DCTL_CGINAK_Msk (0x1UL << DCTL_CGINAK_Pos) // 0x00000100 +#define DCTL_CGINAK DCTL_CGINAK_Msk // Clear global IN NAK +#define DCTL_SGONAK_Pos (9U) +#define DCTL_SGONAK_Msk (0x1UL << DCTL_SGONAK_Pos) // 0x00000200 +#define DCTL_SGONAK DCTL_SGONAK_Msk // Set global OUT NAK +#define DCTL_CGONAK_Pos (10U) +#define DCTL_CGONAK_Msk (0x1UL << DCTL_CGONAK_Pos) // 0x00000400 +#define DCTL_CGONAK DCTL_CGONAK_Msk // Clear global OUT NAK +#define DCTL_POPRGDNE_Pos (11U) +#define DCTL_POPRGDNE_Msk (0x1UL << DCTL_POPRGDNE_Pos) // 0x00000800 +#define DCTL_POPRGDNE DCTL_POPRGDNE_Msk // Power-on programming done + +/******************** Bit definition for HFIR register ********************/ +#define HFIR_FRIVL_Pos (0U) +#define HFIR_FRIVL_Msk (0xFFFFUL << HFIR_FRIVL_Pos) // 0x0000FFFF +#define HFIR_FRIVL HFIR_FRIVL_Msk // Frame interval + +/******************** Bit definition for HFNUM register ********************/ +#define HFNUM_FRNUM_Pos (0U) +#define HFNUM_FRNUM_Msk (0xFFFFUL << HFNUM_FRNUM_Pos) // 0x0000FFFF +#define HFNUM_FRNUM HFNUM_FRNUM_Msk // Frame number +#define HFNUM_FTREM_Pos (16U) +#define HFNUM_FTREM_Msk (0xFFFFUL << HFNUM_FTREM_Pos) // 0xFFFF0000 +#define HFNUM_FTREM HFNUM_FTREM_Msk // Frame time remaining + +/******************** Bit definition for DSTS register ********************/ +#define DSTS_SUSPSTS_Pos (0U) +#define DSTS_SUSPSTS_Msk (0x1UL << DSTS_SUSPSTS_Pos) // 0x00000001 +#define DSTS_SUSPSTS DSTS_SUSPSTS_Msk // Suspend status +#define DSTS_ENUMSPD_Pos (1U) +#define DSTS_ENUMSPD_Msk (0x3UL << DSTS_ENUMSPD_Pos) // 0x00000006 +#define DSTS_ENUMSPD DSTS_ENUMSPD_Msk // Enumerated speed +#define DSTS_ENUMSPD_HS 0 // Highspeed +#define DSTS_ENUMSPD_FS_HSPHY 1 // Fullspeed on HS PHY +#define DSTS_ENUMSPD_LS 2 // Lowspeed +#define DSTS_ENUMSPD_FS 3 // Fullspeed on FS PHY + + +#define DSTS_EERR_Pos (3U) +#define DSTS_EERR_Msk (0x1UL << DSTS_EERR_Pos) // 0x00000008 +#define DSTS_EERR DSTS_EERR_Msk // Erratic error +#define DSTS_FNSOF_Pos (8U) +#define DSTS_FNSOF_Msk (0x3FFFUL << DSTS_FNSOF_Pos) // 0x003FFF00 +#define DSTS_FNSOF DSTS_FNSOF_Msk // Frame number of the received SOF + +/******************** Bit definition for GAHBCFG register ********************/ +#define GAHBCFG_GINT_Pos (0U) +#define GAHBCFG_GINT_Msk (0x1UL << GAHBCFG_GINT_Pos) // 0x00000001 +#define GAHBCFG_GINT GAHBCFG_GINT_Msk // Global interrupt mask +#define GAHBCFG_HBSTLEN_Pos (1U) +#define GAHBCFG_HBSTLEN_Msk (0xFUL << GAHBCFG_HBSTLEN_Pos) // 0x0000001E +#define GAHBCFG_HBSTLEN GAHBCFG_HBSTLEN_Msk // Burst length/type +#define GAHBCFG_HBSTLEN_0 (0x0UL << GAHBCFG_HBSTLEN_Pos) // Single +#define GAHBCFG_HBSTLEN_1 (0x1UL << GAHBCFG_HBSTLEN_Pos) // INCR +#define GAHBCFG_HBSTLEN_2 (0x3UL << GAHBCFG_HBSTLEN_Pos) // INCR4 +#define GAHBCFG_HBSTLEN_3 (0x5UL << GAHBCFG_HBSTLEN_Pos) // INCR8 +#define GAHBCFG_HBSTLEN_4 (0x7UL << GAHBCFG_HBSTLEN_Pos) // INCR16 +#define GAHBCFG_DMAEN_Pos (5U) +#define GAHBCFG_DMAEN_Msk (0x1UL << GAHBCFG_DMAEN_Pos) // 0x00000020 +#define GAHBCFG_DMAEN GAHBCFG_DMAEN_Msk // DMA enable +#define GAHBCFG_TXFELVL_Pos (7U) +#define GAHBCFG_TXFELVL_Msk (0x1UL << GAHBCFG_TXFELVL_Pos) // 0x00000080 +#define GAHBCFG_TXFELVL GAHBCFG_TXFELVL_Msk // TxFIFO empty level +#define GAHBCFG_PTXFELVL_Pos (8U) +#define GAHBCFG_PTXFELVL_Msk (0x1UL << GAHBCFG_PTXFELVL_Pos) // 0x00000100 +#define GAHBCFG_PTXFELVL GAHBCFG_PTXFELVL_Msk // Periodic TxFIFO empty level + +#define GSNPSID_ID_MASK TU_GENMASK(31, 16) + +/******************** Bit definition for GUSBCFG register ********************/ +#define GUSBCFG_TOCAL_Pos (0U) +#define GUSBCFG_TOCAL_Msk (0x7UL << GUSBCFG_TOCAL_Pos) // 0x00000007 +#define GUSBCFG_TOCAL GUSBCFG_TOCAL_Msk // FS timeout calibration +#define GUSBCFG_PHYIF16_Pos (3U) +#define GUSBCFG_PHYIF16_Msk (0x1UL << GUSBCFG_PHYIF16_Pos) // 0x00000008 +#define GUSBCFG_PHYIF16 GUSBCFG_PHYIF16_Msk // PHY Interface (PHYIf) +#define GUSBCFG_ULPI_UTMI_SEL_Pos (4U) +#define GUSBCFG_ULPI_UTMI_SEL_Msk (0x1UL << GUSBCFG_ULPI_UTMI_SEL_Pos) // 0x00000010 +#define GUSBCFG_ULPI_UTMI_SEL GUSBCFG_ULPI_UTMI_SEL_Msk // ULPI or UTMI+ Select (ULPI_UTMI_Sel) +#define GUSBCFG_PHYSEL_Pos (6U) +#define GUSBCFG_PHYSEL_Msk (0x1UL << GUSBCFG_PHYSEL_Pos) // 0x00000040 +#define GUSBCFG_PHYSEL GUSBCFG_PHYSEL_Msk // USB 2.0 high-speed ULPI PHY or USB 1.1 full-speed serial transceiver select +#define GUSBCFG_DDRSEL TU_BIT(7) // Single Data Rate (SDR) or Double Data Rate (DDR) or ULPI interface. +#define GUSBCFG_SRPCAP_Pos (8U) +#define GUSBCFG_SRPCAP_Msk (0x1UL << GUSBCFG_SRPCAP_Pos) // 0x00000100 +#define GUSBCFG_SRPCAP GUSBCFG_SRPCAP_Msk // SRP-capable +#define GUSBCFG_HNPCAP_Pos (9U) +#define GUSBCFG_HNPCAP_Msk (0x1UL << GUSBCFG_HNPCAP_Pos) // 0x00000200 +#define GUSBCFG_HNPCAP GUSBCFG_HNPCAP_Msk // HNP-capable +#define GUSBCFG_TRDT_Pos (10U) +#define GUSBCFG_TRDT_Msk (0xFUL << GUSBCFG_TRDT_Pos) // 0x00003C00 +#define GUSBCFG_TRDT GUSBCFG_TRDT_Msk // USB turnaround time +#define GUSBCFG_PHYLPCS_Pos (15U) +#define GUSBCFG_PHYLPCS_Msk (0x1UL << GUSBCFG_PHYLPCS_Pos) // 0x00008000 +#define GUSBCFG_PHYLPCS GUSBCFG_PHYLPCS_Msk // PHY Low-power clock select +#define GUSBCFG_ULPIFSLS_Pos (17U) +#define GUSBCFG_ULPIFSLS_Msk (0x1UL << GUSBCFG_ULPIFSLS_Pos) // 0x00020000 +#define GUSBCFG_ULPIFSLS GUSBCFG_ULPIFSLS_Msk // ULPI FS/LS select +#define GUSBCFG_ULPIAR_Pos (18U) +#define GUSBCFG_ULPIAR_Msk (0x1UL << GUSBCFG_ULPIAR_Pos) // 0x00040000 +#define GUSBCFG_ULPIAR GUSBCFG_ULPIAR_Msk // ULPI Auto-resume +#define GUSBCFG_ULPICSM_Pos (19U) +#define GUSBCFG_ULPICSM_Msk (0x1UL << GUSBCFG_ULPICSM_Pos) // 0x00080000 +#define GUSBCFG_ULPICSM GUSBCFG_ULPICSM_Msk // ULPI Clock SuspendM +#define GUSBCFG_ULPIEVBUSD_Pos (20U) +#define GUSBCFG_ULPIEVBUSD_Msk (0x1UL << GUSBCFG_ULPIEVBUSD_Pos) // 0x00100000 +#define GUSBCFG_ULPIEVBUSD GUSBCFG_ULPIEVBUSD_Msk // ULPI External VBUS Drive +#define GUSBCFG_ULPIEVBUSI_Pos (21U) +#define GUSBCFG_ULPIEVBUSI_Msk (0x1UL << GUSBCFG_ULPIEVBUSI_Pos) // 0x00200000 +#define GUSBCFG_ULPIEVBUSI GUSBCFG_ULPIEVBUSI_Msk // ULPI external VBUS indicator +#define GUSBCFG_TSDPS_Pos (22U) +#define GUSBCFG_TSDPS_Msk (0x1UL << GUSBCFG_TSDPS_Pos) // 0x00400000 +#define GUSBCFG_TSDPS GUSBCFG_TSDPS_Msk // TermSel DLine pulsing selection +#define GUSBCFG_PCCI_Pos (23U) +#define GUSBCFG_PCCI_Msk (0x1UL << GUSBCFG_PCCI_Pos) // 0x00800000 +#define GUSBCFG_PCCI GUSBCFG_PCCI_Msk // Indicator complement +#define GUSBCFG_PTCI_Pos (24U) +#define GUSBCFG_PTCI_Msk (0x1UL << GUSBCFG_PTCI_Pos) // 0x01000000 +#define GUSBCFG_PTCI GUSBCFG_PTCI_Msk // Indicator pass through +#define GUSBCFG_ULPIIPD_Pos (25U) +#define GUSBCFG_ULPIIPD_Msk (0x1UL << GUSBCFG_ULPIIPD_Pos) // 0x02000000 +#define GUSBCFG_ULPIIPD GUSBCFG_ULPIIPD_Msk // ULPI interface protect disable +#define GUSBCFG_FHMOD_Pos (29U) +#define GUSBCFG_FHMOD_Msk (0x1UL << GUSBCFG_FHMOD_Pos) // 0x20000000 +#define GUSBCFG_FHMOD GUSBCFG_FHMOD_Msk // Forced host mode +#define GUSBCFG_FDMOD_Pos (30U) +#define GUSBCFG_FDMOD_Msk (0x1UL << GUSBCFG_FDMOD_Pos) // 0x40000000 +#define GUSBCFG_FDMOD GUSBCFG_FDMOD_Msk // Forced peripheral mode +#define GUSBCFG_CTXPKT_Pos (31U) +#define GUSBCFG_CTXPKT_Msk (0x1UL << GUSBCFG_CTXPKT_Pos) // 0x80000000 +#define GUSBCFG_CTXPKT GUSBCFG_CTXPKT_Msk // Corrupt Tx packet + +/******************** Bit definition for GRSTCTL register ********************/ +#define GRSTCTL_CSRST_Pos (0U) +#define GRSTCTL_CSRST_Msk (0x1UL << GRSTCTL_CSRST_Pos) // 0x00000001 +#define GRSTCTL_CSRST GRSTCTL_CSRST_Msk // Core soft reset +#define GRSTCTL_HSRST_Pos (1U) +#define GRSTCTL_HSRST_Msk (0x1UL << GRSTCTL_HSRST_Pos) // 0x00000002 +#define GRSTCTL_HSRST GRSTCTL_HSRST_Msk // HCLK soft reset +#define GRSTCTL_FCRST_Pos (2U) +#define GRSTCTL_FCRST_Msk (0x1UL << GRSTCTL_FCRST_Pos) // 0x00000004 +#define GRSTCTL_FCRST GRSTCTL_FCRST_Msk // Host frame counter reset +#define GRSTCTL_RXFFLSH_Pos (4U) +#define GRSTCTL_RXFFLSH_Msk (0x1UL << GRSTCTL_RXFFLSH_Pos) // 0x00000010 +#define GRSTCTL_RXFFLSH GRSTCTL_RXFFLSH_Msk // RxFIFO flush +#define GRSTCTL_TXFFLSH_Pos (5U) +#define GRSTCTL_TXFFLSH_Msk (0x1UL << GRSTCTL_TXFFLSH_Pos) // 0x00000020 +#define GRSTCTL_TXFFLSH GRSTCTL_TXFFLSH_Msk // TxFIFO flush +#define GRSTCTL_TXFNUM_Pos (6U) +#define GRSTCTL_TXFNUM_Msk (0x1FUL << GRSTCTL_TXFNUM_Pos) // 0x000007C0 +#define GRSTCTL_TXFNUM GRSTCTL_TXFNUM_Msk // TxFIFO number +#define GRSTCTL_TXFNUM_0 (0x01UL << GRSTCTL_TXFNUM_Pos) // 0x00000040 +#define GRSTCTL_TXFNUM_1 (0x02UL << GRSTCTL_TXFNUM_Pos) // 0x00000080 +#define GRSTCTL_TXFNUM_2 (0x04UL << GRSTCTL_TXFNUM_Pos) // 0x00000100 +#define GRSTCTL_TXFNUM_3 (0x08UL << GRSTCTL_TXFNUM_Pos) // 0x00000200 +#define GRSTCTL_TXFNUM_4 (0x10UL << GRSTCTL_TXFNUM_Pos) // 0x00000400 +#define GRSTCTL_CSFTRST_DONE_Pos (29) +#define GRSTCTL_CSFTRST_DONE (1u << GRSTCTL_CSFTRST_DONE_Pos) // Reset Done, only available from v4.20a +#define GRSTCTL_DMAREQ_Pos (30U) +#define GRSTCTL_DMAREQ_Msk (0x1UL << GRSTCTL_DMAREQ_Pos) // 0x40000000 +#define GRSTCTL_DMAREQ GRSTCTL_DMAREQ_Msk // DMA request signal +#define GRSTCTL_AHBIDL_Pos (31U) +#define GRSTCTL_AHBIDL_Msk (0x1UL << GRSTCTL_AHBIDL_Pos) // 0x80000000 +#define GRSTCTL_AHBIDL GRSTCTL_AHBIDL_Msk // AHB master idle + +/******************** Bit definition for DIEPMSK register ********************/ +#define DIEPMSK_XFRCM_Pos (0U) +#define DIEPMSK_XFRCM_Msk (0x1UL << DIEPMSK_XFRCM_Pos) // 0x00000001 +#define DIEPMSK_XFRCM DIEPMSK_XFRCM_Msk // Transfer completed interrupt mask +#define DIEPMSK_EPDM_Pos (1U) +#define DIEPMSK_EPDM_Msk (0x1UL << DIEPMSK_EPDM_Pos) // 0x00000002 +#define DIEPMSK_EPDM DIEPMSK_EPDM_Msk // Endpoint disabled interrupt mask +#define DIEPMSK_TOM_Pos (3U) +#define DIEPMSK_TOM_Msk (0x1UL << DIEPMSK_TOM_Pos) // 0x00000008 +#define DIEPMSK_TOM DIEPMSK_TOM_Msk // Timeout condition mask (nonisochronous endpoints) +#define DIEPMSK_ITTXFEMSK_Pos (4U) +#define DIEPMSK_ITTXFEMSK_Msk (0x1UL << DIEPMSK_ITTXFEMSK_Pos) // 0x00000010 +#define DIEPMSK_ITTXFEMSK DIEPMSK_ITTXFEMSK_Msk // IN token received when TxFIFO empty mask +#define DIEPMSK_INEPNMM_Pos (5U) +#define DIEPMSK_INEPNMM_Msk (0x1UL << DIEPMSK_INEPNMM_Pos) // 0x00000020 +#define DIEPMSK_INEPNMM DIEPMSK_INEPNMM_Msk // IN token received with EP mismatch mask +#define DIEPMSK_INEPNEM_Pos (6U) +#define DIEPMSK_INEPNEM_Msk (0x1UL << DIEPMSK_INEPNEM_Pos) // 0x00000040 +#define DIEPMSK_INEPNEM DIEPMSK_INEPNEM_Msk // IN endpoint NAK effective mask +#define DIEPMSK_TXFURM_Pos (8U) +#define DIEPMSK_TXFURM_Msk (0x1UL << DIEPMSK_TXFURM_Pos) // 0x00000100 +#define DIEPMSK_TXFURM DIEPMSK_TXFURM_Msk // FIFO underrun mask +#define DIEPMSK_BIM_Pos (9U) +#define DIEPMSK_BIM_Msk (0x1UL << DIEPMSK_BIM_Pos) // 0x00000200 +#define DIEPMSK_BIM DIEPMSK_BIM_Msk // BNA interrupt mask + +/******************** Bit definition for HPTXSTS register ********************/ +#define HPTXSTS_PTXFSAVL_Pos (0U) +#define HPTXSTS_PTXFSAVL_Msk (0xFFFFUL << HPTXSTS_PTXFSAVL_Pos) // 0x0000FFFF +#define HPTXSTS_PTXFSAVL HPTXSTS_PTXFSAVL_Msk // Periodic transmit data FIFO space available +#define HPTXSTS_PTXQSAV_Pos (16U) +#define HPTXSTS_PTXQSAV_Msk (0xFFUL << HPTXSTS_PTXQSAV_Pos) // 0x00FF0000 +#define HPTXSTS_PTXQSAV HPTXSTS_PTXQSAV_Msk // Periodic transmit request queue space available +#define HPTXSTS_PTXQSAV_0 (0x01UL << HPTXSTS_PTXQSAV_Pos) // 0x00010000 +#define HPTXSTS_PTXQSAV_1 (0x02UL << HPTXSTS_PTXQSAV_Pos) // 0x00020000 +#define HPTXSTS_PTXQSAV_2 (0x04UL << HPTXSTS_PTXQSAV_Pos) // 0x00040000 +#define HPTXSTS_PTXQSAV_3 (0x08UL << HPTXSTS_PTXQSAV_Pos) // 0x00080000 +#define HPTXSTS_PTXQSAV_4 (0x10UL << HPTXSTS_PTXQSAV_Pos) // 0x00100000 +#define HPTXSTS_PTXQSAV_5 (0x20UL << HPTXSTS_PTXQSAV_Pos) // 0x00200000 +#define HPTXSTS_PTXQSAV_6 (0x40UL << HPTXSTS_PTXQSAV_Pos) // 0x00400000 +#define HPTXSTS_PTXQSAV_7 (0x80UL << HPTXSTS_PTXQSAV_Pos) // 0x00800000 + +#define HPTXSTS_PTXQTOP_Pos (24U) +#define HPTXSTS_PTXQTOP_Msk (0xFFUL << HPTXSTS_PTXQTOP_Pos) // 0xFF000000 +#define HPTXSTS_PTXQTOP HPTXSTS_PTXQTOP_Msk // Top of the periodic transmit request queue +#define HPTXSTS_PTXQTOP_0 (0x01UL << HPTXSTS_PTXQTOP_Pos) // 0x01000000 +#define HPTXSTS_PTXQTOP_1 (0x02UL << HPTXSTS_PTXQTOP_Pos) // 0x02000000 +#define HPTXSTS_PTXQTOP_2 (0x04UL << HPTXSTS_PTXQTOP_Pos) // 0x04000000 +#define HPTXSTS_PTXQTOP_3 (0x08UL << HPTXSTS_PTXQTOP_Pos) // 0x08000000 +#define HPTXSTS_PTXQTOP_4 (0x10UL << HPTXSTS_PTXQTOP_Pos) // 0x10000000 +#define HPTXSTS_PTXQTOP_5 (0x20UL << HPTXSTS_PTXQTOP_Pos) // 0x20000000 +#define HPTXSTS_PTXQTOP_6 (0x40UL << HPTXSTS_PTXQTOP_Pos) // 0x40000000 +#define HPTXSTS_PTXQTOP_7 (0x80UL << HPTXSTS_PTXQTOP_Pos) // 0x80000000 + +/******************** Bit definition for HAINT register ********************/ +#define HAINT_HAINT_Pos (0U) +#define HAINT_HAINT_Msk (0xFFFFUL << HAINT_HAINT_Pos) // 0x0000FFFF +#define HAINT_HAINT HAINT_HAINT_Msk // Channel interrupts + +/******************** Bit definition for DOEPMSK register ********************/ +#define DOEPMSK_XFRCM_Pos (0U) +#define DOEPMSK_XFRCM_Msk (0x1UL << DOEPMSK_XFRCM_Pos) // 0x00000001 +#define DOEPMSK_XFRCM DOEPMSK_XFRCM_Msk // Transfer completed interrupt mask +#define DOEPMSK_EPDM_Pos (1U) +#define DOEPMSK_EPDM_Msk (0x1UL << DOEPMSK_EPDM_Pos) // 0x00000002 +#define DOEPMSK_EPDM DOEPMSK_EPDM_Msk // Endpoint disabled interrupt mask +#define DOEPMSK_AHBERRM_Pos (2U) +#define DOEPMSK_AHBERRM_Msk (0x1UL << DOEPMSK_AHBERRM_Pos) // 0x00000004 +#define DOEPMSK_AHBERRM DOEPMSK_AHBERRM_Msk // OUT transaction AHB Error interrupt mask +#define DOEPMSK_STUPM_Pos (3U) +#define DOEPMSK_STUPM_Msk (0x1UL << DOEPMSK_STUPM_Pos) // 0x00000008 +#define DOEPMSK_STUPM DOEPMSK_STUPM_Msk // SETUP phase done mask +#define DOEPMSK_OTEPDM_Pos (4U) +#define DOEPMSK_OTEPDM_Msk (0x1UL << DOEPMSK_OTEPDM_Pos) // 0x00000010 +#define DOEPMSK_OTEPDM DOEPMSK_OTEPDM_Msk // OUT token received when endpoint disabled mask +#define DOEPMSK_OTEPSPRM_Pos (5U) +#define DOEPMSK_OTEPSPRM_Msk (0x1UL << DOEPMSK_OTEPSPRM_Pos) // 0x00000020 +#define DOEPMSK_OTEPSPRM DOEPMSK_OTEPSPRM_Msk // Status Phase Received mask +#define DOEPMSK_B2BSTUP_Pos (6U) +#define DOEPMSK_B2BSTUP_Msk (0x1UL << DOEPMSK_B2BSTUP_Pos) // 0x00000040 +#define DOEPMSK_B2BSTUP DOEPMSK_B2BSTUP_Msk // Back-to-back SETUP packets received mask +#define DOEPMSK_OPEM_Pos (8U) +#define DOEPMSK_OPEM_Msk (0x1UL << DOEPMSK_OPEM_Pos) // 0x00000100 +#define DOEPMSK_OPEM DOEPMSK_OPEM_Msk // OUT packet error mask +#define DOEPMSK_BOIM_Pos (9U) +#define DOEPMSK_BOIM_Msk (0x1UL << DOEPMSK_BOIM_Pos) // 0x00000200 +#define DOEPMSK_BOIM DOEPMSK_BOIM_Msk // BNA interrupt mask +#define DOEPMSK_BERRM_Pos (12U) +#define DOEPMSK_BERRM_Msk (0x1UL << DOEPMSK_BERRM_Pos) // 0x00001000 +#define DOEPMSK_BERRM DOEPMSK_BERRM_Msk // Babble error interrupt mask +#define DOEPMSK_NAKM_Pos (13U) +#define DOEPMSK_NAKM_Msk (0x1UL << DOEPMSK_NAKM_Pos) // 0x00002000 +#define DOEPMSK_NAKM DOEPMSK_NAKM_Msk // OUT Packet NAK interrupt mask +#define DOEPMSK_NYETM_Pos (14U) +#define DOEPMSK_NYETM_Msk (0x1UL << DOEPMSK_NYETM_Pos) // 0x00004000 +#define DOEPMSK_NYETM DOEPMSK_NYETM_Msk // NYET interrupt mask + +/******************** Bit definition for GINTSTS register ********************/ +#define GINTSTS_CMOD_Pos (0U) +#define GINTSTS_CMOD_Msk (0x1UL << GINTSTS_CMOD_Pos) // 0x00000001 +#define GINTSTS_CMOD GINTSTS_CMOD_Msk // Current mode of operation +#define GINTSTS_MMIS_Pos (1U) +#define GINTSTS_MMIS_Msk (0x1UL << GINTSTS_MMIS_Pos) // 0x00000002 +#define GINTSTS_MMIS GINTSTS_MMIS_Msk // Mode mismatch interrupt +#define GINTSTS_OTGINT_Pos (2U) +#define GINTSTS_OTGINT_Msk (0x1UL << GINTSTS_OTGINT_Pos) // 0x00000004 +#define GINTSTS_OTGINT GINTSTS_OTGINT_Msk // OTG interrupt +#define GINTSTS_SOF_Pos (3U) +#define GINTSTS_SOF_Msk (0x1UL << GINTSTS_SOF_Pos) // 0x00000008 +#define GINTSTS_SOF GINTSTS_SOF_Msk // Start of frame +#define GINTSTS_RXFLVL_Pos (4U) +#define GINTSTS_RXFLVL_Msk (0x1UL << GINTSTS_RXFLVL_Pos) // 0x00000010 +#define GINTSTS_RXFLVL GINTSTS_RXFLVL_Msk // RxFIFO nonempty +#define GINTSTS_NPTXFE_Pos (5U) +#define GINTSTS_NPTXFE_Msk (0x1UL << GINTSTS_NPTXFE_Pos) // 0x00000020 +#define GINTSTS_NPTXFE GINTSTS_NPTXFE_Msk // Nonperiodic TxFIFO empty +#define GINTSTS_GINAKEFF_Pos (6U) +#define GINTSTS_GINAKEFF_Msk (0x1UL << GINTSTS_GINAKEFF_Pos) // 0x00000040 +#define GINTSTS_GINAKEFF GINTSTS_GINAKEFF_Msk // Global IN nonperiodic NAK effective +#define GINTSTS_BOUTNAKEFF_Pos (7U) +#define GINTSTS_BOUTNAKEFF_Msk (0x1UL << GINTSTS_BOUTNAKEFF_Pos) // 0x00000080 +#define GINTSTS_BOUTNAKEFF GINTSTS_BOUTNAKEFF_Msk // Global OUT NAK effective +#define GINTSTS_ESUSP_Pos (10U) +#define GINTSTS_ESUSP_Msk (0x1UL << GINTSTS_ESUSP_Pos) // 0x00000400 +#define GINTSTS_ESUSP GINTSTS_ESUSP_Msk // Early suspend +#define GINTSTS_USBSUSP_Pos (11U) +#define GINTSTS_USBSUSP_Msk (0x1UL << GINTSTS_USBSUSP_Pos) // 0x00000800 +#define GINTSTS_USBSUSP GINTSTS_USBSUSP_Msk // USB suspend +#define GINTSTS_USBRST_Pos (12U) +#define GINTSTS_USBRST_Msk (0x1UL << GINTSTS_USBRST_Pos) // 0x00001000 +#define GINTSTS_USBRST GINTSTS_USBRST_Msk // USB reset +#define GINTSTS_ENUMDNE_Pos (13U) +#define GINTSTS_ENUMDNE_Msk (0x1UL << GINTSTS_ENUMDNE_Pos) // 0x00002000 +#define GINTSTS_ENUMDNE GINTSTS_ENUMDNE_Msk // Enumeration done +#define GINTSTS_ISOODRP_Pos (14U) +#define GINTSTS_ISOODRP_Msk (0x1UL << GINTSTS_ISOODRP_Pos) // 0x00004000 +#define GINTSTS_ISOODRP GINTSTS_ISOODRP_Msk // Isochronous OUT packet dropped interrupt +#define GINTSTS_EOPF_Pos (15U) +#define GINTSTS_EOPF_Msk (0x1UL << GINTSTS_EOPF_Pos) // 0x00008000 +#define GINTSTS_EOPF GINTSTS_EOPF_Msk // End of periodic frame interrupt +#define GINTSTS_IEPINT_Pos (18U) +#define GINTSTS_IEPINT_Msk (0x1UL << GINTSTS_IEPINT_Pos) // 0x00040000 +#define GINTSTS_IEPINT GINTSTS_IEPINT_Msk // IN endpoint interrupt +#define GINTSTS_OEPINT_Pos (19U) +#define GINTSTS_OEPINT_Msk (0x1UL << GINTSTS_OEPINT_Pos) // 0x00080000 +#define GINTSTS_OEPINT GINTSTS_OEPINT_Msk // OUT endpoint interrupt +#define GINTSTS_IISOIXFR_Pos (20U) +#define GINTSTS_IISOIXFR_Msk (0x1UL << GINTSTS_IISOIXFR_Pos) // 0x00100000 +#define GINTSTS_IISOIXFR GINTSTS_IISOIXFR_Msk // Incomplete isochronous IN transfer +#define GINTSTS_PXFR_INCOMPISOOUT_Pos (21U) +#define GINTSTS_PXFR_INCOMPISOOUT_Msk (0x1UL << GINTSTS_PXFR_INCOMPISOOUT_Pos) // 0x00200000 +#define GINTSTS_PXFR_INCOMPISOOUT GINTSTS_PXFR_INCOMPISOOUT_Msk // Incomplete periodic transfer +#define GINTSTS_DATAFSUSP_Pos (22U) +#define GINTSTS_DATAFSUSP_Msk (0x1UL << GINTSTS_DATAFSUSP_Pos) // 0x00400000 +#define GINTSTS_DATAFSUSP GINTSTS_DATAFSUSP_Msk // Data fetch suspended +#define GINTSTS_RSTDET_Pos (23U) +#define GINTSTS_RSTDET_Msk (0x1UL << GINTSTS_RSTDET_Pos) // 0x00800000 +#define GINTSTS_RSTDET GINTSTS_RSTDET_Msk // Reset detected interrupt +#define GINTSTS_HPRTINT_Pos (24U) +#define GINTSTS_HPRTINT_Msk (0x1UL << GINTSTS_HPRTINT_Pos) // 0x01000000 +#define GINTSTS_HPRTINT GINTSTS_HPRTINT_Msk // Host port interrupt +#define GINTSTS_HCINT_Pos (25U) +#define GINTSTS_HCINT_Msk (0x1UL << GINTSTS_HCINT_Pos) // 0x02000000 +#define GINTSTS_HCINT GINTSTS_HCINT_Msk // Host channels interrupt +#define GINTSTS_PTXFE_Pos (26U) +#define GINTSTS_PTXFE_Msk (0x1UL << GINTSTS_PTXFE_Pos) // 0x04000000 +#define GINTSTS_PTXFE GINTSTS_PTXFE_Msk // Periodic TxFIFO empty +#define GINTSTS_LPMINT_Pos (27U) +#define GINTSTS_LPMINT_Msk (0x1UL << GINTSTS_LPMINT_Pos) // 0x08000000 +#define GINTSTS_LPMINT GINTSTS_LPMINT_Msk // LPM interrupt +#define GINTSTS_CIDSCHG_Pos (28U) +#define GINTSTS_CIDSCHG_Msk (0x1UL << GINTSTS_CIDSCHG_Pos) // 0x10000000 +#define GINTSTS_CIDSCHG GINTSTS_CIDSCHG_Msk // Connector ID status change +#define GINTSTS_DISCINT_Pos (29U) +#define GINTSTS_DISCINT_Msk (0x1UL << GINTSTS_DISCINT_Pos) // 0x20000000 +#define GINTSTS_DISCINT GINTSTS_DISCINT_Msk // Disconnect detected interrupt +#define GINTSTS_SRQINT_Pos (30U) +#define GINTSTS_SRQINT_Msk (0x1UL << GINTSTS_SRQINT_Pos) // 0x40000000 +#define GINTSTS_SRQINT GINTSTS_SRQINT_Msk // Session request/new session detected interrupt +#define GINTSTS_WKUINT_Pos (31U) +#define GINTSTS_WKUINT_Msk (0x1UL << GINTSTS_WKUINT_Pos) // 0x80000000 +#define GINTSTS_WKUINT GINTSTS_WKUINT_Msk // Resume/remote wakeup detected interrupt + +/******************** Bit definition for GINTMSK register ********************/ +#define GINTMSK_MMISM_Pos (1U) +#define GINTMSK_MMISM_Msk (0x1UL << GINTMSK_MMISM_Pos) // 0x00000002 +#define GINTMSK_MMISM GINTMSK_MMISM_Msk // Mode mismatch interrupt mask +#define GINTMSK_OTGINT_Pos (2U) +#define GINTMSK_OTGINT_Msk (0x1UL << GINTMSK_OTGINT_Pos) // 0x00000004 +#define GINTMSK_OTGINT GINTMSK_OTGINT_Msk // OTG interrupt mask +#define GINTMSK_SOFM_Pos (3U) +#define GINTMSK_SOFM_Msk (0x1UL << GINTMSK_SOFM_Pos) // 0x00000008 +#define GINTMSK_SOFM GINTMSK_SOFM_Msk // Start of frame mask +#define GINTMSK_RXFLVLM_Pos (4U) +#define GINTMSK_RXFLVLM_Msk (0x1UL << GINTMSK_RXFLVLM_Pos) // 0x00000010 +#define GINTMSK_RXFLVLM GINTMSK_RXFLVLM_Msk // Receive FIFO nonempty mask +#define GINTMSK_NPTXFEM_Pos (5U) +#define GINTMSK_NPTXFEM_Msk (0x1UL << GINTMSK_NPTXFEM_Pos) // 0x00000020 +#define GINTMSK_NPTXFEM GINTMSK_NPTXFEM_Msk // Nonperiodic TxFIFO empty mask +#define GINTMSK_GINAKEFFM_Pos (6U) +#define GINTMSK_GINAKEFFM_Msk (0x1UL << GINTMSK_GINAKEFFM_Pos) // 0x00000040 +#define GINTMSK_GINAKEFFM GINTMSK_GINAKEFFM_Msk // Global nonperiodic IN NAK effective mask +#define GINTMSK_GONAKEFFM_Pos (7U) +#define GINTMSK_GONAKEFFM_Msk (0x1UL << GINTMSK_GONAKEFFM_Pos) // 0x00000080 +#define GINTMSK_GONAKEFFM GINTMSK_GONAKEFFM_Msk // Global OUT NAK effective mask +#define GINTMSK_ESUSPM_Pos (10U) +#define GINTMSK_ESUSPM_Msk (0x1UL << GINTMSK_ESUSPM_Pos) // 0x00000400 +#define GINTMSK_ESUSPM GINTMSK_ESUSPM_Msk // Early suspend mask +#define GINTMSK_USBSUSPM_Pos (11U) +#define GINTMSK_USBSUSPM_Msk (0x1UL << GINTMSK_USBSUSPM_Pos) // 0x00000800 +#define GINTMSK_USBSUSPM GINTMSK_USBSUSPM_Msk // USB suspend mask +#define GINTMSK_USBRST_Pos (12U) +#define GINTMSK_USBRST_Msk (0x1UL << GINTMSK_USBRST_Pos) // 0x00001000 +#define GINTMSK_USBRST GINTMSK_USBRST_Msk // USB reset mask +#define GINTMSK_ENUMDNEM_Pos (13U) +#define GINTMSK_ENUMDNEM_Msk (0x1UL << GINTMSK_ENUMDNEM_Pos) // 0x00002000 +#define GINTMSK_ENUMDNEM GINTMSK_ENUMDNEM_Msk // Enumeration done mask +#define GINTMSK_ISOODRPM_Pos (14U) +#define GINTMSK_ISOODRPM_Msk (0x1UL << GINTMSK_ISOODRPM_Pos) // 0x00004000 +#define GINTMSK_ISOODRPM GINTMSK_ISOODRPM_Msk // Isochronous OUT packet dropped interrupt mask +#define GINTMSK_EOPFM_Pos (15U) +#define GINTMSK_EOPFM_Msk (0x1UL << GINTMSK_EOPFM_Pos) // 0x00008000 +#define GINTMSK_EOPFM GINTMSK_EOPFM_Msk // End of periodic frame interrupt mask +#define GINTMSK_EPMISM_Pos (17U) +#define GINTMSK_EPMISM_Msk (0x1UL << GINTMSK_EPMISM_Pos) // 0x00020000 +#define GINTMSK_EPMISM GINTMSK_EPMISM_Msk // Endpoint mismatch interrupt mask +#define GINTMSK_IEPINT_Pos (18U) +#define GINTMSK_IEPINT_Msk (0x1UL << GINTMSK_IEPINT_Pos) // 0x00040000 +#define GINTMSK_IEPINT GINTMSK_IEPINT_Msk // IN endpoints interrupt mask +#define GINTMSK_OEPINT_Pos (19U) +#define GINTMSK_OEPINT_Msk (0x1UL << GINTMSK_OEPINT_Pos) // 0x00080000 +#define GINTMSK_OEPINT GINTMSK_OEPINT_Msk // OUT endpoints interrupt mask +#define GINTMSK_IISOIXFRM_Pos (20U) +#define GINTMSK_IISOIXFRM_Msk (0x1UL << GINTMSK_IISOIXFRM_Pos) // 0x00100000 +#define GINTMSK_IISOIXFRM GINTMSK_IISOIXFRM_Msk // Incomplete isochronous IN transfer mask +#define GINTMSK_PXFRM_IISOOXFRM_Pos (21U) +#define GINTMSK_PXFRM_IISOOXFRM_Msk (0x1UL << GINTMSK_PXFRM_IISOOXFRM_Pos) // 0x00200000 +#define GINTMSK_PXFRM_IISOOXFRM GINTMSK_PXFRM_IISOOXFRM_Msk // Incomplete periodic transfer mask +#define GINTMSK_FSUSPM_Pos (22U) +#define GINTMSK_FSUSPM_Msk (0x1UL << GINTMSK_FSUSPM_Pos) // 0x00400000 +#define GINTMSK_FSUSPM GINTMSK_FSUSPM_Msk // Data fetch suspended mask +#define GINTMSK_RSTDEM_Pos (23U) +#define GINTMSK_RSTDEM_Msk (0x1UL << GINTMSK_RSTDEM_Pos) // 0x00800000 +#define GINTMSK_RSTDEM GINTMSK_RSTDEM_Msk // Reset detected interrupt mask +#define GINTMSK_PRTIM_Pos (24U) +#define GINTMSK_PRTIM_Msk (0x1UL << GINTMSK_PRTIM_Pos) // 0x01000000 +#define GINTMSK_PRTIM GINTMSK_PRTIM_Msk // Host port interrupt mask +#define GINTMSK_HCIM_Pos (25U) +#define GINTMSK_HCIM_Msk (0x1UL << GINTMSK_HCIM_Pos) // 0x02000000 +#define GINTMSK_HCIM GINTMSK_HCIM_Msk // Host channels interrupt mask +#define GINTMSK_PTXFEM_Pos (26U) +#define GINTMSK_PTXFEM_Msk (0x1UL << GINTMSK_PTXFEM_Pos) // 0x04000000 +#define GINTMSK_PTXFEM GINTMSK_PTXFEM_Msk // Periodic TxFIFO empty mask +#define GINTMSK_LPMINTM_Pos (27U) +#define GINTMSK_LPMINTM_Msk (0x1UL << GINTMSK_LPMINTM_Pos) // 0x08000000 +#define GINTMSK_LPMINTM GINTMSK_LPMINTM_Msk // LPM interrupt Mask +#define GINTMSK_CIDSCHGM_Pos (28U) +#define GINTMSK_CIDSCHGM_Msk (0x1UL << GINTMSK_CIDSCHGM_Pos) // 0x10000000 +#define GINTMSK_CIDSCHGM GINTMSK_CIDSCHGM_Msk // Connector ID status change mask +#define GINTMSK_DISCINT_Pos (29U) +#define GINTMSK_DISCINT_Msk (0x1UL << GINTMSK_DISCINT_Pos) // 0x20000000 +#define GINTMSK_DISCINT GINTMSK_DISCINT_Msk // Disconnect detected interrupt mask +#define GINTMSK_SRQIM_Pos (30U) +#define GINTMSK_SRQIM_Msk (0x1UL << GINTMSK_SRQIM_Pos) // 0x40000000 +#define GINTMSK_SRQIM GINTMSK_SRQIM_Msk // Session request/new session detected interrupt mask +#define GINTMSK_WUIM_Pos (31U) +#define GINTMSK_WUIM_Msk (0x1UL << GINTMSK_WUIM_Pos) // 0x80000000 +#define GINTMSK_WUIM GINTMSK_WUIM_Msk // Resume/remote wakeup detected interrupt mask + +/******************** Bit definition for DAINT register ********************/ +#define DAINT_IEPINT_Pos (0U) +#define DAINT_IEPINT_Msk (0xFFFFUL << DAINT_IEPINT_Pos) // 0x0000FFFF +#define DAINT_IEPINT DAINT_IEPINT_Msk // IN endpoint interrupt bits +#define DAINT_OEPINT_Pos (16U) +#define DAINT_OEPINT_Msk (0xFFFFUL << DAINT_OEPINT_Pos) // 0xFFFF0000 +#define DAINT_OEPINT DAINT_OEPINT_Msk // OUT endpoint interrupt bits + +/******************** Bit definition for HAINTMSK register ********************/ +#define HAINTMSK_HAINTM_Pos (0U) +#define HAINTMSK_HAINTM_Msk (0xFFFFUL << HAINTMSK_HAINTM_Pos) // 0x0000FFFF +#define HAINTMSK_HAINTM HAINTMSK_HAINTM_Msk // Channel interrupt mask + +/******************** Bit definition for GRXSTSP register ********************/ +#define GRXSTSP_EPNUM_Pos (0U) +#define GRXSTSP_EPNUM_Msk (0xFUL << GRXSTSP_EPNUM_Pos) // 0x0000000F +#define GRXSTSP_EPNUM GRXSTSP_EPNUM_Msk // IN EP interrupt mask bits +#define GRXSTSP_BCNT_Pos (4U) +#define GRXSTSP_BCNT_Msk (0x7FFUL << GRXSTSP_BCNT_Pos) // 0x00007FF0 +#define GRXSTSP_BCNT GRXSTSP_BCNT_Msk // OUT EP interrupt mask bits +#define GRXSTSP_DPID_Pos (15U) +#define GRXSTSP_DPID_Msk (0x3UL << GRXSTSP_DPID_Pos) // 0x00018000 +#define GRXSTSP_DPID GRXSTSP_DPID_Msk // OUT EP interrupt mask bits +#define GRXSTSP_PKTSTS_Pos (17U) +#define GRXSTSP_PKTSTS_Msk (0xFUL << GRXSTSP_PKTSTS_Pos) // 0x001E0000 +#define GRXSTSP_PKTSTS GRXSTSP_PKTSTS_Msk // OUT EP interrupt mask bits + +#define GRXSTS_PKTSTS_GLOBALOUTNAK 1 +#define GRXSTS_PKTSTS_OUTRX 2 +#define GRXSTS_PKTSTS_HCHIN 2 +#define GRXSTS_PKTSTS_OUTDONE 3 +#define GRXSTS_PKTSTS_HCHIN_XFER_COMP 3 +#define GRXSTS_PKTSTS_SETUPDONE 4 +#define GRXSTS_PKTSTS_DATATOGGLEERR 5 +#define GRXSTS_PKTSTS_SETUPRX 6 +#define GRXSTS_PKTSTS_HCHHALTED 7 + + +/******************** Bit definition for DAINTMSK register ********************/ +#define DAINTMSK_IEPM_Pos (0U) +#define DAINTMSK_IEPM_Msk (0xFFFFUL << DAINTMSK_IEPM_Pos) // 0x0000FFFF +#define DAINTMSK_IEPM DAINTMSK_IEPM_Msk // IN EP interrupt mask bits +#define DAINTMSK_OEPM_Pos (16U) +#define DAINTMSK_OEPM_Msk (0xFFFFUL << DAINTMSK_OEPM_Pos) // 0xFFFF0000 +#define DAINTMSK_OEPM DAINTMSK_OEPM_Msk // OUT EP interrupt mask bits + +#if 0 +/******************** Bit definition for OTG register ********************/ +#define CHNUM_Pos (0U) +#define CHNUM_Msk (0xFUL << CHNUM_Pos) // 0x0000000F +#define CHNUM CHNUM_Msk // Channel number +#define CHNUM_0 (0x1UL << CHNUM_Pos) // 0x00000001 +#define CHNUM_1 (0x2UL << CHNUM_Pos) // 0x00000002 +#define CHNUM_2 (0x4UL << CHNUM_Pos) // 0x00000004 +#define CHNUM_3 (0x8UL << CHNUM_Pos) // 0x00000008 +#define BCNT_Pos (4U) +#define BCNT_Msk (0x7FFUL << BCNT_Pos) // 0x00007FF0 +#define BCNT BCNT_Msk // Byte count + +#define DPID_Pos (15U) +#define DPID_Msk (0x3UL << DPID_Pos) // 0x00018000 +#define DPID DPID_Msk // Data PID +#define DPID_0 (0x1UL << DPID_Pos) // 0x00008000 +#define DPID_1 (0x2UL << DPID_Pos) // 0x00010000 + +#define PKTSTS_Pos (17U) +#define PKTSTS_Msk (0xFUL << PKTSTS_Pos) // 0x001E0000 +#define PKTSTS PKTSTS_Msk // Packet status +#define PKTSTS_0 (0x1UL << PKTSTS_Pos) // 0x00020000 +#define PKTSTS_1 (0x2UL << PKTSTS_Pos) // 0x00040000 +#define PKTSTS_2 (0x4UL << PKTSTS_Pos) // 0x00080000 +#define PKTSTS_3 (0x8UL << PKTSTS_Pos) // 0x00100000 + +#define EPNUM_Pos (0U) +#define EPNUM_Msk (0xFUL << EPNUM_Pos) // 0x0000000F +#define EPNUM EPNUM_Msk // Endpoint number +#define EPNUM_0 (0x1UL << EPNUM_Pos) // 0x00000001 +#define EPNUM_1 (0x2UL << EPNUM_Pos) // 0x00000002 +#define EPNUM_2 (0x4UL << EPNUM_Pos) // 0x00000004 +#define EPNUM_3 (0x8UL << EPNUM_Pos) // 0x00000008 + +#define FRMNUM_Pos (21U) +#define FRMNUM_Msk (0xFUL << FRMNUM_Pos) // 0x01E00000 +#define FRMNUM FRMNUM_Msk // Frame number +#define FRMNUM_0 (0x1UL << FRMNUM_Pos) // 0x00200000 +#define FRMNUM_1 (0x2UL << FRMNUM_Pos) // 0x00400000 +#define FRMNUM_2 (0x4UL << FRMNUM_Pos) // 0x00800000 +#define FRMNUM_3 (0x8UL << FRMNUM_Pos) // 0x01000000 +#endif + +/******************** Bit definition for GRXFSIZ register ********************/ +#define GRXFSIZ_RXFD_Pos (0U) +#define GRXFSIZ_RXFD_Msk (0xFFFFUL << GRXFSIZ_RXFD_Pos) // 0x0000FFFF +#define GRXFSIZ_RXFD GRXFSIZ_RXFD_Msk // RxFIFO depth + +/******************** Bit definition for DVBUSDIS register ********************/ +#define DVBUSDIS_VBUSDT_Pos (0U) +#define DVBUSDIS_VBUSDT_Msk (0xFFFFUL << DVBUSDIS_VBUSDT_Pos) // 0x0000FFFF +#define DVBUSDIS_VBUSDT DVBUSDIS_VBUSDT_Msk // Device VBUS discharge time + +/******************** Bit definition for OTG register ********************/ +#define GNPTXFSIZ_NPTXFSA_Pos (0U) +#define GNPTXFSIZ_NPTXFSA_Msk (0xFFFFUL << GNPTXFSIZ_NPTXFSA_Pos) // 0x0000FFFF +#define GNPTXFSIZ_NPTXFSA GNPTXFSIZ_NPTXFSA_Msk // Nonperiodic transmit RAM start address +#define GNPTXFSIZ_NPTXFD_Pos (16U) +#define GNPTXFSIZ_NPTXFD_Msk (0xFFFFUL << GNPTXFSIZ_NPTXFD_Pos) // 0xFFFF0000 +#define GNPTXFSIZ_NPTXFD GNPTXFSIZ_NPTXFD_Msk // Nonperiodic TxFIFO depth +#define DIEPTXF0_TX0FSA_Pos (0U) +#define DIEPTXF0_TX0FSA_Msk (0xFFFFUL << DIEPTXF0_TX0FSA_Pos) // 0x0000FFFF +#define DIEPTXF0_TX0FSA DIEPTXF0_TX0FSA_Msk // Endpoint 0 transmit RAM start address +#define DIEPTXF0_TX0FD_Pos (16U) +#define DIEPTXF0_TX0FD_Msk (0xFFFFUL << DIEPTXF0_TX0FD_Pos) // 0xFFFF0000 +#define DIEPTXF0_TX0FD DIEPTXF0_TX0FD_Msk // Endpoint 0 TxFIFO depth + +/******************** Bit definition for DVBUSPULSE register ********************/ +#define DVBUSPULSE_DVBUSP_Pos (0U) +#define DVBUSPULSE_DVBUSP_Msk (0xFFFUL << DVBUSPULSE_DVBUSP_Pos) // 0x00000FFF +#define DVBUSPULSE_DVBUSP DVBUSPULSE_DVBUSP_Msk // Device VBUS pulsing time + +/******************** Bit definition for GNPTXSTS register ********************/ +#define GNPTXSTS_NPTXFSAV_Pos (0U) +#define GNPTXSTS_NPTXFSAV_Msk (0xFFFFUL << GNPTXSTS_NPTXFSAV_Pos) // 0x0000FFFF +#define GNPTXSTS_NPTXFSAV GNPTXSTS_NPTXFSAV_Msk // Nonperiodic TxFIFO space available + +#define GNPTXSTS_NPTQXSAV_Pos (16U) +#define GNPTXSTS_NPTQXSAV_Msk (0xFFUL << GNPTXSTS_NPTQXSAV_Pos) // 0x00FF0000 +#define GNPTXSTS_NPTQXSAV GNPTXSTS_NPTQXSAV_Msk // Nonperiodic transmit request queue space available +#define GNPTXSTS_NPTQXSAV_0 (0x01UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00010000 +#define GNPTXSTS_NPTQXSAV_1 (0x02UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00020000 +#define GNPTXSTS_NPTQXSAV_2 (0x04UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00040000 +#define GNPTXSTS_NPTQXSAV_3 (0x08UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00080000 +#define GNPTXSTS_NPTQXSAV_4 (0x10UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00100000 +#define GNPTXSTS_NPTQXSAV_5 (0x20UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00200000 +#define GNPTXSTS_NPTQXSAV_6 (0x40UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00400000 +#define GNPTXSTS_NPTQXSAV_7 (0x80UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00800000 + +#define GNPTXSTS_NPTXQTOP_Pos (24U) +#define GNPTXSTS_NPTXQTOP_Msk (0x7FUL << GNPTXSTS_NPTXQTOP_Pos) // 0x7F000000 +#define GNPTXSTS_NPTXQTOP GNPTXSTS_NPTXQTOP_Msk // Top of the nonperiodic transmit request queue +#define GNPTXSTS_NPTXQTOP_0 (0x01UL << GNPTXSTS_NPTXQTOP_Pos) // 0x01000000 +#define GNPTXSTS_NPTXQTOP_1 (0x02UL << GNPTXSTS_NPTXQTOP_Pos) // 0x02000000 +#define GNPTXSTS_NPTXQTOP_2 (0x04UL << GNPTXSTS_NPTXQTOP_Pos) // 0x04000000 +#define GNPTXSTS_NPTXQTOP_3 (0x08UL << GNPTXSTS_NPTXQTOP_Pos) // 0x08000000 +#define GNPTXSTS_NPTXQTOP_4 (0x10UL << GNPTXSTS_NPTXQTOP_Pos) // 0x10000000 +#define GNPTXSTS_NPTXQTOP_5 (0x20UL << GNPTXSTS_NPTXQTOP_Pos) // 0x20000000 +#define GNPTXSTS_NPTXQTOP_6 (0x40UL << GNPTXSTS_NPTXQTOP_Pos) // 0x40000000 + +/******************** Bit definition for DTHRCTL register ********************/ +#define DTHRCTL_NONISOTHREN_Pos (0U) +#define DTHRCTL_NONISOTHREN_Msk (0x1UL << DTHRCTL_NONISOTHREN_Pos) // 0x00000001 +#define DTHRCTL_NONISOTHREN DTHRCTL_NONISOTHREN_Msk // Nonisochronous IN endpoints threshold enable +#define DTHRCTL_ISOTHREN_Pos (1U) +#define DTHRCTL_ISOTHREN_Msk (0x1UL << DTHRCTL_ISOTHREN_Pos) // 0x00000002 +#define DTHRCTL_ISOTHREN DTHRCTL_ISOTHREN_Msk // ISO IN endpoint threshold enable + +#define DTHRCTL_TXTHRLEN_Pos (2U) +#define DTHRCTL_TXTHRLEN_Msk (0x1FFUL << DTHRCTL_TXTHRLEN_Pos) // 0x000007FC +#define DTHRCTL_TXTHRLEN DTHRCTL_TXTHRLEN_Msk // Transmit threshold length +#define DTHRCTL_TXTHRLEN_0 (0x001UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000004 +#define DTHRCTL_TXTHRLEN_1 (0x002UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000008 +#define DTHRCTL_TXTHRLEN_2 (0x004UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000010 +#define DTHRCTL_TXTHRLEN_3 (0x008UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000020 +#define DTHRCTL_TXTHRLEN_4 (0x010UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000040 +#define DTHRCTL_TXTHRLEN_5 (0x020UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000080 +#define DTHRCTL_TXTHRLEN_6 (0x040UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000100 +#define DTHRCTL_TXTHRLEN_7 (0x080UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000200 +#define DTHRCTL_TXTHRLEN_8 (0x100UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000400 +#define DTHRCTL_RXTHREN_Pos (16U) +#define DTHRCTL_RXTHREN_Msk (0x1UL << DTHRCTL_RXTHREN_Pos) // 0x00010000 +#define DTHRCTL_RXTHREN DTHRCTL_RXTHREN_Msk // Receive threshold enable + +#define DTHRCTL_RXTHRLEN_Pos (17U) +#define DTHRCTL_RXTHRLEN_Msk (0x1FFUL << DTHRCTL_RXTHRLEN_Pos) // 0x03FE0000 +#define DTHRCTL_RXTHRLEN DTHRCTL_RXTHRLEN_Msk // Receive threshold length +#define DTHRCTL_RXTHRLEN_0 (0x001UL << DTHRCTL_RXTHRLEN_Pos) // 0x00020000 +#define DTHRCTL_RXTHRLEN_1 (0x002UL << DTHRCTL_RXTHRLEN_Pos) // 0x00040000 +#define DTHRCTL_RXTHRLEN_2 (0x004UL << DTHRCTL_RXTHRLEN_Pos) // 0x00080000 +#define DTHRCTL_RXTHRLEN_3 (0x008UL << DTHRCTL_RXTHRLEN_Pos) // 0x00100000 +#define DTHRCTL_RXTHRLEN_4 (0x010UL << DTHRCTL_RXTHRLEN_Pos) // 0x00200000 +#define DTHRCTL_RXTHRLEN_5 (0x020UL << DTHRCTL_RXTHRLEN_Pos) // 0x00400000 +#define DTHRCTL_RXTHRLEN_6 (0x040UL << DTHRCTL_RXTHRLEN_Pos) // 0x00800000 +#define DTHRCTL_RXTHRLEN_7 (0x080UL << DTHRCTL_RXTHRLEN_Pos) // 0x01000000 +#define DTHRCTL_RXTHRLEN_8 (0x100UL << DTHRCTL_RXTHRLEN_Pos) // 0x02000000 +#define DTHRCTL_ARPEN_Pos (27U) +#define DTHRCTL_ARPEN_Msk (0x1UL << DTHRCTL_ARPEN_Pos) // 0x08000000 +#define DTHRCTL_ARPEN DTHRCTL_ARPEN_Msk // Arbiter parking enable + +/******************** Bit definition for DIEPEMPMSK register ********************/ +#define DIEPEMPMSK_INEPTXFEM_Pos (0U) +#define DIEPEMPMSK_INEPTXFEM_Msk (0xFFFFUL << DIEPEMPMSK_INEPTXFEM_Pos) // 0x0000FFFF +#define DIEPEMPMSK_INEPTXFEM DIEPEMPMSK_INEPTXFEM_Msk // IN EP Tx FIFO empty interrupt mask bits + +/******************** Bit definition for DEACHINT register ********************/ +#define DEACHINT_IEP1INT_Pos (1U) +#define DEACHINT_IEP1INT_Msk (0x1UL << DEACHINT_IEP1INT_Pos) // 0x00000002 +#define DEACHINT_IEP1INT DEACHINT_IEP1INT_Msk // IN endpoint 1interrupt bit +#define DEACHINT_OEP1INT_Pos (17U) +#define DEACHINT_OEP1INT_Msk (0x1UL << DEACHINT_OEP1INT_Pos) // 0x00020000 +#define DEACHINT_OEP1INT DEACHINT_OEP1INT_Msk // OUT endpoint 1 interrupt bit + +/******************** Bit definition for GCCFG register ********************/ +#define STM32_GCCFG_DCDET_Pos (0U) +#define STM32_GCCFG_DCDET_Msk (0x1UL << STM32_GCCFG_DCDET_Pos) // 0x00000001 +#define STM32_GCCFG_DCDET STM32_GCCFG_DCDET_Msk // Data contact detection (DCD) status + +#define STM32_GCCFG_PDET_Pos (1U) +#define STM32_GCCFG_PDET_Msk (0x1UL << STM32_GCCFG_PDET_Pos) // 0x00000002 +#define STM32_GCCFG_PDET STM32_GCCFG_PDET_Msk // Primary detection (PD) status + +#define STM32_GCCFG_SDET_Pos (2U) +#define STM32_GCCFG_SDET_Msk (0x1UL << STM32_GCCFG_SDET_Pos) // 0x00000004 +#define STM32_GCCFG_SDET STM32_GCCFG_SDET_Msk // Secondary detection (SD) status + +#define STM32_GCCFG_PS2DET_Pos (3U) +#define STM32_GCCFG_PS2DET_Msk (0x1UL << STM32_GCCFG_PS2DET_Pos) // 0x00000008 +#define STM32_GCCFG_PS2DET STM32_GCCFG_PS2DET_Msk // DM pull-up detection status + +#define STM32_GCCFG_PWRDWN_Pos (16U) +#define STM32_GCCFG_PWRDWN_Msk (0x1UL << STM32_GCCFG_PWRDWN_Pos) // 0x00010000 +#define STM32_GCCFG_PWRDWN STM32_GCCFG_PWRDWN_Msk // Power down + +#define STM32_GCCFG_BCDEN_Pos (17U) +#define STM32_GCCFG_BCDEN_Msk (0x1UL << STM32_GCCFG_BCDEN_Pos) // 0x00020000 +#define STM32_GCCFG_BCDEN STM32_GCCFG_BCDEN_Msk // Battery charging detector (BCD) enable + +#define STM32_GCCFG_DCDEN_Pos (18U) +#define STM32_GCCFG_DCDEN_Msk (0x1UL << STM32_GCCFG_DCDEN_Pos) // 0x00040000 +#define STM32_GCCFG_DCDEN STM32_GCCFG_DCDEN_Msk // Data contact detection (DCD) mode enable*/ + +#define STM32_GCCFG_PDEN_Pos (19U) +#define STM32_GCCFG_PDEN_Msk (0x1UL << STM32_GCCFG_PDEN_Pos) // 0x00080000 +#define STM32_GCCFG_PDEN STM32_GCCFG_PDEN_Msk // Primary detection (PD) mode enable*/ + +#define STM32_GCCFG_SDEN_Pos (20U) +#define STM32_GCCFG_SDEN_Msk (0x1UL << STM32_GCCFG_SDEN_Pos) // 0x00100000 +#define STM32_GCCFG_SDEN STM32_GCCFG_SDEN_Msk // Secondary detection (SD) mode enable + +#define STM32_GCCFG_VBDEN_Pos (21U) +#define STM32_GCCFG_VBDEN_Msk (0x1UL << STM32_GCCFG_VBDEN_Pos) // 0x00200000 +#define STM32_GCCFG_VBDEN STM32_GCCFG_VBDEN_Msk // VBUS mode enable + +#define STM32_GCCFG_OTGIDEN_Pos (22U) +#define STM32_GCCFG_OTGIDEN_Msk (0x1UL << STM32_GCCFG_OTGIDEN_Pos) // 0x00400000 +#define STM32_GCCFG_OTGIDEN STM32_GCCFG_OTGIDEN_Msk // OTG Id enable + +#define STM32_GCCFG_PHYHSEN_Pos (23U) +#define STM32_GCCFG_PHYHSEN_Msk (0x1UL << STM32_GCCFG_PHYHSEN_Pos) // 0x00800000 +#define STM32_GCCFG_PHYHSEN STM32_GCCFG_PHYHSEN_Msk // HS PHY enable + +// TODO stm32u5a5 SDEN is 22nd bit, conflict with 20th bit above +//#define STM32_GCCFG_SDEN_Pos (22U) +//#define STM32_GCCFG_SDEN_Msk (0x1U << STM32_GCCFG_SDEN_Pos) // 0x00400000 +//#define STM32_GCCFG_SDEN STM32_GCCFG_SDEN_Msk // Secondary detection (PD) mode enable + +// TODO stm32u5a5 VBVALOVA is 23rd bit, conflict with PHYHSEN bit above +#define STM32_GCCFG_VBVALOVAL_Pos (23U) +#define STM32_GCCFG_VBVALOVAL_Msk (0x1U << STM32_GCCFG_VBVALOVAL_Pos) // 0x00800000 +#define STM32_GCCFG_VBVALOVAL STM32_GCCFG_VBVALOVAL_Msk // Value of VBUSVLDEXT0 femtoPHY input + +#define STM32_GCCFG_VBVALEXTOEN_Pos (24U) +#define STM32_GCCFG_VBVALEXTOEN_Msk (0x1U << STM32_GCCFG_VBVALEXTOEN_Pos) // 0x01000000 +#define STM32_GCCFG_VBVALEXTOEN STM32_GCCFG_VBVALEXTOEN_Msk // Enables of VBUSVLDEXT0 femtoPHY input override + +#define STM32_GCCFG_PULLDOWNEN_Pos (25U) +#define STM32_GCCFG_PULLDOWNEN_Msk (0x1U << STM32_GCCFG_PULLDOWNEN_Pos) // 0x02000000 +#define STM32_GCCFG_PULLDOWNEN STM32_GCCFG_PULLDOWNEN_Msk // Enables of femtoPHY pulldown resistors, used when ID PAD is disabled + + +/******************** Bit definition for DEACHINTMSK register ********************/ +#define DEACHINTMSK_IEP1INTM_Pos (1U) +#define DEACHINTMSK_IEP1INTM_Msk (0x1UL << DEACHINTMSK_IEP1INTM_Pos) // 0x00000002 +#define DEACHINTMSK_IEP1INTM DEACHINTMSK_IEP1INTM_Msk // IN Endpoint 1 interrupt mask bit +#define DEACHINTMSK_OEP1INTM_Pos (17U) +#define DEACHINTMSK_OEP1INTM_Msk (0x1UL << DEACHINTMSK_OEP1INTM_Pos) // 0x00020000 +#define DEACHINTMSK_OEP1INTM DEACHINTMSK_OEP1INTM_Msk // OUT Endpoint 1 interrupt mask bit + +/******************** Bit definition for CID register ********************/ +#define CID_PRODUCT_ID_Pos (0U) +#define CID_PRODUCT_ID_Msk (0xFFFFFFFFUL << CID_PRODUCT_ID_Pos) // 0xFFFFFFFF +#define CID_PRODUCT_ID CID_PRODUCT_ID_Msk // Product ID field + +/******************** Bit definition for GLPMCFG register ********************/ +#define GLPMCFG_LPMEN_Pos (0U) +#define GLPMCFG_LPMEN_Msk (0x1UL << GLPMCFG_LPMEN_Pos) // 0x00000001 +#define GLPMCFG_LPMEN GLPMCFG_LPMEN_Msk // LPM support enable +#define GLPMCFG_LPMACK_Pos (1U) +#define GLPMCFG_LPMACK_Msk (0x1UL << GLPMCFG_LPMACK_Pos) // 0x00000002 +#define GLPMCFG_LPMACK GLPMCFG_LPMACK_Msk // LPM Token acknowledge enable +#define GLPMCFG_BESL_Pos (2U) +#define GLPMCFG_BESL_Msk (0xFUL << GLPMCFG_BESL_Pos) // 0x0000003C +#define GLPMCFG_BESL GLPMCFG_BESL_Msk // BESL value received with last ACKed LPM Token +#define GLPMCFG_REMWAKE_Pos (6U) +#define GLPMCFG_REMWAKE_Msk (0x1UL << GLPMCFG_REMWAKE_Pos) // 0x00000040 +#define GLPMCFG_REMWAKE GLPMCFG_REMWAKE_Msk // bRemoteWake value received with last ACKed LPM Token +#define GLPMCFG_L1SSEN_Pos (7U) +#define GLPMCFG_L1SSEN_Msk (0x1UL << GLPMCFG_L1SSEN_Pos) // 0x00000080 +#define GLPMCFG_L1SSEN GLPMCFG_L1SSEN_Msk // L1 shallow sleep enable +#define GLPMCFG_BESLTHRS_Pos (8U) +#define GLPMCFG_BESLTHRS_Msk (0xFUL << GLPMCFG_BESLTHRS_Pos) // 0x00000F00 +#define GLPMCFG_BESLTHRS GLPMCFG_BESLTHRS_Msk // BESL threshold +#define GLPMCFG_L1DSEN_Pos (12U) +#define GLPMCFG_L1DSEN_Msk (0x1UL << GLPMCFG_L1DSEN_Pos) // 0x00001000 +#define GLPMCFG_L1DSEN GLPMCFG_L1DSEN_Msk // L1 deep sleep enable +#define GLPMCFG_LPMRSP_Pos (13U) +#define GLPMCFG_LPMRSP_Msk (0x3UL << GLPMCFG_LPMRSP_Pos) // 0x00006000 +#define GLPMCFG_LPMRSP GLPMCFG_LPMRSP_Msk // LPM response +#define GLPMCFG_SLPSTS_Pos (15U) +#define GLPMCFG_SLPSTS_Msk (0x1UL << GLPMCFG_SLPSTS_Pos) // 0x00008000 +#define GLPMCFG_SLPSTS GLPMCFG_SLPSTS_Msk // Port sleep status +#define GLPMCFG_L1RSMOK_Pos (16U) +#define GLPMCFG_L1RSMOK_Msk (0x1UL << GLPMCFG_L1RSMOK_Pos) // 0x00010000 +#define GLPMCFG_L1RSMOK GLPMCFG_L1RSMOK_Msk // Sleep State Resume OK +#define GLPMCFG_LPMCHIDX_Pos (17U) +#define GLPMCFG_LPMCHIDX_Msk (0xFUL << GLPMCFG_LPMCHIDX_Pos) // 0x001E0000 +#define GLPMCFG_LPMCHIDX GLPMCFG_LPMCHIDX_Msk // LPM Channel Index +#define GLPMCFG_LPMRCNT_Pos (21U) +#define GLPMCFG_LPMRCNT_Msk (0x7UL << GLPMCFG_LPMRCNT_Pos) // 0x00E00000 +#define GLPMCFG_LPMRCNT GLPMCFG_LPMRCNT_Msk // LPM retry count +#define GLPMCFG_SNDLPM_Pos (24U) +#define GLPMCFG_SNDLPM_Msk (0x1UL << GLPMCFG_SNDLPM_Pos) // 0x01000000 +#define GLPMCFG_SNDLPM GLPMCFG_SNDLPM_Msk // Send LPM transaction +#define GLPMCFG_LPMRCNTSTS_Pos (25U) +#define GLPMCFG_LPMRCNTSTS_Msk (0x7UL << GLPMCFG_LPMRCNTSTS_Pos) // 0x0E000000 +#define GLPMCFG_LPMRCNTSTS GLPMCFG_LPMRCNTSTS_Msk // LPM retry count status +#define GLPMCFG_ENBESL_Pos (28U) +#define GLPMCFG_ENBESL_Msk (0x1UL << GLPMCFG_ENBESL_Pos) // 0x10000000 +#define GLPMCFG_ENBESL GLPMCFG_ENBESL_Msk // Enable best effort service latency + +/******************** Bit definition for DIEPEACHMSK1 register ********************/ +#define DIEPEACHMSK1_XFRCM_Pos (0U) +#define DIEPEACHMSK1_XFRCM_Msk (0x1UL << DIEPEACHMSK1_XFRCM_Pos) // 0x00000001 +#define DIEPEACHMSK1_XFRCM DIEPEACHMSK1_XFRCM_Msk // Transfer completed interrupt mask +#define DIEPEACHMSK1_EPDM_Pos (1U) +#define DIEPEACHMSK1_EPDM_Msk (0x1UL << DIEPEACHMSK1_EPDM_Pos) // 0x00000002 +#define DIEPEACHMSK1_EPDM DIEPEACHMSK1_EPDM_Msk // Endpoint disabled interrupt mask +#define DIEPEACHMSK1_TOM_Pos (3U) +#define DIEPEACHMSK1_TOM_Msk (0x1UL << DIEPEACHMSK1_TOM_Pos) // 0x00000008 +#define DIEPEACHMSK1_TOM DIEPEACHMSK1_TOM_Msk // Timeout condition mask (nonisochronous endpoints) +#define DIEPEACHMSK1_ITTXFEMSK_Pos (4U) +#define DIEPEACHMSK1_ITTXFEMSK_Msk (0x1UL << DIEPEACHMSK1_ITTXFEMSK_Pos) // 0x00000010 +#define DIEPEACHMSK1_ITTXFEMSK DIEPEACHMSK1_ITTXFEMSK_Msk // IN token received when TxFIFO empty mask +#define DIEPEACHMSK1_INEPNMM_Pos (5U) +#define DIEPEACHMSK1_INEPNMM_Msk (0x1UL << DIEPEACHMSK1_INEPNMM_Pos) // 0x00000020 +#define DIEPEACHMSK1_INEPNMM DIEPEACHMSK1_INEPNMM_Msk // IN token received with EP mismatch mask +#define DIEPEACHMSK1_INEPNEM_Pos (6U) +#define DIEPEACHMSK1_INEPNEM_Msk (0x1UL << DIEPEACHMSK1_INEPNEM_Pos) // 0x00000040 +#define DIEPEACHMSK1_INEPNEM DIEPEACHMSK1_INEPNEM_Msk // IN endpoint NAK effective mask +#define DIEPEACHMSK1_TXFURM_Pos (8U) +#define DIEPEACHMSK1_TXFURM_Msk (0x1UL << DIEPEACHMSK1_TXFURM_Pos) // 0x00000100 +#define DIEPEACHMSK1_TXFURM DIEPEACHMSK1_TXFURM_Msk // FIFO underrun mask +#define DIEPEACHMSK1_BIM_Pos (9U) +#define DIEPEACHMSK1_BIM_Msk (0x1UL << DIEPEACHMSK1_BIM_Pos) // 0x00000200 +#define DIEPEACHMSK1_BIM DIEPEACHMSK1_BIM_Msk // BNA interrupt mask +#define DIEPEACHMSK1_NAKM_Pos (13U) +#define DIEPEACHMSK1_NAKM_Msk (0x1UL << DIEPEACHMSK1_NAKM_Pos) // 0x00002000 +#define DIEPEACHMSK1_NAKM DIEPEACHMSK1_NAKM_Msk // NAK interrupt mask + +/******************** Bit definition for HPRT register ********************/ +#define HPRT_PCSTS_Pos (0U) +#define HPRT_PCSTS_Msk (0x1UL << HPRT_PCSTS_Pos) // 0x00000001 +#define HPRT_PCSTS HPRT_PCSTS_Msk // Port connect status +#define HPRT_PCDET_Pos (1U) +#define HPRT_PCDET_Msk (0x1UL << HPRT_PCDET_Pos) // 0x00000002 +#define HPRT_PCDET HPRT_PCDET_Msk // Port connect detected +#define HPRT_PENA_Pos (2U) +#define HPRT_PENA_Msk (0x1UL << HPRT_PENA_Pos) // 0x00000004 +#define HPRT_PENA HPRT_PENA_Msk // Port enable +#define HPRT_PENCHNG_Pos (3U) +#define HPRT_PENCHNG_Msk (0x1UL << HPRT_PENCHNG_Pos) // 0x00000008 +#define HPRT_PENCHNG HPRT_PENCHNG_Msk // Port enable/disable change +#define HPRT_POCA_Pos (4U) +#define HPRT_POCA_Msk (0x1UL << HPRT_POCA_Pos) // 0x00000010 +#define HPRT_POCA HPRT_POCA_Msk // Port overcurrent active +#define HPRT_POCCHNG_Pos (5U) +#define HPRT_POCCHNG_Msk (0x1UL << HPRT_POCCHNG_Pos) // 0x00000020 +#define HPRT_POCCHNG HPRT_POCCHNG_Msk // Port overcurrent change +#define HPRT_PRES_Pos (6U) +#define HPRT_PRES_Msk (0x1UL << HPRT_PRES_Pos) // 0x00000040 +#define HPRT_PRES HPRT_PRES_Msk // Port resume +#define HPRT_PSUSP_Pos (7U) +#define HPRT_PSUSP_Msk (0x1UL << HPRT_PSUSP_Pos) // 0x00000080 +#define HPRT_PSUSP HPRT_PSUSP_Msk // Port suspend +#define HPRT_PRST_Pos (8U) +#define HPRT_PRST_Msk (0x1UL << HPRT_PRST_Pos) // 0x00000100 +#define HPRT_PRST HPRT_PRST_Msk // Port reset + +#define HPRT_PLSTS_Pos (10U) +#define HPRT_PLSTS_Msk (0x3UL << HPRT_PLSTS_Pos) // 0x00000C00 +#define HPRT_PLSTS HPRT_PLSTS_Msk // Port line status +#define HPRT_PLSTS_0 (0x1UL << HPRT_PLSTS_Pos) // 0x00000400 +#define HPRT_PLSTS_1 (0x2UL << HPRT_PLSTS_Pos) // 0x00000800 +#define HPRT_PPWR_Pos (12U) +#define HPRT_PPWR_Msk (0x1UL << HPRT_PPWR_Pos) // 0x00001000 +#define HPRT_PPWR HPRT_PPWR_Msk // Port power + +#define HPRT_PTCTL_Pos (13U) +#define HPRT_PTCTL_Msk (0xFUL << HPRT_PTCTL_Pos) // 0x0001E000 +#define HPRT_PTCTL HPRT_PTCTL_Msk // Port test control +#define HPRT_PTCTL_0 (0x1UL << HPRT_PTCTL_Pos) // 0x00002000 +#define HPRT_PTCTL_1 (0x2UL << HPRT_PTCTL_Pos) // 0x00004000 +#define HPRT_PTCTL_2 (0x4UL << HPRT_PTCTL_Pos) // 0x00008000 +#define HPRT_PTCTL_3 (0x8UL << HPRT_PTCTL_Pos) // 0x00010000 + +#define HPRT_PSPD_Pos (17U) +#define HPRT_PSPD_Msk (0x3UL << HPRT_PSPD_Pos) // 0x00060000 +#define HPRT_PSPD HPRT_PSPD_Msk // Port speed +#define HPRT_PSPD_0 (0x1UL << HPRT_PSPD_Pos) // 0x00020000 +#define HPRT_PSPD_1 (0x2UL << HPRT_PSPD_Pos) // 0x00040000 + +/******************** Bit definition for DOEPEACHMSK1 register ********************/ +#define DOEPEACHMSK1_XFRCM_Pos (0U) +#define DOEPEACHMSK1_XFRCM_Msk (0x1UL << DOEPEACHMSK1_XFRCM_Pos) // 0x00000001 +#define DOEPEACHMSK1_XFRCM DOEPEACHMSK1_XFRCM_Msk // Transfer completed interrupt mask +#define DOEPEACHMSK1_EPDM_Pos (1U) +#define DOEPEACHMSK1_EPDM_Msk (0x1UL << DOEPEACHMSK1_EPDM_Pos) // 0x00000002 +#define DOEPEACHMSK1_EPDM DOEPEACHMSK1_EPDM_Msk // Endpoint disabled interrupt mask +#define DOEPEACHMSK1_TOM_Pos (3U) +#define DOEPEACHMSK1_TOM_Msk (0x1UL << DOEPEACHMSK1_TOM_Pos) // 0x00000008 +#define DOEPEACHMSK1_TOM DOEPEACHMSK1_TOM_Msk // Timeout condition mask +#define DOEPEACHMSK1_ITTXFEMSK_Pos (4U) +#define DOEPEACHMSK1_ITTXFEMSK_Msk (0x1UL << DOEPEACHMSK1_ITTXFEMSK_Pos) // 0x00000010 +#define DOEPEACHMSK1_ITTXFEMSK DOEPEACHMSK1_ITTXFEMSK_Msk // IN token received when TxFIFO empty mask +#define DOEPEACHMSK1_INEPNMM_Pos (5U) +#define DOEPEACHMSK1_INEPNMM_Msk (0x1UL << DOEPEACHMSK1_INEPNMM_Pos) // 0x00000020 +#define DOEPEACHMSK1_INEPNMM DOEPEACHMSK1_INEPNMM_Msk // IN token received with EP mismatch mask +#define DOEPEACHMSK1_INEPNEM_Pos (6U) +#define DOEPEACHMSK1_INEPNEM_Msk (0x1UL << DOEPEACHMSK1_INEPNEM_Pos) // 0x00000040 +#define DOEPEACHMSK1_INEPNEM DOEPEACHMSK1_INEPNEM_Msk // IN endpoint NAK effective mask +#define DOEPEACHMSK1_TXFURM_Pos (8U) +#define DOEPEACHMSK1_TXFURM_Msk (0x1UL << DOEPEACHMSK1_TXFURM_Pos) // 0x00000100 +#define DOEPEACHMSK1_TXFURM DOEPEACHMSK1_TXFURM_Msk // OUT packet error mask +#define DOEPEACHMSK1_BIM_Pos (9U) +#define DOEPEACHMSK1_BIM_Msk (0x1UL << DOEPEACHMSK1_BIM_Pos) // 0x00000200 +#define DOEPEACHMSK1_BIM DOEPEACHMSK1_BIM_Msk // BNA interrupt mask +#define DOEPEACHMSK1_BERRM_Pos (12U) +#define DOEPEACHMSK1_BERRM_Msk (0x1UL << DOEPEACHMSK1_BERRM_Pos) // 0x00001000 +#define DOEPEACHMSK1_BERRM DOEPEACHMSK1_BERRM_Msk // Bubble error interrupt mask +#define DOEPEACHMSK1_NAKM_Pos (13U) +#define DOEPEACHMSK1_NAKM_Msk (0x1UL << DOEPEACHMSK1_NAKM_Pos) // 0x00002000 +#define DOEPEACHMSK1_NAKM DOEPEACHMSK1_NAKM_Msk // NAK interrupt mask +#define DOEPEACHMSK1_NYETM_Pos (14U) +#define DOEPEACHMSK1_NYETM_Msk (0x1UL << DOEPEACHMSK1_NYETM_Pos) // 0x00004000 +#define DOEPEACHMSK1_NYETM DOEPEACHMSK1_NYETM_Msk // NYET interrupt mask + +/******************** Bit definition for HPTXFSIZ register ********************/ +#define HPTXFSIZ_PTXSA_Pos (0U) +#define HPTXFSIZ_PTXSA_Msk (0xFFFFUL << HPTXFSIZ_PTXSA_Pos) // 0x0000FFFF +#define HPTXFSIZ_PTXSA HPTXFSIZ_PTXSA_Msk // Host periodic TxFIFO start address +#define HPTXFSIZ_PTXFD_Pos (16U) +#define HPTXFSIZ_PTXFD_Msk (0xFFFFUL << HPTXFSIZ_PTXFD_Pos) // 0xFFFF0000 +#define HPTXFSIZ_PTXFD HPTXFSIZ_PTXFD_Msk // Host periodic TxFIFO depth + +/******************** Bit definition for DIEPCTL register ********************/ +#define DIEPCTL_MPSIZ_Pos (0U) +#define DIEPCTL_MPSIZ_Msk (0x7FFUL << DIEPCTL_MPSIZ_Pos) // 0x000007FF +#define DIEPCTL_MPSIZ DIEPCTL_MPSIZ_Msk // Maximum packet size +#define DIEPCTL_USBAEP_Pos (15U) +#define DIEPCTL_USBAEP_Msk (0x1UL << DIEPCTL_USBAEP_Pos) // 0x00008000 +#define DIEPCTL_USBAEP DIEPCTL_USBAEP_Msk // USB active endpoint +#define DIEPCTL_EONUM_DPID_Pos (16U) +#define DIEPCTL_EONUM_DPID_Msk (0x1UL << DIEPCTL_EONUM_DPID_Pos) // 0x00010000 +#define DIEPCTL_EONUM_DPID DIEPCTL_EONUM_DPID_Msk // Even/odd frame +#define DIEPCTL_NAKSTS_Pos (17U) +#define DIEPCTL_NAKSTS_Msk (0x1UL << DIEPCTL_NAKSTS_Pos) // 0x00020000 +#define DIEPCTL_NAKSTS DIEPCTL_NAKSTS_Msk // NAK status + +#define DIEPCTL_EPTYP_Pos (18U) +#define DIEPCTL_EPTYP_Msk (0x3UL << DIEPCTL_EPTYP_Pos) // 0x000C0000 +#define DIEPCTL_EPTYP DIEPCTL_EPTYP_Msk // Endpoint type +#define DIEPCTL_EPTYP_0 (0x1UL << DIEPCTL_EPTYP_Pos) // 0x00040000 +#define DIEPCTL_EPTYP_1 (0x2UL << DIEPCTL_EPTYP_Pos) // 0x00080000 +#define DIEPCTL_STALL_Pos (21U) +#define DIEPCTL_STALL_Msk (0x1UL << DIEPCTL_STALL_Pos) // 0x00200000 +#define DIEPCTL_STALL DIEPCTL_STALL_Msk // STALL handshake + +#define DIEPCTL_TXFNUM_Pos (22U) +#define DIEPCTL_TXFNUM_Msk (0xFUL << DIEPCTL_TXFNUM_Pos) // 0x03C00000 +#define DIEPCTL_TXFNUM DIEPCTL_TXFNUM_Msk // TxFIFO number +#define DIEPCTL_TXFNUM_0 (0x1UL << DIEPCTL_TXFNUM_Pos) // 0x00400000 +#define DIEPCTL_TXFNUM_1 (0x2UL << DIEPCTL_TXFNUM_Pos) // 0x00800000 +#define DIEPCTL_TXFNUM_2 (0x4UL << DIEPCTL_TXFNUM_Pos) // 0x01000000 +#define DIEPCTL_TXFNUM_3 (0x8UL << DIEPCTL_TXFNUM_Pos) // 0x02000000 +#define DIEPCTL_CNAK_Pos (26U) +#define DIEPCTL_CNAK_Msk (0x1UL << DIEPCTL_CNAK_Pos) // 0x04000000 +#define DIEPCTL_CNAK DIEPCTL_CNAK_Msk // Clear NAK +#define DIEPCTL_SNAK_Pos (27U) +#define DIEPCTL_SNAK_Msk (0x1UL << DIEPCTL_SNAK_Pos) // 0x08000000 +#define DIEPCTL_SNAK DIEPCTL_SNAK_Msk // Set NAK +#define DIEPCTL_SD0PID_SEVNFRM_Pos (28U) +#define DIEPCTL_SD0PID_SEVNFRM_Msk (0x1UL << DIEPCTL_SD0PID_SEVNFRM_Pos) // 0x10000000 +#define DIEPCTL_SD0PID_SEVNFRM DIEPCTL_SD0PID_SEVNFRM_Msk // Set DATA0 PID +#define DIEPCTL_SODDFRM_Pos (29U) +#define DIEPCTL_SODDFRM_Msk (0x1UL << DIEPCTL_SODDFRM_Pos) // 0x20000000 +#define DIEPCTL_SODDFRM DIEPCTL_SODDFRM_Msk // Set odd frame +#define DIEPCTL_EPDIS_Pos (30U) +#define DIEPCTL_EPDIS_Msk (0x1UL << DIEPCTL_EPDIS_Pos) // 0x40000000 +#define DIEPCTL_EPDIS DIEPCTL_EPDIS_Msk // Endpoint disable +#define DIEPCTL_EPENA_Pos (31U) +#define DIEPCTL_EPENA_Msk (0x1UL << DIEPCTL_EPENA_Pos) // 0x80000000 +#define DIEPCTL_EPENA DIEPCTL_EPENA_Msk // Endpoint enable + +/******************** Bit definition for HCCHAR register ********************/ +#define HCCHAR_MPSIZ_Pos (0U) +#define HCCHAR_MPSIZ_Msk (0x7FFUL << HCCHAR_MPSIZ_Pos) // 0x000007FF +#define HCCHAR_MPSIZ HCCHAR_MPSIZ_Msk // Maximum packet size + +#define HCCHAR_EPNUM_Pos (11U) +#define HCCHAR_EPNUM_Msk (0xFUL << HCCHAR_EPNUM_Pos) // 0x00007800 +#define HCCHAR_EPNUM HCCHAR_EPNUM_Msk // Endpoint number +#define HCCHAR_EPNUM_0 (0x1UL << HCCHAR_EPNUM_Pos) // 0x00000800 +#define HCCHAR_EPNUM_1 (0x2UL << HCCHAR_EPNUM_Pos) // 0x00001000 +#define HCCHAR_EPNUM_2 (0x4UL << HCCHAR_EPNUM_Pos) // 0x00002000 +#define HCCHAR_EPNUM_3 (0x8UL << HCCHAR_EPNUM_Pos) // 0x00004000 +#define HCCHAR_EPDIR_Pos (15U) +#define HCCHAR_EPDIR_Msk (0x1UL << HCCHAR_EPDIR_Pos) // 0x00008000 +#define HCCHAR_EPDIR HCCHAR_EPDIR_Msk // Endpoint direction +#define HCCHAR_LSDEV_Pos (17U) +#define HCCHAR_LSDEV_Msk (0x1UL << HCCHAR_LSDEV_Pos) // 0x00020000 +#define HCCHAR_LSDEV HCCHAR_LSDEV_Msk // Low-speed device + +#define HCCHAR_EPTYP_Pos (18U) +#define HCCHAR_EPTYP_Msk (0x3UL << HCCHAR_EPTYP_Pos) // 0x000C0000 +#define HCCHAR_EPTYP HCCHAR_EPTYP_Msk // Endpoint type +#define HCCHAR_EPTYP_0 (0x1UL << HCCHAR_EPTYP_Pos) // 0x00040000 +#define HCCHAR_EPTYP_1 (0x2UL << HCCHAR_EPTYP_Pos) // 0x00080000 + +#define HCCHAR_MC_Pos (20U) +#define HCCHAR_MC_Msk (0x3UL << HCCHAR_MC_Pos) // 0x00300000 +#define HCCHAR_MC HCCHAR_MC_Msk // Multi Count (MC) / Error Count (EC) +#define HCCHAR_MC_0 (0x1UL << HCCHAR_MC_Pos) // 0x00100000 +#define HCCHAR_MC_1 (0x2UL << HCCHAR_MC_Pos) // 0x00200000 + +#define HCCHAR_DAD_Pos (22U) +#define HCCHAR_DAD_Msk (0x7FUL << HCCHAR_DAD_Pos) // 0x1FC00000 +#define HCCHAR_DAD HCCHAR_DAD_Msk // Device address +#define HCCHAR_DAD_0 (0x01UL << HCCHAR_DAD_Pos) // 0x00400000 +#define HCCHAR_DAD_1 (0x02UL << HCCHAR_DAD_Pos) // 0x00800000 +#define HCCHAR_DAD_2 (0x04UL << HCCHAR_DAD_Pos) // 0x01000000 +#define HCCHAR_DAD_3 (0x08UL << HCCHAR_DAD_Pos) // 0x02000000 +#define HCCHAR_DAD_4 (0x10UL << HCCHAR_DAD_Pos) // 0x04000000 +#define HCCHAR_DAD_5 (0x20UL << HCCHAR_DAD_Pos) // 0x08000000 +#define HCCHAR_DAD_6 (0x40UL << HCCHAR_DAD_Pos) // 0x10000000 +#define HCCHAR_ODDFRM_Pos (29U) +#define HCCHAR_ODDFRM_Msk (0x1UL << HCCHAR_ODDFRM_Pos) // 0x20000000 +#define HCCHAR_ODDFRM HCCHAR_ODDFRM_Msk // Odd frame +#define HCCHAR_CHDIS_Pos (30U) +#define HCCHAR_CHDIS_Msk (0x1UL << HCCHAR_CHDIS_Pos) // 0x40000000 +#define HCCHAR_CHDIS HCCHAR_CHDIS_Msk // Channel disable +#define HCCHAR_CHENA_Pos (31U) +#define HCCHAR_CHENA_Msk (0x1UL << HCCHAR_CHENA_Pos) // 0x80000000 +#define HCCHAR_CHENA HCCHAR_CHENA_Msk // Channel enable + +/******************** Bit definition for HCSPLT register ********************/ + +#define HCSPLT_PRTADDR_Pos (0U) +#define HCSPLT_PRTADDR_Msk (0x7FUL << HCSPLT_PRTADDR_Pos) // 0x0000007F +#define HCSPLT_PRTADDR HCSPLT_PRTADDR_Msk // Port address +#define HCSPLT_PRTADDR_0 (0x01UL << HCSPLT_PRTADDR_Pos) // 0x00000001 +#define HCSPLT_PRTADDR_1 (0x02UL << HCSPLT_PRTADDR_Pos) // 0x00000002 +#define HCSPLT_PRTADDR_2 (0x04UL << HCSPLT_PRTADDR_Pos) // 0x00000004 +#define HCSPLT_PRTADDR_3 (0x08UL << HCSPLT_PRTADDR_Pos) // 0x00000008 +#define HCSPLT_PRTADDR_4 (0x10UL << HCSPLT_PRTADDR_Pos) // 0x00000010 +#define HCSPLT_PRTADDR_5 (0x20UL << HCSPLT_PRTADDR_Pos) // 0x00000020 +#define HCSPLT_PRTADDR_6 (0x40UL << HCSPLT_PRTADDR_Pos) // 0x00000040 + +#define HCSPLT_HUBADDR_Pos (7U) +#define HCSPLT_HUBADDR_Msk (0x7FUL << HCSPLT_HUBADDR_Pos) // 0x00003F80 +#define HCSPLT_HUBADDR HCSPLT_HUBADDR_Msk // Hub address +#define HCSPLT_HUBADDR_0 (0x01UL << HCSPLT_HUBADDR_Pos) // 0x00000080 +#define HCSPLT_HUBADDR_1 (0x02UL << HCSPLT_HUBADDR_Pos) // 0x00000100 +#define HCSPLT_HUBADDR_2 (0x04UL << HCSPLT_HUBADDR_Pos) // 0x00000200 +#define HCSPLT_HUBADDR_3 (0x08UL << HCSPLT_HUBADDR_Pos) // 0x00000400 +#define HCSPLT_HUBADDR_4 (0x10UL << HCSPLT_HUBADDR_Pos) // 0x00000800 +#define HCSPLT_HUBADDR_5 (0x20UL << HCSPLT_HUBADDR_Pos) // 0x00001000 +#define HCSPLT_HUBADDR_6 (0x40UL << HCSPLT_HUBADDR_Pos) // 0x00002000 + +#define HCSPLT_XACTPOS_Pos (14U) +#define HCSPLT_XACTPOS_Msk (0x3UL << HCSPLT_XACTPOS_Pos) // 0x0000C000 +#define HCSPLT_XACTPOS HCSPLT_XACTPOS_Msk // XACTPOS +#define HCSPLT_XACTPOS_0 (0x1UL << HCSPLT_XACTPOS_Pos) // 0x00004000 +#define HCSPLT_XACTPOS_1 (0x2UL << HCSPLT_XACTPOS_Pos) // 0x00008000 +#define HCSPLT_COMPLSPLT_Pos (16U) +#define HCSPLT_COMPLSPLT_Msk (0x1UL << HCSPLT_COMPLSPLT_Pos) // 0x00010000 +#define HCSPLT_COMPLSPLT HCSPLT_COMPLSPLT_Msk // Do complete split +#define HCSPLT_SPLITEN_Pos (31U) +#define HCSPLT_SPLITEN_Msk (0x1UL << HCSPLT_SPLITEN_Pos) // 0x80000000 +#define HCSPLT_SPLITEN HCSPLT_SPLITEN_Msk // Split enable + +/******************** Bit definition for HCINT register ********************/ +#define HCINT_XFRC_Pos (0U) +#define HCINT_XFRC_Msk (0x1UL << HCINT_XFRC_Pos) // 0x00000001 +#define HCINT_XFRC HCINT_XFRC_Msk // Transfer completed +#define HCINT_CHH_Pos (1U) +#define HCINT_CHH_Msk (0x1UL << HCINT_CHH_Pos) // 0x00000002 +#define HCINT_CHH HCINT_CHH_Msk // Channel halted +#define HCINT_AHBERR_Pos (2U) +#define HCINT_AHBERR_Msk (0x1UL << HCINT_AHBERR_Pos) // 0x00000004 +#define HCINT_AHBERR HCINT_AHBERR_Msk // AHB error +#define HCINT_STALL_Pos (3U) +#define HCINT_STALL_Msk (0x1UL << HCINT_STALL_Pos) // 0x00000008 +#define HCINT_STALL HCINT_STALL_Msk // STALL response received interrupt +#define HCINT_NAK_Pos (4U) +#define HCINT_NAK_Msk (0x1UL << HCINT_NAK_Pos) // 0x00000010 +#define HCINT_NAK HCINT_NAK_Msk // NAK response received interrupt +#define HCINT_ACK_Pos (5U) +#define HCINT_ACK_Msk (0x1UL << HCINT_ACK_Pos) // 0x00000020 +#define HCINT_ACK HCINT_ACK_Msk // ACK response received/transmitted interrupt +#define HCINT_NYET_Pos (6U) +#define HCINT_NYET_Msk (0x1UL << HCINT_NYET_Pos) // 0x00000040 +#define HCINT_NYET HCINT_NYET_Msk // Response received interrupt +#define HCINT_TXERR_Pos (7U) +#define HCINT_TXERR_Msk (0x1UL << HCINT_TXERR_Pos) // 0x00000080 +#define HCINT_TXERR HCINT_TXERR_Msk // Transaction error +#define HCINT_BBERR_Pos (8U) +#define HCINT_BBERR_Msk (0x1UL << HCINT_BBERR_Pos) // 0x00000100 +#define HCINT_BBERR HCINT_BBERR_Msk // Babble error +#define HCINT_FRMOR_Pos (9U) +#define HCINT_FRMOR_Msk (0x1UL << HCINT_FRMOR_Pos) // 0x00000200 +#define HCINT_FRMOR HCINT_FRMOR_Msk // Frame overrun +#define HCINT_DTERR_Pos (10U) +#define HCINT_DTERR_Msk (0x1UL << HCINT_DTERR_Pos) // 0x00000400 +#define HCINT_DTERR HCINT_DTERR_Msk // Data toggle error + +/******************** Bit definition for DIEPINT register ********************/ +#define DIEPINT_XFRC_Pos (0U) +#define DIEPINT_XFRC_Msk (0x1UL << DIEPINT_XFRC_Pos) // 0x00000001 +#define DIEPINT_XFRC DIEPINT_XFRC_Msk // Transfer completed interrupt +#define DIEPINT_EPDISD_Pos (1U) +#define DIEPINT_EPDISD_Msk (0x1UL << DIEPINT_EPDISD_Pos) // 0x00000002 +#define DIEPINT_EPDISD DIEPINT_EPDISD_Msk // Endpoint disabled interrupt +#define DIEPINT_AHBERR_Pos (2U) +#define DIEPINT_AHBERR_Msk (0x1UL << DIEPINT_AHBERR_Pos) // 0x00000004 +#define DIEPINT_AHBERR DIEPINT_AHBERR_Msk // AHB Error (AHBErr) during an IN transaction +#define DIEPINT_TOC_Pos (3U) +#define DIEPINT_TOC_Msk (0x1UL << DIEPINT_TOC_Pos) // 0x00000008 +#define DIEPINT_TOC DIEPINT_TOC_Msk // Timeout condition +#define DIEPINT_ITTXFE_Pos (4U) +#define DIEPINT_ITTXFE_Msk (0x1UL << DIEPINT_ITTXFE_Pos) // 0x00000010 +#define DIEPINT_ITTXFE DIEPINT_ITTXFE_Msk // IN token received when TxFIFO is empty +#define DIEPINT_INEPNM_Pos (5U) +#define DIEPINT_INEPNM_Msk (0x1UL << DIEPINT_INEPNM_Pos) // 0x00000020 +#define DIEPINT_INEPNM DIEPINT_INEPNM_Msk // IN token received with EP mismatch +#define DIEPINT_INEPNE_Pos (6U) +#define DIEPINT_INEPNE_Msk (0x1UL << DIEPINT_INEPNE_Pos) // 0x00000040 +#define DIEPINT_INEPNE DIEPINT_INEPNE_Msk // IN endpoint NAK effective +#define DIEPINT_TXFE_Pos (7U) +#define DIEPINT_TXFE_Msk (0x1UL << DIEPINT_TXFE_Pos) // 0x00000080 +#define DIEPINT_TXFE DIEPINT_TXFE_Msk // Transmit FIFO empty +#define DIEPINT_TXFIFOUDRN_Pos (8U) +#define DIEPINT_TXFIFOUDRN_Msk (0x1UL << DIEPINT_TXFIFOUDRN_Pos) // 0x00000100 +#define DIEPINT_TXFIFOUDRN DIEPINT_TXFIFOUDRN_Msk // Transmit Fifo Underrun +#define DIEPINT_BNA_Pos (9U) +#define DIEPINT_BNA_Msk (0x1UL << DIEPINT_BNA_Pos) // 0x00000200 +#define DIEPINT_BNA DIEPINT_BNA_Msk // Buffer not available interrupt +#define DIEPINT_PKTDRPSTS_Pos (11U) +#define DIEPINT_PKTDRPSTS_Msk (0x1UL << DIEPINT_PKTDRPSTS_Pos) // 0x00000800 +#define DIEPINT_PKTDRPSTS DIEPINT_PKTDRPSTS_Msk // Packet dropped status +#define DIEPINT_BERR_Pos (12U) +#define DIEPINT_BERR_Msk (0x1UL << DIEPINT_BERR_Pos) // 0x00001000 +#define DIEPINT_BERR DIEPINT_BERR_Msk // Babble error interrupt +#define DIEPINT_NAK_Pos (13U) +#define DIEPINT_NAK_Msk (0x1UL << DIEPINT_NAK_Pos) // 0x00002000 +#define DIEPINT_NAK DIEPINT_NAK_Msk // NAK interrupt + +/******************** Bit definition for HCINTMSK register ********************/ +#define HCINTMSK_XFRCM_Pos (0U) +#define HCINTMSK_XFRCM_Msk (0x1UL << HCINTMSK_XFRCM_Pos) // 0x00000001 +#define HCINTMSK_XFRCM HCINTMSK_XFRCM_Msk // Transfer completed mask +#define HCINTMSK_CHHM_Pos (1U) +#define HCINTMSK_CHHM_Msk (0x1UL << HCINTMSK_CHHM_Pos) // 0x00000002 +#define HCINTMSK_CHHM HCINTMSK_CHHM_Msk // Channel halted mask +#define HCINTMSK_AHBERR_Pos (2U) +#define HCINTMSK_AHBERR_Msk (0x1UL << HCINTMSK_AHBERR_Pos) // 0x00000004 +#define HCINTMSK_AHBERR HCINTMSK_AHBERR_Msk // AHB error +#define HCINTMSK_STALLM_Pos (3U) +#define HCINTMSK_STALLM_Msk (0x1UL << HCINTMSK_STALLM_Pos) // 0x00000008 +#define HCINTMSK_STALLM HCINTMSK_STALLM_Msk // STALL response received interrupt mask +#define HCINTMSK_NAKM_Pos (4U) +#define HCINTMSK_NAKM_Msk (0x1UL << HCINTMSK_NAKM_Pos) // 0x00000010 +#define HCINTMSK_NAKM HCINTMSK_NAKM_Msk // NAK response received interrupt mask +#define HCINTMSK_ACKM_Pos (5U) +#define HCINTMSK_ACKM_Msk (0x1UL << HCINTMSK_ACKM_Pos) // 0x00000020 +#define HCINTMSK_ACKM HCINTMSK_ACKM_Msk // ACK response received/transmitted interrupt mask +#define HCINTMSK_NYET_Pos (6U) +#define HCINTMSK_NYET_Msk (0x1UL << HCINTMSK_NYET_Pos) // 0x00000040 +#define HCINTMSK_NYET HCINTMSK_NYET_Msk // response received interrupt mask +#define HCINTMSK_TXERRM_Pos (7U) +#define HCINTMSK_TXERRM_Msk (0x1UL << HCINTMSK_TXERRM_Pos) // 0x00000080 +#define HCINTMSK_TXERRM HCINTMSK_TXERRM_Msk // Transaction error mask +#define HCINTMSK_BBERRM_Pos (8U) +#define HCINTMSK_BBERRM_Msk (0x1UL << HCINTMSK_BBERRM_Pos) // 0x00000100 +#define HCINTMSK_BBERRM HCINTMSK_BBERRM_Msk // Babble error mask +#define HCINTMSK_FRMORM_Pos (9U) +#define HCINTMSK_FRMORM_Msk (0x1UL << HCINTMSK_FRMORM_Pos) // 0x00000200 +#define HCINTMSK_FRMORM HCINTMSK_FRMORM_Msk // Frame overrun mask +#define HCINTMSK_DTERRM_Pos (10U) +#define HCINTMSK_DTERRM_Msk (0x1UL << HCINTMSK_DTERRM_Pos) // 0x00000400 +#define HCINTMSK_DTERRM HCINTMSK_DTERRM_Msk // Data toggle error mask + +/******************** Bit definition for DIEPTSIZ register ********************/ + +#define DIEPTSIZ_XFRSIZ_Pos (0U) +#define DIEPTSIZ_XFRSIZ_Msk (0x7FFFFUL << DIEPTSIZ_XFRSIZ_Pos) // 0x0007FFFF +#define DIEPTSIZ_XFRSIZ DIEPTSIZ_XFRSIZ_Msk // Transfer size +#define DIEPTSIZ_PKTCNT_Pos (19U) +#define DIEPTSIZ_PKTCNT_Msk (0x3FFUL << DIEPTSIZ_PKTCNT_Pos) // 0x1FF80000 +#define DIEPTSIZ_PKTCNT DIEPTSIZ_PKTCNT_Msk // Packet count +#define DIEPTSIZ_MULCNT_Pos (29U) +#define DIEPTSIZ_MULCNT_Msk (0x3UL << DIEPTSIZ_MULCNT_Pos) // 0x60000000 +#define DIEPTSIZ_MULCNT DIEPTSIZ_MULCNT_Msk // Packet count + /******************** Bit definition for HCTSIZ register ********************/ +#define HCTSIZ_XFRSIZ_Pos (0U) +#define HCTSIZ_XFRSIZ_Msk (0x7FFFFUL << HCTSIZ_XFRSIZ_Pos) // 0x0007FFFF +#define HCTSIZ_XFRSIZ HCTSIZ_XFRSIZ_Msk // Transfer size +#define HCTSIZ_PKTCNT_Pos (19U) +#define HCTSIZ_PKTCNT_Msk (0x3FFUL << HCTSIZ_PKTCNT_Pos) // 0x1FF80000 +#define HCTSIZ_PKTCNT HCTSIZ_PKTCNT_Msk // Packet count +#define HCTSIZ_DOPING_Pos (31U) +#define HCTSIZ_DOPING_Msk (0x1UL << HCTSIZ_DOPING_Pos) // 0x80000000 +#define HCTSIZ_DOPING HCTSIZ_DOPING_Msk // Do PING +#define HCTSIZ_DPID_Pos (29U) +#define HCTSIZ_DPID_Msk (0x3UL << HCTSIZ_DPID_Pos) // 0x60000000 +#define HCTSIZ_DPID HCTSIZ_DPID_Msk // Data PID +#define HCTSIZ_DPID_0 (0x1UL << HCTSIZ_DPID_Pos) // 0x20000000 +#define HCTSIZ_DPID_1 (0x2UL << HCTSIZ_DPID_Pos) // 0x40000000 + +/******************** Bit definition for DIEPDMA register ********************/ +#define DIEPDMA_DMAADDR_Pos (0U) +#define DIEPDMA_DMAADDR_Msk (0xFFFFFFFFUL << DIEPDMA_DMAADDR_Pos) // 0xFFFFFFFF +#define DIEPDMA_DMAADDR DIEPDMA_DMAADDR_Msk // DMA address + +/******************** Bit definition for HCDMA register ********************/ +#define HCDMA_DMAADDR_Pos (0U) +#define HCDMA_DMAADDR_Msk (0xFFFFFFFFUL << HCDMA_DMAADDR_Pos) // 0xFFFFFFFF +#define HCDMA_DMAADDR HCDMA_DMAADDR_Msk // DMA address + + /******************** Bit definition for DTXFSTS register ********************/ +#define DTXFSTS_INEPTFSAV_Pos (0U) +#define DTXFSTS_INEPTFSAV_Msk (0xFFFFUL << DTXFSTS_INEPTFSAV_Pos) // 0x0000FFFF +#define DTXFSTS_INEPTFSAV DTXFSTS_INEPTFSAV_Msk // IN endpoint TxFIFO space available + + /******************** Bit definition for DIEPTXF register ********************/ +#define DIEPTXF_INEPTXSA_Pos (0U) +#define DIEPTXF_INEPTXSA_Msk (0xFFFFUL << DIEPTXF_INEPTXSA_Pos) // 0x0000FFFF +#define DIEPTXF_INEPTXSA DIEPTXF_INEPTXSA_Msk // IN endpoint FIFOx transmit RAM start address +#define DIEPTXF_INEPTXFD_Pos (16U) +#define DIEPTXF_INEPTXFD_Msk (0xFFFFUL << DIEPTXF_INEPTXFD_Pos) // 0xFFFF0000 +#define DIEPTXF_INEPTXFD DIEPTXF_INEPTXFD_Msk // IN endpoint TxFIFO depth + +/******************** Bit definition for DOEPCTL register ********************/ +#define DOEPCTL_MPSIZ_Pos (0U) +#define DOEPCTL_MPSIZ_Msk (0x7FFUL << DOEPCTL_MPSIZ_Pos) // 0x000007FF +#define DOEPCTL_MPSIZ DOEPCTL_MPSIZ_Msk // Maximum packet size //Bit 1 +#define DOEPCTL_USBAEP_Pos (15U) +#define DOEPCTL_USBAEP_Msk (0x1UL << DOEPCTL_USBAEP_Pos) // 0x00008000 +#define DOEPCTL_USBAEP DOEPCTL_USBAEP_Msk // USB active endpoint +#define DOEPCTL_NAKSTS_Pos (17U) +#define DOEPCTL_NAKSTS_Msk (0x1UL << DOEPCTL_NAKSTS_Pos) // 0x00020000 +#define DOEPCTL_NAKSTS DOEPCTL_NAKSTS_Msk // NAK status +#define DOEPCTL_SD0PID_SEVNFRM_Pos (28U) +#define DOEPCTL_SD0PID_SEVNFRM_Msk (0x1UL << DOEPCTL_SD0PID_SEVNFRM_Pos) // 0x10000000 +#define DOEPCTL_SD0PID_SEVNFRM DOEPCTL_SD0PID_SEVNFRM_Msk // Set DATA0 PID +#define DOEPCTL_SODDFRM_Pos (29U) +#define DOEPCTL_SODDFRM_Msk (0x1UL << DOEPCTL_SODDFRM_Pos) // 0x20000000 +#define DOEPCTL_SODDFRM DOEPCTL_SODDFRM_Msk // Set odd frame +#define DOEPCTL_EPTYP_Pos (18U) +#define DOEPCTL_EPTYP_Msk (0x3UL << DOEPCTL_EPTYP_Pos) // 0x000C0000 +#define DOEPCTL_EPTYP DOEPCTL_EPTYP_Msk // Endpoint type +#define DOEPCTL_EPTYP_0 (0x1UL << DOEPCTL_EPTYP_Pos) // 0x00040000 +#define DOEPCTL_EPTYP_1 (0x2UL << DOEPCTL_EPTYP_Pos) // 0x00080000 +#define DOEPCTL_SNPM_Pos (20U) +#define DOEPCTL_SNPM_Msk (0x1UL << DOEPCTL_SNPM_Pos) // 0x00100000 +#define DOEPCTL_SNPM DOEPCTL_SNPM_Msk // Snoop mode +#define DOEPCTL_STALL_Pos (21U) +#define DOEPCTL_STALL_Msk (0x1UL << DOEPCTL_STALL_Pos) // 0x00200000 +#define DOEPCTL_STALL DOEPCTL_STALL_Msk // STALL handshake +#define DOEPCTL_CNAK_Pos (26U) +#define DOEPCTL_CNAK_Msk (0x1UL << DOEPCTL_CNAK_Pos) // 0x04000000 +#define DOEPCTL_CNAK DOEPCTL_CNAK_Msk // Clear NAK +#define DOEPCTL_SNAK_Pos (27U) +#define DOEPCTL_SNAK_Msk (0x1UL << DOEPCTL_SNAK_Pos) // 0x08000000 +#define DOEPCTL_SNAK DOEPCTL_SNAK_Msk // Set NAK +#define DOEPCTL_EPDIS_Pos (30U) +#define DOEPCTL_EPDIS_Msk (0x1UL << DOEPCTL_EPDIS_Pos) // 0x40000000 +#define DOEPCTL_EPDIS DOEPCTL_EPDIS_Msk // Endpoint disable +#define DOEPCTL_EPENA_Pos (31U) +#define DOEPCTL_EPENA_Msk (0x1UL << DOEPCTL_EPENA_Pos) // 0x80000000 +#define DOEPCTL_EPENA DOEPCTL_EPENA_Msk // Endpoint enable + +/******************** Bit definition for DOEPINT register ********************/ +#define DOEPINT_XFRC_Pos (0U) +#define DOEPINT_XFRC_Msk (0x1UL << DOEPINT_XFRC_Pos) // 0x00000001 +#define DOEPINT_XFRC DOEPINT_XFRC_Msk // Transfer completed interrupt +#define DOEPINT_EPDISD_Pos (1U) +#define DOEPINT_EPDISD_Msk (0x1UL << DOEPINT_EPDISD_Pos) // 0x00000002 +#define DOEPINT_EPDISD DOEPINT_EPDISD_Msk // Endpoint disabled interrupt +#define DOEPINT_AHBERR_Pos (2U) +#define DOEPINT_AHBERR_Msk (0x1UL << DOEPINT_AHBERR_Pos) // 0x00000004 +#define DOEPINT_AHBERR DOEPINT_AHBERR_Msk // AHB Error (AHBErr) during an OUT transaction +#define DOEPINT_STUP_Pos (3U) +#define DOEPINT_STUP_Msk (0x1UL << DOEPINT_STUP_Pos) // 0x00000008 +#define DOEPINT_STUP DOEPINT_STUP_Msk // SETUP phase done +#define DOEPINT_OTEPDIS_Pos (4U) +#define DOEPINT_OTEPDIS_Msk (0x1UL << DOEPINT_OTEPDIS_Pos) // 0x00000010 +#define DOEPINT_OTEPDIS DOEPINT_OTEPDIS_Msk // OUT token received when endpoint disabled +#define DOEPINT_OTEPSPR_Pos (5U) +#define DOEPINT_OTEPSPR_Msk (0x1UL << DOEPINT_OTEPSPR_Pos) // 0x00000020 +#define DOEPINT_OTEPSPR DOEPINT_OTEPSPR_Msk // Status Phase Received For Control Write +#define DOEPINT_B2BSTUP_Pos (6U) +#define DOEPINT_B2BSTUP_Msk (0x1UL << DOEPINT_B2BSTUP_Pos) // 0x00000040 +#define DOEPINT_B2BSTUP DOEPINT_B2BSTUP_Msk // Back-to-back SETUP packets received +#define DOEPINT_OUTPKTERR_Pos (8U) +#define DOEPINT_OUTPKTERR_Msk (0x1UL << DOEPINT_OUTPKTERR_Pos) // 0x00000100 +#define DOEPINT_OUTPKTERR DOEPINT_OUTPKTERR_Msk // OUT packet error +#define DOEPINT_NAK_Pos (13U) +#define DOEPINT_NAK_Msk (0x1UL << DOEPINT_NAK_Pos) // 0x00002000 +#define DOEPINT_NAK DOEPINT_NAK_Msk // NAK Packet is transmitted by the device +#define DOEPINT_NYET_Pos (14U) +#define DOEPINT_NYET_Msk (0x1UL << DOEPINT_NYET_Pos) // 0x00004000 +#define DOEPINT_NYET DOEPINT_NYET_Msk // NYET interrupt +#define DOEPINT_STPKTRX_Pos (15U) +#define DOEPINT_STPKTRX_Msk (0x1UL << DOEPINT_STPKTRX_Pos) // 0x00008000 +#define DOEPINT_STPKTRX DOEPINT_STPKTRX_Msk // Setup Packet Received + +/******************** Bit definition for DOEPTSIZ register ********************/ +#define DOEPTSIZ_XFRSIZ_Pos (0U) +#define DOEPTSIZ_XFRSIZ_Msk (0x7FFFFUL << DOEPTSIZ_XFRSIZ_Pos) // 0x0007FFFF +#define DOEPTSIZ_XFRSIZ DOEPTSIZ_XFRSIZ_Msk // Transfer size +#define DOEPTSIZ_PKTCNT_Pos (19U) +#define DOEPTSIZ_PKTCNT_Msk (0x3FFUL << DOEPTSIZ_PKTCNT_Pos) // 0x1FF80000 +#define DOEPTSIZ_PKTCNT DOEPTSIZ_PKTCNT_Msk // Packet count + +#define DOEPTSIZ_STUPCNT_Pos (29U) +#define DOEPTSIZ_STUPCNT_Msk (0x3UL << DOEPTSIZ_STUPCNT_Pos) // 0x60000000 +#define DOEPTSIZ_STUPCNT DOEPTSIZ_STUPCNT_Msk // SETUP packet count +#define DOEPTSIZ_STUPCNT_0 (0x1UL << DOEPTSIZ_STUPCNT_Pos) // 0x20000000 +#define DOEPTSIZ_STUPCNT_1 (0x2UL << DOEPTSIZ_STUPCNT_Pos) // 0x40000000 + +/******************** Bit definition for PCGCTL register ********************/ +#define PCGCTL_IF_DEV_MODE TU_BIT(31) +#define PCGCTL_P2HD_PRT_SPD_MASK (0x3ul << 29) +#define PCGCTL_P2HD_PRT_SPD_SHIFT 29 +#define PCGCTL_P2HD_DEV_ENUM_SPD_MASK (0x3ul << 27) +#define PCGCTL_P2HD_DEV_ENUM_SPD_SHIFT 27 +#define PCGCTL_MAC_DEV_ADDR_MASK (0x7ful << 20) +#define PCGCTL_MAC_DEV_ADDR_SHIFT 20 +#define PCGCTL_MAX_TERMSEL TU_BIT(19) +#define PCGCTL_MAX_XCVRSELECT_MASK (0x3ul << 17) +#define PCGCTL_MAX_XCVRSELECT_SHIFT 17 +#define PCGCTL_PORT_POWER TU_BIT(16) +#define PCGCTL_PRT_CLK_SEL_MASK (0x3ul << 14) +#define PCGCTL_PRT_CLK_SEL_SHIFT 14 +#define PCGCTL_ESS_REG_RESTORED TU_BIT(13) +#define PCGCTL_EXTND_HIBER_SWITCH TU_BIT(12) +#define PCGCTL_EXTND_HIBER_PWRCLMP TU_BIT(11) +#define PCGCTL_ENBL_EXTND_HIBER TU_BIT(10) +#define PCGCTL_RESTOREMODE TU_BIT(9) +#define PCGCTL_RESETAFTSUSP TU_BIT(8) +#define PCGCTL_DEEP_SLEEP TU_BIT(7) +#define PCGCTL_PHY_IN_SLEEP TU_BIT(6) +#define PCGCTL_ENBL_SLEEP_GATING TU_BIT(5) +#define PCGCTL_RSTPDWNMODULE TU_BIT(3) +#define PCGCTL_PWRCLMP TU_BIT(2) +#define PCGCTL_GATEHCLK TU_BIT(1) +#define PCGCTL_STOPPCLK TU_BIT(0) + +#define PCGCTL1_TIMER (0x3ul << 1) +#define PCGCTL1_GATEEN TU_BIT(0) + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_xmc.h b/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_xmc.h new file mode 100644 index 00000000..63419abf --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_xmc.h @@ -0,0 +1,88 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 Rafael Silva (@perigoso) + * Copyright (c) 2021, Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _DWC2_XMC_H_ +#define _DWC2_XMC_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include "xmc_device.h" + +#define DWC2_EP_MAX 7 + +static const dwc2_controller_t _dwc2_controller[] = +{ + // Note: XMC has some custom control registers before DWC registers + { .reg_base = USB0_BASE, .irqnum = USB0_0_IRQn, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 2048 } +}; + +TU_ATTR_ALWAYS_INLINE +static inline void dwc2_dcd_int_enable(uint8_t rhport) +{ + NVIC_EnableIRQ(_dwc2_controller[rhport].irqnum); +} + +TU_ATTR_ALWAYS_INLINE +static inline void dwc2_dcd_int_disable (uint8_t rhport) +{ + NVIC_DisableIRQ(_dwc2_controller[rhport].irqnum); +} + +static inline void dwc2_remote_wakeup_delay(void) +{ + // try to delay for 1 ms +// uint32_t count = SystemCoreClock / 1000; +// while ( count-- ) __NOP(); +} + +// MCU specific PHY init, called BEFORE core reset +static inline void dwc2_phy_init(dwc2_regs_t * dwc2, uint8_t hs_phy_type) +{ + (void) dwc2; + (void) hs_phy_type; + + // Enable PHY + //USB->ROUTE = USB_ROUTE_PHYPEN; +} + +// MCU specific PHY update, it is called AFTER init() and core reset +static inline void dwc2_phy_update(dwc2_regs_t * dwc2, uint8_t hs_phy_type) +{ + (void) dwc2; + (void) hs_phy_type; + + // XMC Manual: turn around must be 5 (reset & default value) + // dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_TRDT_Msk) | (5u << GUSBCFG_TRDT_Pos); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test-devices/composite-stm32/lib/tinyusb/tusb.c b/test-devices/composite-stm32/lib/tinyusb/tusb.c new file mode 100644 index 00000000..0092267a --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/tusb.c @@ -0,0 +1,457 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if CFG_TUH_ENABLED || CFG_TUD_ENABLED + +#include "tusb.h" +#include "common/tusb_private.h" + +#if CFG_TUD_ENABLED +#include "device/usbd_pvt.h" +#endif + +#if CFG_TUH_ENABLED +#include "host/usbh_pvt.h" +#endif + +//--------------------------------------------------------------------+ +// Public API +//--------------------------------------------------------------------+ + +bool tusb_init(void) { + #if CFG_TUD_ENABLED && defined(TUD_OPT_RHPORT) + // init device stack CFG_TUSB_RHPORTx_MODE must be defined + TU_ASSERT ( tud_init(TUD_OPT_RHPORT) ); + #endif + + #if CFG_TUH_ENABLED && defined(TUH_OPT_RHPORT) + // init host stack CFG_TUSB_RHPORTx_MODE must be defined + TU_ASSERT( tuh_init(TUH_OPT_RHPORT) ); + #endif + + return true; +} + +bool tusb_inited(void) { + bool ret = false; + + #if CFG_TUD_ENABLED + ret = ret || tud_inited(); + #endif + + #if CFG_TUH_ENABLED + ret = ret || tuh_inited(); + #endif + + return ret; +} + +//--------------------------------------------------------------------+ +// Descriptor helper +//--------------------------------------------------------------------+ + +uint8_t const* tu_desc_find(uint8_t const* desc, uint8_t const* end, uint8_t byte1) { + while (desc + 1 < end) { + if (desc[1] == byte1) return desc; + desc += desc[DESC_OFFSET_LEN]; + } + return NULL; +} + +uint8_t const* tu_desc_find2(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2) { + while (desc + 2 < end) { + if (desc[1] == byte1 && desc[2] == byte2) return desc; + desc += desc[DESC_OFFSET_LEN]; + } + return NULL; +} + +uint8_t const* tu_desc_find3(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2, uint8_t byte3) { + while (desc + 3 < end) { + if (desc[1] == byte1 && desc[2] == byte2 && desc[3] == byte3) return desc; + desc += desc[DESC_OFFSET_LEN]; + } + return NULL; +} + +//--------------------------------------------------------------------+ +// Endpoint Helper for both Host and Device stack +//--------------------------------------------------------------------+ + +bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex) { + (void) mutex; + + // pre-check to help reducing mutex lock + TU_VERIFY((ep_state->busy == 0) && (ep_state->claimed == 0)); + (void) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER); + + // can only claim the endpoint if it is not busy and not claimed yet. + bool const available = (ep_state->busy == 0) && (ep_state->claimed == 0); + if (available) { + ep_state->claimed = 1; + } + + (void) osal_mutex_unlock(mutex); + return available; +} + +bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex) { + (void) mutex; + (void) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER); + + // can only release the endpoint if it is claimed and not busy + bool const ret = (ep_state->claimed == 1) && (ep_state->busy == 0); + if (ret) { + ep_state->claimed = 0; + } + + (void) osal_mutex_unlock(mutex); + return ret; +} + +bool tu_edpt_validate(tusb_desc_endpoint_t const* desc_ep, tusb_speed_t speed) { + uint16_t const max_packet_size = tu_edpt_packet_size(desc_ep); + TU_LOG2(" Open EP %02X with Size = %u\r\n", desc_ep->bEndpointAddress, max_packet_size); + + switch (desc_ep->bmAttributes.xfer) { + case TUSB_XFER_ISOCHRONOUS: { + uint16_t const spec_size = (speed == TUSB_SPEED_HIGH ? 1024 : 1023); + TU_ASSERT(max_packet_size <= spec_size); + break; + } + + case TUSB_XFER_BULK: + if (speed == TUSB_SPEED_HIGH) { + // Bulk highspeed must be EXACTLY 512 + TU_ASSERT(max_packet_size == 512); + } else { + // TODO Bulk fullspeed can only be 8, 16, 32, 64 + TU_ASSERT(max_packet_size <= 64); + } + break; + + case TUSB_XFER_INTERRUPT: { + uint16_t const spec_size = (speed == TUSB_SPEED_HIGH ? 1024 : 64); + TU_ASSERT(max_packet_size <= spec_size); + break; + } + + default: + return false; + } + + return true; +} + +void tu_edpt_bind_driver(uint8_t ep2drv[][2], tusb_desc_interface_t const* desc_itf, uint16_t desc_len, + uint8_t driver_id) { + uint8_t const* p_desc = (uint8_t const*) desc_itf; + uint8_t const* desc_end = p_desc + desc_len; + + while (p_desc < desc_end) { + if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) { + uint8_t const ep_addr = ((tusb_desc_endpoint_t const*) p_desc)->bEndpointAddress; + TU_LOG(2, " Bind EP %02x to driver id %u\r\n", ep_addr, driver_id); + ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)] = driver_id; + } + p_desc = tu_desc_next(p_desc); + } +} + +uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const* desc_itf, uint8_t itf_count, uint16_t max_len) { + uint8_t const* p_desc = (uint8_t const*) desc_itf; + uint16_t len = 0; + + while (itf_count--) { + // Next on interface desc + len += tu_desc_len(desc_itf); + p_desc = tu_desc_next(p_desc); + + while (len < max_len) { + // return on IAD regardless of itf count + if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE_ASSOCIATION) { + return len; + } + if ((tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) && + ((tusb_desc_interface_t const*) p_desc)->bAlternateSetting == 0) { + break; + } + + len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } + } + + return len; +} + +//--------------------------------------------------------------------+ +// Endpoint Stream Helper for both Host and Device stack +//--------------------------------------------------------------------+ + +bool tu_edpt_stream_init(tu_edpt_stream_t* s, bool is_host, bool is_tx, bool overwritable, + void* ff_buf, uint16_t ff_bufsize, uint8_t* ep_buf, uint16_t ep_bufsize) { + osal_mutex_t new_mutex = osal_mutex_create(&s->ff_mutexdef); + (void) new_mutex; + (void) is_tx; + + s->is_host = is_host; + tu_fifo_config(&s->ff, ff_buf, ff_bufsize, 1, overwritable); + tu_fifo_config_mutex(&s->ff, is_tx ? new_mutex : NULL, is_tx ? NULL : new_mutex); + + s->ep_buf = ep_buf; + s->ep_bufsize = ep_bufsize; + + return true; +} + +bool tu_edpt_stream_deinit(tu_edpt_stream_t* s) { + (void) s; + #if OSAL_MUTEX_REQUIRED + if (s->ff.mutex_wr) osal_mutex_delete(s->ff.mutex_wr); + if (s->ff.mutex_rd) osal_mutex_delete(s->ff.mutex_rd); + #endif + return true; +} + +TU_ATTR_ALWAYS_INLINE static inline +bool stream_claim(tu_edpt_stream_t* s) { + if (s->is_host) { + #if CFG_TUH_ENABLED + return usbh_edpt_claim(s->daddr, s->ep_addr); + #endif + } else { + #if CFG_TUD_ENABLED + return usbd_edpt_claim(s->rhport, s->ep_addr); + #endif + } + return false; +} + +TU_ATTR_ALWAYS_INLINE static inline +bool stream_xfer(tu_edpt_stream_t* s, uint16_t count) { + if (s->is_host) { + #if CFG_TUH_ENABLED + return usbh_edpt_xfer(s->daddr, s->ep_addr, count ? s->ep_buf : NULL, count); + #endif + } else { + #if CFG_TUD_ENABLED + return usbd_edpt_xfer(s->rhport, s->ep_addr, count ? s->ep_buf : NULL, count); + #endif + } + return false; +} + +TU_ATTR_ALWAYS_INLINE static inline +bool stream_release(tu_edpt_stream_t* s) { + if (s->is_host) { + #if CFG_TUH_ENABLED + return usbh_edpt_release(s->daddr, s->ep_addr); + #endif + } else { + #if CFG_TUD_ENABLED + return usbd_edpt_release(s->rhport, s->ep_addr); + #endif + } + return false; +} + +//--------------------------------------------------------------------+ +// Stream Write +//--------------------------------------------------------------------+ +bool tu_edpt_stream_write_zlp_if_needed(tu_edpt_stream_t* s, uint32_t last_xferred_bytes) { + // ZLP condition: no pending data, last transferred bytes is multiple of packet size + TU_VERIFY(!tu_fifo_count(&s->ff) && last_xferred_bytes && (0 == (last_xferred_bytes & (s->ep_packetsize - 1)))); + TU_VERIFY(stream_claim(s)); + TU_ASSERT(stream_xfer(s, 0)); + return true; +} + +uint32_t tu_edpt_stream_write_xfer(tu_edpt_stream_t* s) { + // skip if no data + TU_VERIFY(tu_fifo_count(&s->ff), 0); + + // Claim the endpoint + TU_VERIFY(stream_claim(s), 0); + + // Pull data from FIFO -> EP buf + uint16_t const count = tu_fifo_read_n(&s->ff, s->ep_buf, s->ep_bufsize); + + if (count) { + TU_ASSERT(stream_xfer(s, count), 0); + return count; + } else { + // Release endpoint since we don't make any transfer + // Note: data is dropped if terminal is not connected + stream_release(s); + return 0; + } +} + +uint32_t tu_edpt_stream_write(tu_edpt_stream_t* s, void const* buffer, uint32_t bufsize) { + TU_VERIFY(bufsize); // TODO support ZLP + uint16_t ret = tu_fifo_write_n(&s->ff, buffer, (uint16_t) bufsize); + + // flush if fifo has more than packet size or + // in rare case: fifo depth is configured too small (which never reach packet size) + if ((tu_fifo_count(&s->ff) >= s->ep_packetsize) || (tu_fifo_depth(&s->ff) < s->ep_packetsize)) { + tu_edpt_stream_write_xfer(s); + } + + return ret; +} + +//--------------------------------------------------------------------+ +// Stream Read +//--------------------------------------------------------------------+ +uint32_t tu_edpt_stream_read_xfer(tu_edpt_stream_t* s) { + uint16_t available = tu_fifo_remaining(&s->ff); + + // Prepare for incoming data but only allow what we can store in the ring buffer. + // TODO Actually we can still carry out the transfer, keeping count of received bytes + // and slowly move it to the FIFO when read(). + // This pre-check reduces endpoint claiming + TU_VERIFY(available >= s->ep_packetsize); + + // claim endpoint + TU_VERIFY(stream_claim(s), 0); + + // get available again since fifo can be changed before endpoint is claimed + available = tu_fifo_remaining(&s->ff); + + if (available >= s->ep_packetsize) { + // multiple of packet size limit by ep bufsize + uint16_t count = (uint16_t) (available & ~(s->ep_packetsize - 1)); + count = tu_min16(count, s->ep_bufsize); + + TU_ASSERT(stream_xfer(s, count), 0); + return count; + } else { + // Release endpoint since we don't make any transfer + stream_release(s); + return 0; + } +} + +uint32_t tu_edpt_stream_read(tu_edpt_stream_t* s, void* buffer, uint32_t bufsize) { + uint32_t num_read = tu_fifo_read_n(&s->ff, buffer, (uint16_t) bufsize); + tu_edpt_stream_read_xfer(s); + return num_read; +} + +//--------------------------------------------------------------------+ +// Debug +//--------------------------------------------------------------------+ + +#if CFG_TUSB_DEBUG +#include + +#if CFG_TUSB_DEBUG >= CFG_TUH_LOG_LEVEL || CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL +char const* const tu_str_speed[] = {"Full", "Low", "High"}; +char const* const tu_str_std_request[] = { + "Get Status", + "Clear Feature", + "Reserved", + "Set Feature", + "Reserved", + "Set Address", + "Get Descriptor", + "Set Descriptor", + "Get Configuration", + "Set Configuration", + "Get Interface", + "Set Interface", + "Synch Frame" +}; + +char const* const tu_str_xfer_result[] = { + "OK", "FAILED", "STALLED", "TIMEOUT" +}; +#endif + +static void dump_str_line(uint8_t const* buf, uint16_t count) { + tu_printf(" |"); + // each line is 16 bytes + for (uint16_t i = 0; i < count; i++) { + const char ch = buf[i]; + tu_printf("%c", isprint(ch) ? ch : '.'); + } + tu_printf("|\r\n"); +} + +/* Print out memory contents + * - buf : buffer + * - count : number of item + * - indent: prefix spaces on every line + */ +void tu_print_mem(void const* buf, uint32_t count, uint8_t indent) { + uint8_t const size = 1; // fixed 1 byte for now + if (!buf || !count) { + tu_printf("NULL\r\n"); + return; + } + + uint8_t const* buf8 = (uint8_t const*) buf; + char format[] = "%00X"; + format[2] += (uint8_t) (2 * size); // 1 byte = 2 hex digits + const uint8_t item_per_line = 16 / size; + + for (unsigned int i = 0; i < count; i++) { + unsigned int value = 0; + + if (i % item_per_line == 0) { + // Print Ascii + if (i != 0) dump_str_line(buf8 - 16, 16); + for (uint8_t s = 0; s < indent; s++) tu_printf(" "); + // print offset or absolute address + tu_printf("%04X: ", 16 * i / item_per_line); + } + + tu_memcpy_s(&value, sizeof(value), buf8, size); + buf8 += size; + + tu_printf(" "); + tu_printf(format, value); + } + + // fill up last row to 16 for printing ascii + const uint32_t remain = count % 16; + uint8_t nback = (uint8_t) (remain ? remain : 16); + if (remain) { + for (uint32_t i = 0; i < 16 - remain; i++) { + tu_printf(" "); + for (int j = 0; j < 2 * size; j++) tu_printf(" "); + } + } + + dump_str_line(buf8 - nback, nback); +} + +#endif + +#endif // host or device enabled diff --git a/test-devices/composite-stm32/lib/tinyusb/tusb.h b/test-devices/composite-stm32/lib/tinyusb/tusb.h new file mode 100644 index 00000000..4f69a141 --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/tusb.h @@ -0,0 +1,148 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_H_ +#define _TUSB_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// INCLUDE +//--------------------------------------------------------------------+ +#include "common/tusb_common.h" +#include "osal/osal.h" +#include "common/tusb_fifo.h" + +//------------- TypeC -------------// +#if CFG_TUC_ENABLED + #include "typec/usbc.h" +#endif + +//------------- HOST -------------// +#if CFG_TUH_ENABLED + #include "host/usbh.h" + + #if CFG_TUH_HID + #include "class/hid/hid_host.h" + #endif + + #if CFG_TUH_MSC + #include "class/msc/msc_host.h" + #endif + + #if CFG_TUH_CDC + #include "class/cdc/cdc_host.h" + #endif + + #if CFG_TUH_VENDOR + #include "class/vendor/vendor_host.h" + #endif +#else + #ifndef tuh_int_handler + #define tuh_int_handler(...) + #endif +#endif + +//------------- DEVICE -------------// +#if CFG_TUD_ENABLED + #include "device/usbd.h" + + #if CFG_TUD_HID + #include "class/hid/hid_device.h" + #endif + + #if CFG_TUD_CDC + #include "class/cdc/cdc_device.h" + #endif + + #if CFG_TUD_MSC + #include "class/msc/msc_device.h" + #endif + + #if CFG_TUD_AUDIO + #include "class/audio/audio_device.h" + #endif + + #if CFG_TUD_VIDEO + #include "class/video/video_device.h" + #endif + + #if CFG_TUD_MIDI + #include "class/midi/midi_device.h" + #endif + + #if CFG_TUD_VENDOR + #include "class/vendor/vendor_device.h" + #endif + + #if CFG_TUD_USBTMC + #include "class/usbtmc/usbtmc_device.h" + #endif + + #if CFG_TUD_DFU_RUNTIME + #include "class/dfu/dfu_rt_device.h" + #endif + + #if CFG_TUD_DFU + #include "class/dfu/dfu_device.h" + #endif + + #if CFG_TUD_ECM_RNDIS || CFG_TUD_NCM + #include "class/net/net_device.h" + #endif + + #if CFG_TUD_BTH + #include "class/bth/bth_device.h" + #endif +#else + #ifndef tud_int_handler + #define tud_int_handler(...) + #endif +#endif + + +//--------------------------------------------------------------------+ +// APPLICATION API +//--------------------------------------------------------------------+ + +// Initialize device/host stack +// Note: when using with RTOS, this should be called after scheduler/kernel is started. +// Otherwise it could cause kernel issue since USB IRQ handler does use RTOS queue API. +bool tusb_init(void); + +// Check if stack is initialized +bool tusb_inited(void); + +// TODO +// bool tusb_teardown(void); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_H_ */ diff --git a/test-devices/composite-stm32/lib/tinyusb/tusb_option.h b/test-devices/composite-stm32/lib/tinyusb/tusb_option.h new file mode 100644 index 00000000..3ead20ee --- /dev/null +++ b/test-devices/composite-stm32/lib/tinyusb/tusb_option.h @@ -0,0 +1,558 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_OPTION_H_ +#define _TUSB_OPTION_H_ + +#include "common/tusb_compiler.h" + +// Version is release as major.minor.revision eg 1.0.0. though there could be notable APIs before a new release. +// For notable API changes within a release, we increase the build number. +#define TUSB_VERSION_MAJOR 0 +#define TUSB_VERSION_MINOR 16 +#define TUSB_VERSION_REVISION 0 +#define TUSB_VERSION_BUILD 3 + +#define TUSB_VERSION_NUMBER (TUSB_VERSION_MAJOR << 24 | TUSB_VERSION_MINOR << 16 | TUSB_VERSION_REVISION << 8 | TUSB_VERSION_BUILD) +#define TUSB_VERSION_STRING TU_STRING(TUSB_VERSION_MAJOR) "." TU_STRING(TUSB_VERSION_MINOR) "." TU_STRING(TUSB_VERSION_REVISION) + +//--------------------------------------------------------------------+ +// Supported MCUs +// CFG_TUSB_MCU must be defined to one of following value +//--------------------------------------------------------------------+ + +#define OPT_MCU_NONE 0 + +// LPC +#define OPT_MCU_LPC11UXX 1 ///< NXP LPC11Uxx +#define OPT_MCU_LPC13XX 2 ///< NXP LPC13xx +#define OPT_MCU_LPC15XX 3 ///< NXP LPC15xx +#define OPT_MCU_LPC175X_6X 4 ///< NXP LPC175x, LPC176x +#define OPT_MCU_LPC177X_8X 5 ///< NXP LPC177x, LPC178x +#define OPT_MCU_LPC18XX 6 ///< NXP LPC18xx +#define OPT_MCU_LPC40XX 7 ///< NXP LPC40xx +#define OPT_MCU_LPC43XX 8 ///< NXP LPC43xx +#define OPT_MCU_LPC51UXX 9 ///< NXP LPC51U6x +#define OPT_MCU_LPC54 10 ///< NXP LPC54 +#define OPT_MCU_LPC55 11 ///< NXP LPC55 +// legacy naming +#define OPT_MCU_LPC54XXX OPT_MCU_LPC54 +#define OPT_MCU_LPC55XX OPT_MCU_LPC55 + +// NRF +#define OPT_MCU_NRF5X 100 ///< Nordic nRF5x series + +// SAM +#define OPT_MCU_SAMD21 200 ///< MicroChip SAMD21 +#define OPT_MCU_SAMD51 201 ///< MicroChip SAMD51 +#define OPT_MCU_SAMG 202 ///< MicroChip SAMDG series +#define OPT_MCU_SAME5X 203 ///< MicroChip SAM E5x +#define OPT_MCU_SAMD11 204 ///< MicroChip SAMD11 +#define OPT_MCU_SAML22 205 ///< MicroChip SAML22 +#define OPT_MCU_SAML21 206 ///< MicroChip SAML21 +#define OPT_MCU_SAMX7X 207 ///< MicroChip SAME70, S70, V70, V71 family + +// STM32 +#define OPT_MCU_STM32F0 300 ///< ST F0 +#define OPT_MCU_STM32F1 301 ///< ST F1 +#define OPT_MCU_STM32F2 302 ///< ST F2 +#define OPT_MCU_STM32F3 303 ///< ST F3 +#define OPT_MCU_STM32F4 304 ///< ST F4 +#define OPT_MCU_STM32F7 305 ///< ST F7 +#define OPT_MCU_STM32H7 306 ///< ST H7 +#define OPT_MCU_STM32L1 308 ///< ST L1 +#define OPT_MCU_STM32L0 307 ///< ST L0 +#define OPT_MCU_STM32L4 309 ///< ST L4 +#define OPT_MCU_STM32G0 310 ///< ST G0 +#define OPT_MCU_STM32G4 311 ///< ST G4 +#define OPT_MCU_STM32WB 312 ///< ST WB +#define OPT_MCU_STM32U5 313 ///< ST U5 +#define OPT_MCU_STM32L5 314 ///< ST L5 +#define OPT_MCU_STM32H5 315 ///< ST H5 + +// Sony +#define OPT_MCU_CXD56 400 ///< SONY CXD56 + +// TI +#define OPT_MCU_MSP430x5xx 500 ///< TI MSP430x5xx +#define OPT_MCU_MSP432E4 510 ///< TI MSP432E4xx +#define OPT_MCU_TM4C123 511 ///< TI Tiva-C 123x +#define OPT_MCU_TM4C129 512 ///< TI Tiva-C 129x + +// ValentyUSB eptri +#define OPT_MCU_VALENTYUSB_EPTRI 600 ///< Fomu eptri config + +// NXP iMX RT +#define OPT_MCU_MIMXRT1XXX 700 ///< NXP iMX RT1xxx Series +#define OPT_MCU_MIMXRT10XX OPT_MCU_MIMXRT1XXX ///< RT10xx +#define OPT_MCU_MIMXRT11XX OPT_MCU_MIMXRT1XXX ///< RT11xx + +// Nuvoton +#define OPT_MCU_NUC121 800 +#define OPT_MCU_NUC126 801 +#define OPT_MCU_NUC120 802 +#define OPT_MCU_NUC505 803 + +// Espressif +#define OPT_MCU_ESP32S2 900 ///< Espressif ESP32-S2 +#define OPT_MCU_ESP32S3 901 ///< Espressif ESP32-S3 +#define OPT_MCU_ESP32 902 ///< Espressif ESP32 (for host max3421e) +#define OPT_MCU_ESP32C3 903 ///< Espressif ESP32-C3 +#define OPT_MCU_ESP32C6 904 ///< Espressif ESP32-C6 +#define TUP_MCU_ESPRESSIF (CFG_TUSB_MCU >= 900 && CFG_TUSB_MCU < 1000) // check if Espressif MCU + +// Dialog +#define OPT_MCU_DA1469X 1000 ///< Dialog Semiconductor DA1469x + +// Raspberry Pi +#define OPT_MCU_RP2040 1100 ///< Raspberry Pi RP2040 + +// NXP Kinetis +#define OPT_MCU_KINETIS_KL 1200 ///< NXP KL series +#define OPT_MCU_KINETIS_K32L 1201 ///< NXP K32L series +#define OPT_MCU_KINETIS_K32 1201 ///< Alias to K32L +#define OPT_MCU_KINETIS_K 1202 ///< NXP K series + +#define OPT_MCU_MKL25ZXX 1200 ///< Alias to KL (obsolete) +#define OPT_MCU_K32L2BXX 1201 ///< Alias to K32 (obsolete) + +// Silabs +#define OPT_MCU_EFM32GG 1300 ///< Silabs EFM32GG + +// Renesas RX +#define OPT_MCU_RX63X 1400 ///< Renesas RX63N/631 +#define OPT_MCU_RX65X 1401 ///< Renesas RX65N/RX651 +#define OPT_MCU_RX72N 1402 ///< Renesas RX72N +#define OPT_MCU_RAXXX 1403 ///< Renesas RAxxx families + +// Mind Motion +#define OPT_MCU_MM32F327X 1500 ///< Mind Motion MM32F327 + +// GigaDevice +#define OPT_MCU_GD32VF103 1600 ///< GigaDevice GD32VF103 + +// Broadcom +#define OPT_MCU_BCM2711 1700 ///< Broadcom BCM2711 +#define OPT_MCU_BCM2835 1701 ///< Broadcom BCM2835 +#define OPT_MCU_BCM2837 1702 ///< Broadcom BCM2837 + +// Infineon +#define OPT_MCU_XMC4000 1800 ///< Infineon XMC4000 + +// PIC +#define OPT_MCU_PIC32MZ 1900 ///< MicroChip PIC32MZ family +#define OPT_MCU_PIC32MM 1901 ///< MicroChip PIC32MM family +#define OPT_MCU_PIC32MX 1902 ///< MicroChip PIC32MX family +#define OPT_MCU_PIC32MK 1903 ///< MicroChip PIC32MK family +#define OPT_MCU_PIC24 1910 ///< MicroChip PIC24 family +#define OPT_MCU_DSPIC33 1911 ///< MicroChip DSPIC33 family + +// BridgeTek +#define OPT_MCU_FT90X 2000 ///< BridgeTek FT90x +#define OPT_MCU_FT93X 2001 ///< BridgeTek FT93x + +// Allwinner +#define OPT_MCU_F1C100S 2100 ///< Allwinner F1C100s family + +// WCH +#define OPT_MCU_CH32V307 2200 ///< WCH CH32V307 +#define OPT_MCU_CH32F20X 2210 ///< WCH CH32F20x + + +// NXP LPC MCX +#define OPT_MCU_MCXN9 2300 ///< NXP MCX N9 Series +#define OPT_MCU_MCXA15 2301 ///< NXP MCX A15 Series + +// Check if configured MCU is one of listed +// Apply _TU_CHECK_MCU with || as separator to list of input +#define _TU_CHECK_MCU(_m) (CFG_TUSB_MCU == _m) +#define TU_CHECK_MCU(...) (TU_ARGS_APPLY(_TU_CHECK_MCU, ||, __VA_ARGS__)) + +//--------------------------------------------------------------------+ +// Supported OS +//--------------------------------------------------------------------+ + +#define OPT_OS_NONE 1 ///< No RTOS +#define OPT_OS_FREERTOS 2 ///< FreeRTOS +#define OPT_OS_MYNEWT 3 ///< Mynewt OS +#define OPT_OS_CUSTOM 4 ///< Custom OS is implemented by application +#define OPT_OS_PICO 5 ///< Raspberry Pi Pico SDK +#define OPT_OS_RTTHREAD 6 ///< RT-Thread +#define OPT_OS_RTX4 7 ///< Keil RTX 4 + +// Allow to use command line to change the config name/location +#ifdef CFG_TUSB_CONFIG_FILE + #include CFG_TUSB_CONFIG_FILE +#else + #include "tusb_config.h" +#endif + +#include "common/tusb_mcu.h" + +//-------------------------------------------------------------------- +// RootHub Mode Configuration +// CFG_TUSB_RHPORTx_MODE contains operation mode and speed for that port +//-------------------------------------------------------------------- + +// Low byte is operational mode +#define OPT_MODE_NONE 0x0000 ///< Disabled +#define OPT_MODE_DEVICE 0x0001 ///< Device Mode +#define OPT_MODE_HOST 0x0002 ///< Host Mode + +// High byte is max operational speed (corresponding to tusb_speed_t) +#define OPT_MODE_DEFAULT_SPEED 0x0000 ///< Default (max) speed supported by MCU +#define OPT_MODE_LOW_SPEED 0x0100 ///< Low Speed +#define OPT_MODE_FULL_SPEED 0x0200 ///< Full Speed +#define OPT_MODE_HIGH_SPEED 0x0400 ///< High Speed +#define OPT_MODE_SPEED_MASK 0xff00 + +//------------- Roothub as Device -------------// + +#if defined(CFG_TUSB_RHPORT0_MODE) && ((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_DEVICE) + #define TUD_RHPORT_MODE (CFG_TUSB_RHPORT0_MODE) + #define TUD_OPT_RHPORT 0 +#elif defined(CFG_TUSB_RHPORT1_MODE) && ((CFG_TUSB_RHPORT1_MODE) & OPT_MODE_DEVICE) + #define TUD_RHPORT_MODE (CFG_TUSB_RHPORT1_MODE) + #define TUD_OPT_RHPORT 1 +#else + #define TUD_RHPORT_MODE OPT_MODE_NONE +#endif + +#ifndef CFG_TUD_ENABLED + // fallback to use CFG_TUSB_RHPORTx_MODE + #define CFG_TUD_ENABLED (TUD_RHPORT_MODE & OPT_MODE_DEVICE) +#endif + +#ifndef CFG_TUD_MAX_SPEED + // fallback to use CFG_TUSB_RHPORTx_MODE + #define CFG_TUD_MAX_SPEED (TUD_RHPORT_MODE & OPT_MODE_SPEED_MASK) +#endif + +// For backward compatible +#define TUSB_OPT_DEVICE_ENABLED CFG_TUD_ENABLED + +// highspeed support indicator +#define TUD_OPT_HIGH_SPEED (CFG_TUD_MAX_SPEED ? (CFG_TUD_MAX_SPEED & OPT_MODE_HIGH_SPEED) : TUP_RHPORT_HIGHSPEED) + +//------------- Roothub as Host -------------// + +#if defined(CFG_TUSB_RHPORT0_MODE) && ((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_HOST) + #define TUH_RHPORT_MODE (CFG_TUSB_RHPORT0_MODE) + #define TUH_OPT_RHPORT 0 +#elif defined(CFG_TUSB_RHPORT1_MODE) && ((CFG_TUSB_RHPORT1_MODE) & OPT_MODE_HOST) + #define TUH_RHPORT_MODE (CFG_TUSB_RHPORT1_MODE) + #define TUH_OPT_RHPORT 1 +#else + #define TUH_RHPORT_MODE OPT_MODE_NONE +#endif + +#ifndef CFG_TUH_ENABLED + // fallback to use CFG_TUSB_RHPORTx_MODE + #define CFG_TUH_ENABLED (TUH_RHPORT_MODE & OPT_MODE_HOST) +#endif + +#ifndef CFG_TUH_MAX_SPEED + // fallback to use CFG_TUSB_RHPORTx_MODE + #define CFG_TUH_MAX_SPEED (TUH_RHPORT_MODE & OPT_MODE_SPEED_MASK) +#endif + +// For backward compatible +#define TUSB_OPT_HOST_ENABLED CFG_TUH_ENABLED + +// highspeed support indicator +#define TUH_OPT_HIGH_SPEED (CFG_TUH_MAX_SPEED ? (CFG_TUH_MAX_SPEED & OPT_MODE_HIGH_SPEED) : TUP_RHPORT_HIGHSPEED) + + +//--------------------------------------------------------------------+ +// TODO move later +//--------------------------------------------------------------------+ + +// TUP_MCU_STRICT_ALIGN will overwrite TUP_ARCH_STRICT_ALIGN. +// In case TUP_MCU_STRICT_ALIGN = 1 and TUP_ARCH_STRICT_ALIGN =0, we will not reply on compiler +// to generate unaligned access code. +// LPC_IP3511 Highspeed cannot access unaligned memory on USB_RAM +#if TUD_OPT_HIGH_SPEED && TU_CHECK_MCU(OPT_MCU_LPC54XXX, OPT_MCU_LPC55XX) + #define TUP_MCU_STRICT_ALIGN 1 +#else + #define TUP_MCU_STRICT_ALIGN 0 +#endif + + +//--------------------------------------------------------------------+ +// Common Options (Default) +//--------------------------------------------------------------------+ + +// Debug enable to print out error message +#ifndef CFG_TUSB_DEBUG + #define CFG_TUSB_DEBUG 0 +#endif + +// Level where CFG_TUSB_DEBUG must be at least for USBH is logged +#ifndef CFG_TUH_LOG_LEVEL + #define CFG_TUH_LOG_LEVEL 2 +#endif + +// Level where CFG_TUSB_DEBUG must be at least for USBD is logged +#ifndef CFG_TUD_LOG_LEVEL + #define CFG_TUD_LOG_LEVEL 2 +#endif + +// Memory section for placing buffer used for usb transferring. If MEM_SECTION is different for +// host and device use: CFG_TUD_MEM_SECTION, CFG_TUH_MEM_SECTION instead +#ifndef CFG_TUSB_MEM_SECTION + #define CFG_TUSB_MEM_SECTION +#endif + +// Alignment requirement of buffer used for usb transferring. if MEM_ALIGN is different for +// host and device controller use: CFG_TUD_MEM_ALIGN, CFG_TUH_MEM_ALIGN instead +#ifndef CFG_TUSB_MEM_ALIGN + #define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4) +#endif + +// OS selection +#ifndef CFG_TUSB_OS + #define CFG_TUSB_OS OPT_OS_NONE +#endif + +#ifndef CFG_TUSB_OS_INC_PATH + #define CFG_TUSB_OS_INC_PATH +#endif + +//-------------------------------------------------------------------- +// Device Options (Default) +//-------------------------------------------------------------------- + +// Attribute to place data in accessible RAM for device controller (default: CFG_TUSB_MEM_SECTION) +#ifndef CFG_TUD_MEM_SECTION + #define CFG_TUD_MEM_SECTION CFG_TUSB_MEM_SECTION +#endif + +// Attribute to align memory for device controller (default: CFG_TUSB_MEM_ALIGN) +#ifndef CFG_TUD_MEM_ALIGN + #define CFG_TUD_MEM_ALIGN CFG_TUSB_MEM_ALIGN +#endif + +#ifndef CFG_TUD_ENDPOINT0_SIZE + #define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +#ifndef CFG_TUD_INTERFACE_MAX + #define CFG_TUD_INTERFACE_MAX 16 +#endif + +//------------- Device Class Driver -------------// +#ifndef CFG_TUD_BTH + #define CFG_TUD_BTH 0 +#endif + +#if CFG_TUD_BTH && !defined(CFG_TUD_BTH_ISO_ALT_COUNT) +#error CFG_TUD_BTH_ISO_ALT_COUNT must be defined to tell Bluetooth driver the number of ISO endpoints to use +#endif + +#ifndef CFG_TUD_CDC + #define CFG_TUD_CDC 0 +#endif + +#ifndef CFG_TUD_MSC + #define CFG_TUD_MSC 0 +#endif + +#ifndef CFG_TUD_HID + #define CFG_TUD_HID 0 +#endif + +#ifndef CFG_TUD_AUDIO + #define CFG_TUD_AUDIO 0 +#endif + +#ifndef CFG_TUD_VIDEO + #define CFG_TUD_VIDEO 0 +#endif + +#ifndef CFG_TUD_MIDI + #define CFG_TUD_MIDI 0 +#endif + +#ifndef CFG_TUD_VENDOR + #define CFG_TUD_VENDOR 0 +#endif + +#ifndef CFG_TUD_USBTMC + #define CFG_TUD_USBTMC 0 +#endif + +#ifndef CFG_TUD_DFU_RUNTIME + #define CFG_TUD_DFU_RUNTIME 0 +#endif + +#ifndef CFG_TUD_DFU + #define CFG_TUD_DFU 0 +#endif + +#ifndef CFG_TUD_ECM_RNDIS + #ifdef CFG_TUD_NET + #warning "CFG_TUD_NET is renamed to CFG_TUD_ECM_RNDIS" + #define CFG_TUD_ECM_RNDIS CFG_TUD_NET + #else + #define CFG_TUD_ECM_RNDIS 0 + #endif +#endif + +#ifndef CFG_TUD_NCM + #define CFG_TUD_NCM 0 +#endif + +//-------------------------------------------------------------------- +// Host Options (Default) +//-------------------------------------------------------------------- +#if CFG_TUH_ENABLED + #ifndef CFG_TUH_DEVICE_MAX + #define CFG_TUH_DEVICE_MAX 1 + #endif + + #ifndef CFG_TUH_ENUMERATION_BUFSIZE + #define CFG_TUH_ENUMERATION_BUFSIZE 256 + #endif +#endif // CFG_TUH_ENABLED + +// Attribute to place data in accessible RAM for host controller (default: CFG_TUSB_MEM_SECTION) +#ifndef CFG_TUH_MEM_SECTION + #define CFG_TUH_MEM_SECTION CFG_TUSB_MEM_SECTION +#endif + +// Attribute to align memory for host controller +#ifndef CFG_TUH_MEM_ALIGN + #define CFG_TUH_MEM_ALIGN CFG_TUSB_MEM_ALIGN +#endif + +//------------- CLASS -------------// + +#ifndef CFG_TUH_HUB + #define CFG_TUH_HUB 0 +#endif + +#ifndef CFG_TUH_CDC + #define CFG_TUH_CDC 0 +#endif + +#ifndef CFG_TUH_CDC_FTDI + // FTDI is not part of CDC class, only to re-use CDC driver API + #define CFG_TUH_CDC_FTDI 0 +#endif + +#ifndef CFG_TUH_CDC_FTDI_VID_PID_LIST + // List of product IDs that can use the FTDI CDC driver. 0x0403 is FTDI's VID + #define CFG_TUH_CDC_FTDI_VID_PID_LIST \ + {0x0403, 0x6001}, {0x0403, 0x6006}, {0x0403, 0x6010}, {0x0403, 0x6011}, \ + {0x0403, 0x6014}, {0x0403, 0x6015}, {0x0403, 0x8372}, {0x0403, 0xFBFA}, \ + {0x0403, 0xCD18} +#endif + +#ifndef CFG_TUH_CDC_CP210X + // CP210X is not part of CDC class, only to re-use CDC driver API + #define CFG_TUH_CDC_CP210X 0 +#endif + +#ifndef CFG_TUH_CDC_CP210X_VID_PID_LIST + // List of product IDs that can use the CP210X CDC driver. 0x10C4 is Silicon Labs' VID + #define CFG_TUH_CDC_CP210X_VID_PID_LIST \ + {0x10C4, 0xEA60}, {0x10C4, 0xEA70} +#endif + +#ifndef CFG_TUH_CDC_CH34X + // CH34X is not part of CDC class, only to re-use CDC driver API + #define CFG_TUH_CDC_CH34X 0 +#endif + +#ifndef CFG_TUH_CDC_CH34X_VID_PID_LIST + // List of product IDs that can use the CH34X CDC driver + #define CFG_TUH_CDC_CH34X_VID_PID_LIST \ + { 0x1a86, 0x5523 }, /* ch341 chip */ \ + { 0x1a86, 0x7522 }, /* ch340k chip */ \ + { 0x1a86, 0x7523 }, /* ch340 chip */ \ + { 0x1a86, 0xe523 }, /* ch330 chip */ \ + { 0x4348, 0x5523 }, /* ch340 custom chip */ \ + { 0x2184, 0x0057 }, /* overtaken from Linux Kernel driver /drivers/usb/serial/ch341.c */ \ + { 0x9986, 0x7523 } /* overtaken from Linux Kernel driver /drivers/usb/serial/ch341.c */ +#endif + +#ifndef CFG_TUH_HID + #define CFG_TUH_HID 0 +#endif + +#ifndef CFG_TUH_MIDI + #define CFG_TUH_MIDI 0 +#endif + +#ifndef CFG_TUH_MSC + #define CFG_TUH_MSC 0 +#endif + +#ifndef CFG_TUH_VENDOR + #define CFG_TUH_VENDOR 0 +#endif + +#ifndef CFG_TUH_API_EDPT_XFER + #define CFG_TUH_API_EDPT_XFER 0 +#endif + +// Enable PIO-USB software host controller +#ifndef CFG_TUH_RPI_PIO_USB + #define CFG_TUH_RPI_PIO_USB 0 +#endif + +#ifndef CFG_TUD_RPI_PIO_USB + #define CFG_TUD_RPI_PIO_USB 0 +#endif + +// MAX3421 Host controller option +#ifndef CFG_TUH_MAX3421 + #define CFG_TUH_MAX3421 0 +#endif + +//--------------------------------------------------------------------+ +// TypeC Options (Default) +//--------------------------------------------------------------------+ + +#ifndef CFG_TUC_ENABLED +#define CFG_TUC_ENABLED 0 + +#define tuc_int_handler(_p) +#endif + +//------------------------------------------------------------------ +// Configuration Validation +//------------------------------------------------------------------ +#if CFG_TUD_ENDPOINT0_SIZE > 64 + #error Control Endpoint Max Packet Size cannot be larger than 64 +#endif + +// To avoid GCC compiler warnings when -pedantic option is used (strict ISO C) +typedef int make_iso_compilers_happy; + +#endif /* _TUSB_OPTION_H_ */ + +/** @} */ diff --git a/test-devices/composite-stm32/platformio.ini b/test-devices/composite-stm32/platformio.ini index 7d74ce50..0a2fda29 100644 --- a/test-devices/composite-stm32/platformio.ini +++ b/test-devices/composite-stm32/platformio.ini @@ -1,4 +1,33 @@ -[env:composite-stm32] +[common] +tinyusb_flags = + -D CFG_TUD_CDC=1 + -D CFG_VENDOR_ADVANCED=1 + -D CFG_VENDOR_ADVANCED_NUM_INTF=2 + -D CFG_TUSB_RHPORT1_MODE=OPT_MODE_NONE + -D CFG_WINUSB=OPT_WINUSB_MSOS20 platform = ststm32 +framework = cmsis +debug_tool = stlink + +[env:blackpill-f401cc] +extends = common +board = blackpill_f401cc +build_flags = + ${common.tinyusb_flags} + -D CFG_TUSB_MCU=OPT_MCU_STM32F4 + -D HSE_VALUE=25000000 + +[env:blackpill-f411ce] +extends = common +board = blackpill_f411ce +build_flags = + ${common.tinyusb_flags} + -D CFG_TUSB_MCU=OPT_MCU_STM32F4 + -D HSE_VALUE=25000000 + +[env:bluepill-f103c8] +extends = common board = bluepill_f103c8 -framework = libopencm3 +build_flags = + ${common.tinyusb_flags} + -D CFG_TUSB_MCU=OPT_MCU_STM32F1 diff --git a/test-devices/composite-stm32/save_firmware.sh b/test-devices/composite-stm32/save_firmware.sh new file mode 100755 index 00000000..303a542a --- /dev/null +++ b/test-devices/composite-stm32/save_firmware.sh @@ -0,0 +1,6 @@ +#!/bin/sh +rm -rf .pio +pio run +cp .pio/build/bluepill-f103c8/firmware.bin bin/bluepill-f103c8.bin +cp .pio/build/blackpill-f401cc/firmware.bin bin/blackpill-f401cc.bin +cp .pio/build/blackpill-f411ce/firmware.bin bin/blackpill-f411ce.bin diff --git a/test-devices/composite-stm32/src/board.h b/test-devices/composite-stm32/src/board.h new file mode 100644 index 00000000..4e032fd4 --- /dev/null +++ b/test-devices/composite-stm32/src/board.h @@ -0,0 +1,37 @@ +// +// Java Does USB +// Loopback device for testing +// +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Board specific functions (HAL) +// + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +// Initialize the board +void board_init(void); + +// Set the LED on or off +void board_led_write(bool on); + +// Return the number of milliseconds since a time in the past +uint32_t board_millis(void); + +// USB serial number +extern char board_serial_num[13]; + + +#ifdef __cplusplus +} +#endif diff --git a/test-devices/composite-stm32/src/board_f1.c b/test-devices/composite-stm32/src/board_f1.c new file mode 100644 index 00000000..344cae68 --- /dev/null +++ b/test-devices/composite-stm32/src/board_f1.c @@ -0,0 +1,211 @@ +// +// Java Does USB +// Loopback device for testing +// +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Board specific code for STM32F1 family +// + +#if defined(STM32F1) + +#include +#include "stm32f1xx.h" +#include "device/usbd.h" + + +extern uint32_t SystemCoreClock; +void SystemCoreClockUpdate(void); + + +static inline uint32_t get_reg(__I uint32_t* reg, uint32_t mask) { + return *reg & mask; +} + +static inline void set_reg(__IO uint32_t* reg, uint32_t value, uint32_t mask) { + *reg = (*reg & ~mask) | (value & mask); +} + + +// --- additional RCC constants + +#define RCC_CFGR_PLLSRC_HSI (0 << RCC_CFGR_PLLSRC_Pos) +#define RCC_CFGR_PLLSRC_HSE (1 << RCC_CFGR_PLLSRC_Pos) + +// --- additional SysTick constants + +#define SysTick_CTRL_CLKSOURCE_AHB_DIV8 (0 << SysTick_CTRL_CLKSOURCE_Pos) +#define SysTick_CTRL_CLKSOURCE_AHB (1 << SysTick_CTRL_CLKSOURCE_Pos) + +// --- additional GPIO constants + +#define GPIO_CNF_INPUT_ANALOG 0 +#define GPIO_CNF_INPUT_FLOAT 1 +#define GPIO_CNF_INPUT_PUPD 2 +#define GPIO_CNF_OUTPUT_PUSH_PULL 0 +#define GPIO_CNF_OUTPUT_OPEN_DRAIN 1 +#define GPIO_CNF_OUTPUT_ALT_PUSH_PULL 2 +#define GPIO_CNG_OUTPUT_ALT_OPEN_DRAIN 3 + +#define GPIO_MODE_INPUT 0 +#define GPIO_MODE_OUTPUT_10_MHZ 1 +#define GPIO_MODE_OUTPUT_2_MHZ 2 +#define GPIO_MODE_OUTPUT_50_MHZ 3 + + +static inline void rcc_wait_for_osc_ready(uint32_t rcc_cr_clk_rdy) { + while (get_reg(&RCC->CR, rcc_cr_clk_rdy) == 0) + ; +} + + +static void gpio_set_mode(GPIO_TypeDef* gpioport, int gpio, uint8_t mode, uint8_t cnf) { + + int offset; + __IO uint32_t* reg; + if (gpio < 8) { + offset = 4 * gpio; + reg = &gpioport->CRL; + } else { + offset = 4 * (gpio - 8); + reg = &gpioport->CRH; + } + + set_reg(reg, ((cnf << 2) | mode) << offset, 0xf << offset); +} + +static inline void gpio_set(GPIO_TypeDef* gpioport, int gpio) { + gpioport->BSRR = 1 << gpio; +} + +static inline void gpio_clear(GPIO_TypeDef* gpioport, int gpio) { + gpioport->BSRR = 1 << (gpio + 16); +} + + +static void rcc_clock_setup_in_hse_8mhz_out_72mhz(void) { + + // Enable internal high-speed oscillator + set_reg(&RCC->CR, RCC_CR_HSION, RCC_CR_HSION_Msk); + rcc_wait_for_osc_ready(RCC_CR_HSIRDY); + + // Select HSI as SYSCLK source + set_reg(&RCC->CFGR, RCC_CFGR_SW_HSI, RCC_CFGR_SW_Msk); + + // Enable external high-speed oscillator 8MHz + set_reg(&RCC->CR, RCC_CR_HSEON, RCC_CR_HSEON_Msk); + rcc_wait_for_osc_ready(RCC_CR_HSERDY); + set_reg(&RCC->CFGR, RCC_CFGR_SW_HSE, RCC_CFGR_SW_Msk); + + // Set prescalers for AHB, ADC, APB1, APB2 + set_reg(&RCC->CFGR, RCC_CFGR_HPRE_DIV1 | RCC_CFGR_ADCPRE_DIV8 | RCC_CFGR_PPRE1_DIV2 | RCC_CFGR_PPRE2_DIV1, + RCC_CFGR_HPRE_Msk | RCC_CFGR_ADCPRE_Msk | RCC_CFGR_PPRE1_Msk | RCC_CFGR_PPRE2_Msk); + + // System clock of 72 MHz requires 2 wait states + set_reg(&FLASH->ACR, FLASH_ACR_LATENCY_2, FLASH_ACR_LATENCY_Msk); + + // PLL multiplier 9 (for 72 MHz), HSE as PLL source, no clock predevision + set_reg(&RCC->CFGR, RCC_CFGR_PLLMULL9 | RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLXTPRE_HSE, + RCC_CFGR_PLLMULL_Msk | RCC_CFGR_PLLSRC_Msk | RCC_CFGR_PLLXTPRE_Msk); + + // Enable PLL oscillator and wait for it to stabilize + set_reg(&RCC->CR, RCC_CR_PLLON, RCC_CR_PLLON_Msk); + rcc_wait_for_osc_ready(RCC_CR_PLLRDY); + + // Select PLL as SYSCLK source + set_reg(&RCC->CFGR, RCC_CFGR_SW_PLL, RCC_CFGR_SW_Msk); + + // Update the SystemCoreClock variable used by TinyUSB + SystemCoreClockUpdate(); +} + +static volatile uint32_t millis_count; + +static void systick_init(void) { + + // Initialize SysTick + set_reg(&SysTick->CTRL, SysTick_CTRL_CLKSOURCE_AHB_DIV8, SysTick_CTRL_CLKSOURCE_Msk); + SysTick->LOAD = SystemCoreClock / 8 / 1000 - 1; + + // Enable and start + set_reg(&SysTick->CTRL, SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk, + SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk); +} + + +// --- Serial number --- + +char board_serial_num[13]; + +const static char HEX_DIGITS[] = "0123456789ABCDEF"; + +void put_hex(uint32_t value, char *buf, int len) { + for (int idx = 0; idx < len; idx++) { + buf[idx] = HEX_DIGITS[value >> 28]; + value = value << 4; + } +} + +void usb_init_serial_num() { + __I uint32_t* unique_id =(__I uint32_t*) UID_BASE; + uint32_t id0 = unique_id[0]; + uint32_t id1 = unique_id[1]; + uint32_t id2 = unique_id[2]; + + id0 += id2; + + put_hex(id0, board_serial_num, 8); + put_hex(id1, board_serial_num + 8, 4); + board_serial_num[12] = 0; +} + + +// --- Exported board functions + +void board_init(void) { + + rcc_clock_setup_in_hse_8mhz_out_72mhz(); + systick_init(); + + // clock for GPIOA (USB pins) + RCC->APB2ENR |= RCC_APB2ENR_IOPAEN_Msk; + // clock for GPIOB (LED) + RCC->APB2ENR |= RCC_APB2ENR_IOPBEN_Msk; + // clock for USB + RCC->APB1ENR |= RCC_APB1ENR_USBEN_Msk; + + // LED + gpio_set_mode(GPIOB, 12, GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSH_PULL); + + usb_init_serial_num(); +} + +uint32_t board_millis(void) { + return millis_count; +} + +void board_led_write(bool on) { + if (on) + gpio_clear(GPIOB, 12); + else + gpio_set(GPIOB, 12); +} + + +// --- Interrupt handlers --- + +void SysTick_Handler (void) { + millis_count++; +} + +void USB_HP_IRQHandler(void) { + tud_int_handler(0); +} + +void USB_LP_IRQHandler(void) { + tud_int_handler(0); +} + +#endif diff --git a/test-devices/composite-stm32/src/board_f4.c b/test-devices/composite-stm32/src/board_f4.c new file mode 100644 index 00000000..927320d5 --- /dev/null +++ b/test-devices/composite-stm32/src/board_f4.c @@ -0,0 +1,281 @@ +// +// Java Does USB +// Loopback device for testing +// +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Board specific code for STM32F4 family +// + +#if defined(STM32F4xx) + +#include +#include "stm32f4xx.h" +#include "device/usbd.h" + +extern uint32_t SystemCoreClock; +void SystemCoreClockUpdate(void); + + +static inline uint32_t get_reg(__I uint32_t* reg, uint32_t mask) { + return *reg & mask; +} + +static inline void set_reg(__IO uint32_t* reg, uint32_t value, uint32_t mask) { + *reg = (*reg & ~mask) | (value & mask); +} + + +// --- additional PWR constants + +#define PWR_CR_VOS_SCALE3 (1 << PWR_CR_VOS_Pos) +#define PWR_CR_VOS_SCALE2 (2 << PWR_CR_VOS_Pos) +#define PWR_CR_VOS_SCALE1 (3 << PWR_CR_VOS_Pos) + + +// --- additional RCC constants + +typedef struct rcc_clock_setup { + uint8_t pllm; + uint16_t plln; + uint8_t pllp; + uint8_t pllq; + uint32_t pll_source; + uint32_t flash_config; + uint32_t hpre; + uint32_t ppre1; + uint32_t ppre2; + uint32_t voltage_scale; +} rcc_clock_setup_t; + +const rcc_clock_setup_t clock_setup_hse_value_out_84mhz_3v3 = { + .pllm = HSE_VALUE / 1000000, + .plln = 336, + .pllp = 4, + .pllq = 7, + .pll_source = RCC_PLLCFGR_PLLSRC_HSE, + .hpre = RCC_CFGR_HPRE_DIV1, + .ppre1 = RCC_CFGR_PPRE1_DIV2, + .ppre2 = RCC_CFGR_PPRE2_DIV1, + .voltage_scale = PWR_CR_VOS_SCALE1, + .flash_config = FLASH_ACR_DCEN | FLASH_ACR_ICEN | FLASH_ACR_LATENCY_2WS +}; + +// --- additional SysTick constants + +#define SysTick_CTRL_CLKSOURCE_AHB_DIV8 (0 << SysTick_CTRL_CLKSOURCE_Pos) +#define SysTick_CTRL_CLKSOURCE_AHB (1 << SysTick_CTRL_CLKSOURCE_Pos) + +// --- additional GPIO constants + +#define GPIO_PUPD_NO_PULL 0 +#define GPIO_PUPD_PULL_UP 1 +#define GPIO_PUPD_PULL_DOWN 2 + +#define GPIO_MODE_INPUT 0 +#define GPIO_MODE_OUTPUT 1 +#define GPIO_MODE_ALT 2 +#define GPIO_MODE_ANALOG 3 + +#define GPIO_OSPEED_LOW 0 +#define GPIO_OSPEED_MEDIUM 1 +#define GPIO_OSPEED_FAST 2 +#define GPIO_OSPEED_HIGH 3 + + +static inline void rcc_wait_for_osc_ready(uint32_t rcc_cr_clk_rdy) { + while (get_reg(&RCC->CR, rcc_cr_clk_rdy) == 0) + ; +} + +static void gpio_mode_setup(GPIO_TypeDef* gpioport, int gpio, uint8_t mode, uint8_t pull_up_down) { + + int offset = gpio * 2; + set_reg(&gpioport->PUPDR, pull_up_down << offset, 3 << offset); + set_reg(&gpioport->MODER, mode << offset, 3 << offset); +} + +void gpio_set_af(GPIO_TypeDef* gpioport, int gpio, uint8_t alt_func_num) { + + int offset = 4 * gpio; + __IO uint32_t* reg; + if (offset < 32) { + reg = gpioport->AFR; + } else { + reg = gpioport->AFR + 1; + offset -= 32; + } + + set_reg(reg, alt_func_num << offset, 0xf << offset); +} + +static inline void gpio_set_ospeed(GPIO_TypeDef* gpioport, int gpio, uint8_t ospeed) { + int offset = gpio * 2; + set_reg(&gpioport->OSPEEDR, ospeed << offset, 3 << offset); +} + +static inline void gpio_set(GPIO_TypeDef* gpioport, int gpio) { + gpioport->BSRR = 1 << gpio; +} + +static inline void gpio_clear(GPIO_TypeDef* gpioport, int gpio) { + gpioport->BSRR = 1 << (gpio + 16); +} + + +static void rcc_clock_setup_pll(const rcc_clock_setup_t* setup) { + + // Enable internal high-speed oscillator (HSI) + set_reg(&RCC->CR, RCC_CR_HSION, RCC_CR_HSION_Msk); + rcc_wait_for_osc_ready(RCC_CR_HSIRDY); + + // Select HSI as SYSCLK source + set_reg(&RCC->CFGR, RCC_CFGR_SW_HSI, RCC_CFGR_SW_Msk); + + // Enable external high-speed oscillator (HSE) + if (setup->pll_source == RCC_PLLCFGR_PLLSRC_HSE) { + set_reg(&RCC->CR, RCC_CR_HSEON, RCC_CR_HSEON_Msk); + rcc_wait_for_osc_ready(RCC_CR_HSERDY); + } + + // Set the VOS scale mode + set_reg(&RCC->APB1ENR, RCC_APB1ENR_PWREN, RCC_APB1ENR_PWREN_Msk); + set_reg(&PWR->CR, setup->voltage_scale, PWR_CR_VOS_Msk); + + // Set prescalers for AHB, APB1, APB2 + set_reg(&RCC->CFGR, setup->hpre | setup->ppre1 | setup->ppre2, + RCC_CFGR_HPRE_Msk | RCC_CFGR_PPRE1_Msk | RCC_CFGR_PPRE2_Msk); + + // Disable PLL oscillator before changing its configuration + set_reg(&RCC->CR, 0, RCC_CR_PLLON_Msk); + + // Configure the PLL oscillator + int pllp_val = (setup->pllp >> 1) - 1; + RCC->PLLCFGR = setup->pll_source + | (setup->pllm << RCC_PLLCFGR_PLLM_Pos) + | (setup->plln << RCC_PLLCFGR_PLLN_Pos) + | (pllp_val << RCC_PLLCFGR_PLLP_Pos) + | (setup->pllq << RCC_PLLCFGR_PLLQ_Pos); + + // Enable PLL oscillator and wait for it to stabilize + set_reg(&RCC->CR, RCC_CR_PLLON, RCC_CR_PLLON_Msk); + rcc_wait_for_osc_ready(RCC_CR_PLLRDY); + + // Configure flash settings + set_reg(&FLASH->ACR, setup->flash_config, FLASH_ACR_DCEN_Msk | FLASH_ACR_ICEN_Msk | FLASH_ACR_LATENCY_Msk); + + // Select PLL as SYSCLK source + set_reg(&RCC->CFGR, RCC_CFGR_SW_PLL, RCC_CFGR_SW_Msk); + + // Wait for PLL clock to be selected + while (get_reg(&RCC->CFGR, RCC_CFGR_SWS_Msk) != RCC_CFGR_SWS_PLL) + ; + + // Disable internal high-speed oscillator + if (setup->pll_source == RCC_PLLCFGR_PLLSRC_HSE) + set_reg(&RCC->CR, 0, RCC_CR_HSION_Msk); + + // Update the SystemCoreClock variable used by TinyUSB + SystemCoreClockUpdate(); +} + +static volatile uint32_t millis_count; + +static void systick_init(void) { + + // Initialize SysTick + SysTick->CTRL = (SysTick->CTRL & ~SysTick_CTRL_CLKSOURCE_Msk) | SysTick_CTRL_CLKSOURCE_AHB_DIV8; + SysTick->LOAD = SystemCoreClock / 8 / 1000 - 1; + + // Enable and start + SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; + SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; +} + + +// --- Serial number --- + +char board_serial_num[13]; + +const static char HEX_DIGITS[] = "0123456789ABCDEF"; + +static void put_hex(uint32_t value, char *buf, int len) { + for (int idx = 0; idx < len; idx++) { + buf[idx] = HEX_DIGITS[value >> 28]; + value = value << 4; + } +} + +static void usb_init_serial_num() { + __I uint32_t* unique_id =(__I uint32_t*) UID_BASE; + uint32_t id0 = unique_id[0]; + uint32_t id1 = unique_id[1]; + uint32_t id2 = unique_id[2]; + + id0 += id2; + + put_hex(id0, board_serial_num, 8); + put_hex(id1, board_serial_num + 8, 4); + board_serial_num[12] = 0; +} + + +// --- Exported board functions + +void board_init(void) { + + rcc_clock_setup_pll(&clock_setup_hse_value_out_84mhz_3v3); + systick_init(); + + // clock for GPIOA (USB pins) + set_reg(&RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN, RCC_AHB1ENR_GPIOAEN_Msk); + + // Configure USB D+/D- pins + gpio_mode_setup(GPIOA, 11, GPIO_MODE_ALT, GPIO_PUPD_NO_PULL); + gpio_set_af(GPIOA, 11, 10); + gpio_set_ospeed(GPIOA, 11, GPIO_OSPEED_HIGH); + gpio_mode_setup(GPIOA, 12, GPIO_MODE_ALT, GPIO_PUPD_NO_PULL); + gpio_set_af(GPIOA, 12, 10); + gpio_set_ospeed(GPIOA, 12, GPIO_OSPEED_HIGH); + + // clock for USB + set_reg(&RCC->AHB2ENR, RCC_AHB2ENR_OTGFSEN, RCC_AHB2ENR_OTGFSEN_Msk); + + // Disable VBUS sense + set_reg(&USB_OTG_FS->GCCFG, USB_OTG_GCCFG_NOVBUSSENS, + USB_OTG_GCCFG_NOVBUSSENS_Msk | USB_OTG_GCCFG_VBUSASEN_Msk | USB_OTG_GCCFG_VBUSBSEN_Msk); + + // clock for GPIOC (LED) + set_reg(&RCC->AHB1ENR, RCC_AHB1ENR_GPIOCEN, RCC_AHB1ENR_GPIOCEN_Msk); + + // LED pin + gpio_mode_setup(GPIOC, 13, GPIO_MODE_OUTPUT, GPIO_PUPD_NO_PULL); + + usb_init_serial_num(); +} + +uint32_t board_millis(void) { + return millis_count; +} + +void board_led_write(bool on) { + if (on) + gpio_clear(GPIOC, 13); + else + gpio_set(GPIOC, 13); +} + + +// --- Interrupt handlers --- + +void SysTick_Handler (void) { + millis_count++; +} + +void OTG_FS_IRQHandler(void) { + tud_int_handler(0); +} + +#endif diff --git a/test-devices/composite-stm32/src/common.cpp b/test-devices/composite-stm32/src/common.cpp deleted file mode 100644 index 5102b6bd..00000000 --- a/test-devices/composite-stm32/src/common.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// -// Java Does USB -// Loopback device for testing -// -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// -// Commmon functions -// - -#include "common.h" - -#include - -static volatile uint32_t millis_count; - -uint32_t millis() { return millis_count; } - -void delay(uint32_t ms) { - int32_t target_time = millis_count + ms; - while (target_time - (int32_t)millis_count > 0) - ; -} - -void systick_init() { - // Initialize SysTick - systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8); - systick_set_reload(rcc_ahb_frequency / 8 / 1000 - 1); - - // Enable and start - systick_interrupt_enable(); - systick_counter_enable(); -} - -// System tick timer interrupt handler -extern "C" void sys_tick_handler() { millis_count++; } diff --git a/test-devices/composite-stm32/src/main.c b/test-devices/composite-stm32/src/main.c new file mode 100644 index 00000000..0259b140 --- /dev/null +++ b/test-devices/composite-stm32/src/main.c @@ -0,0 +1,292 @@ +// +// Java Does USB +// Loopback device for testing +// +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Board specific function (HAL) +// + +#include +#include +#include + +#include "board.h" +#include "tusb.h" +#include "usb_descriptors.h" +#include "vendor_custom.h" + +// FIFO buffer for loopback data +tu_fifo_t loopback_fifo; +uint8_t loopback_buffer[512]; +bool delay_loopback_reset = false; + +// RX buffer for loopback +uint8_t loopback_rx_buffer[64]; + +static bool is_blinking = true; +static uint32_t led_on_until = 0; +static uint32_t blink_toogle_at = 0; +static bool is_blink_on = true; + +static inline bool has_expired(uint32_t deadline, uint32_t now) { + return (int32_t)(now - deadline) >= 0; +} + +static void led_busy(void); +static void led_blinking_task(void); +static void cdc_task(void); +static void loopback_init(void); +static void loopback_check_rx(void); +static void loopback_check_tx(void); +static void reset_buffers(void); + + +int main(void) { + + board_init(); + loopback_init(); + + // init device stack + tud_init(BOARD_TUD_RHPORT); + + while (1) { + tud_task(); + cdc_task(); + led_blinking_task(); + } + + return 0; +} + +// reset device in predictable state +void reset_buffers(void) { + if (cust_vendor_is_transmitting(EP_LOOPBACK_TX)) { + delay_loopback_reset = true; + } else { + tu_fifo_clear(&loopback_fifo); + } +} + +// --- Loopback + +void loopback_init(void) { + tu_fifo_config(&loopback_fifo, loopback_buffer, sizeof(loopback_buffer), 1, false); +} + +// Check if the next transmission should be started +void loopback_check_tx(void) { + + if (delay_loopback_reset) { + tu_fifo_clear(&loopback_fifo); + delay_loopback_reset = false; + } + + tu_fifo_buffer_info_t info; + tu_fifo_get_read_info(&loopback_fifo, &info); + + if (info.len_lin > 0 && !cust_vendor_is_transmitting(EP_LOOPBACK_TX)) { + int n = info.len_lin; + if (n > 128) + n = 128; + + cust_vendor_start_transmit(EP_LOOPBACK_TX, info.ptr_lin, n); + led_busy(); + } +} + +// Check if receiving should be started again +void loopback_check_rx(void) { + + int n = tu_fifo_remaining(&loopback_fifo); + if (n >= sizeof(loopback_rx_buffer) && !cust_vendor_is_receiving(EP_LOOPBACK_RX)) + cust_vendor_prepare_recv(EP_LOOPBACK_RX, loopback_rx_buffer, sizeof(loopback_rx_buffer)); +} + +// --- CDC class + +void cdc_task(void) { + // echo all received data + if (!tud_cdc_available()) + return; + + uint8_t buf[64]; + uint32_t n = tud_cdc_read(buf, sizeof(buf)); + + tud_cdc_write(buf, n); + tud_cdc_write_flush(); + led_busy(); +} + + +// --- Vendor class callbacks + +// Invoked when new data has been received +void cust_vendor_rx_cb(uint8_t ep_addr, uint32_t recv_bytes) { + tu_fifo_write_n(&loopback_fifo, loopback_rx_buffer, recv_bytes); + loopback_check_rx(); + loopback_check_tx(); + led_busy(); +} + +// Invoked when last tx transfer finished +void cust_vendor_tx_cb(uint8_t ep_addr, uint32_t sent_bytes) { + if (sent_bytes > 0) + tu_fifo_advance_read_pointer(&loopback_fifo, sent_bytes); + + loopback_check_tx(); + loopback_check_rx(); + + // check ZLP + if (sent_bytes > 0 + && (sent_bytes & (BULK_MAX_PACKET_SIZE - 1)) == 0 + && !cust_vendor_is_transmitting(ep_addr)) + cust_vendor_start_transmit(EP_LOOPBACK_TX, NULL, 0); + + led_busy(); +} + +// Invoked when interface has been opened +void cust_vendor_intf_open_cb(uint8_t intf) { + loopback_check_rx(); + led_busy(); +} + +void cust_vendor_halt_cleared_cb(uint8_t ep_addr) { + switch (ep_addr) { + case EP_LOOPBACK_RX: + loopback_check_rx(); + break; + case EP_LOOPBACK_TX: + loopback_check_tx(); + break; + default: + break; + } + led_busy(); +} + + +// --- Control messages (see README) + +#define REQUEST_SAVE_VALUE 0x01 +#define REQUEST_SAVE_DATA 0x02 +#define REQUEST_SEND_DATA 0x03 +#define REQUEST_RESET_BUFFERS 0x04 +#define REQUEST_GET_INTF_NUM 0x05 + +static uint32_t saved_value = 0; + +bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) { + if (stage != CONTROL_STAGE_SETUP) + return true; // nothing to do + + switch (request->bmRequestType_bit.type) { + case TUSB_REQ_TYPE_VENDOR: + + switch (request->bRequest) { + + case REQUEST_SAVE_VALUE: + if (request->bmRequestType_bit.direction == TUSB_DIR_OUT && request->wLength == 0) { + led_busy(); + // save value from wValue + saved_value = request->wValue; + return tud_control_status(rhport, request); + } + break; + + case REQUEST_SAVE_DATA: + if (request->bmRequestType_bit.direction == TUSB_DIR_OUT && request->wLength == 4) { + led_busy(); + // receive into `saved_value` + return tud_control_xfer(rhport, request, &saved_value, 4); + } + break; + + case REQUEST_SEND_DATA: + if (request->bmRequestType_bit.direction == TUSB_DIR_IN && request->wLength == 4) { + led_busy(); + // transmit from `saved_value` + return tud_control_xfer(rhport, request, &saved_value, 4); + } + break; + + case REQUEST_RESET_BUFFERS: + if (request->bmRequestType_bit.direction == TUSB_DIR_OUT && request->wLength == 0) { + led_busy(); + reset_buffers(); + return tud_control_status(rhport, request); + } + break; + + case REQUEST_GET_INTF_NUM: + if (request->bmRequestType_bit.direction == TUSB_DIR_IN && request->wLength == 1) { + uint8_t intf_num = request->wIndex & 0xff; + if (intf_num < 4) { + led_busy(); + // return inteface number + return tud_control_xfer(rhport, request, &intf_num, 1); + } + } + break; + +#if CFG_WINUSB == OPT_WINUSB_MSOS20 + case MSOS_VENDOR_CODE: + if (request->wIndex == 7) { + // Get Microsoft OS 2.0 compatible descriptor + uint16_t total_len; + memcpy(&total_len, desc_ms_os_20 + 8, 2); + return tud_control_xfer(rhport, request, (uint8_t*) desc_ms_os_20, total_len); + } + break; +#endif + + default: + break; + } + + default: + break; + } + + // stall unknown request + return false; +} + +// --- Device callbacks + +// Register additional driver +usbd_class_driver_t const* usbd_app_driver_get_cb(uint8_t* driver_count) { + *driver_count = 1; + return &cust_vendor_driver; +} + + +// Invoked when device is mounted +void tud_mount_cb(void) { + is_blinking = false; +} + + +// --- LED blinking --- + +void led_busy(void) { + led_on_until = board_millis() + 100; + board_led_write(true); +} + +void led_blinking_task(void) { + uint32_t now = board_millis(); + if (is_blinking) { + if (has_expired(blink_toogle_at, now)) { + is_blink_on = !is_blink_on; + blink_toogle_at = now + 250; + } + board_led_write(is_blink_on && (now & 7) == 0); + + } else if (has_expired(led_on_until, now)) { + board_led_write((now & 3) == 0); + } +} diff --git a/test-devices/composite-stm32/src/main.cpp b/test-devices/composite-stm32/src/main.cpp deleted file mode 100644 index 20adde7b..00000000 --- a/test-devices/composite-stm32/src/main.cpp +++ /dev/null @@ -1,341 +0,0 @@ -// -// Java Does USB -// Loopback device for testing -// -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// -// Main program -// - -#include -#include -#include -#include -#include - -#include - -#include "circ_buf.h" -#include "common.h" -#include "usb_descriptor.h" -#include "usb_bos_desc.h" - -#define USB_CDC_REQ_GET_LINE_CODING 0x21 - -static void on_usb_set_config(usbd_device *usbd_dev, uint16_t wValue); -static usbd_request_return_codes on_vendor_control_request(usbd_device *usbd_dev, usb_setup_data *req, uint8_t **buf, - uint16_t *len, usbd_control_complete_callback *complete); -static usbd_request_return_codes on_class_control_request(usbd_device *usbd_dev, usb_setup_data *req, uint8_t **buf, - uint16_t *len, usbd_control_complete_callback *complete); -static void on_usb_loopback_received(usbd_device *usbd_dev, uint8_t ep); -static void on_usb_loopback_transmitted(usbd_device *usbd_dev, uint8_t ep); -static void on_usb_serial_received(usbd_device *usbd_dev, uint8_t ep); -static void on_usb_serial_transmitted(usbd_device *usbd_dev, uint8_t ep); -static void on_usb_serial_notif_transmitted(usbd_device *usbd_dev, uint8_t ep); -static void on_usb_echo_received(usbd_device *usbd_dev, uint8_t ep); -static void on_usb_echo_transmitted(usbd_device *usbd_dev, uint8_t ep); -static void check_loopback_buffers(); -static void check_serial_buffers(); - -// USB device instance -static usbd_device *usb_device; - -// buffer for control requests -static uint8_t usbd_control_buffer[256]; - -// Circular buffer for loopback test endpoints -static circ_buf<1024> loopback_buffer; - -// Minimum free space in circular buffer for requesting more packets -static constexpr int MIN_FREE_SPACE = 2 * BULK_MAX_PACKET_SIZE; - -// indicates if loopback data is being transmitted -static bool is_loopback_tx = false; - -// indicates if the loopback RX endpoint is forced to NAK to prevent receiving further data -static bool is_loopback_rx_nak = false; - -// value that can be saved and retrieved with control requests -static uint32_t saved_value; - -// echo message -static char echo_msg[INTR_MAX_PACKET_SIZE]; - -// echo message length -static int echo_msg_len; - -// number of echos left to transmit (if > 1, RX endpointed is NAKed) -static int num_echos_left; - -// Circular buffer for serial port -static circ_buf<1024> serial_buffer; - -// indicates if serial data is being transmitted -static bool is_serial_tx = false; - -// indicates if the serial RX endpoint is forced to NAK to prevent receiving further data -static bool is_serial_rx_nak = false; - - -void init() { - // Enable required clocks - rcc_clock_setup_in_hse_8mhz_out_72mhz(); - rcc_periph_clock_enable(RCC_GPIOA); - rcc_periph_clock_enable(RCC_GPIOC); - rcc_periph_clock_enable(RCC_AFIO); - rcc_periph_clock_enable(RCC_SPI1); - rcc_periph_clock_enable(RCC_USB); - - // Initialize systick services - systick_init(); -} - -void usb_init() { - // reset USB peripheral - rcc_periph_reset_pulse(RST_USB); - - // Pull USB D+ (A12) low for 80ms to trigger device reenumeration - gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO12); - gpio_clear(GPIOA, GPIO12); - delay(80); - - usb_init_serial_num(); - - // create USB device - usb_device = usbd_init(&st_usbfs_v1_usb_driver, &usb_device_desc, usb_config_descs, usb_desc_strings, - sizeof(usb_desc_strings) / sizeof(usb_desc_strings[0]), usbd_control_buffer, - sizeof(usbd_control_buffer)); - - // Set callback for config calls - usbd_register_set_config_callback(usb_device, on_usb_set_config); - usb_dev_register_bos(usb_device, bos_descs, sizeof(bos_descs) / sizeof(bos_descs[0]), - msos_desc_set, MSOS_VENDOR_CODE); -} - -// Called when the host connects to the device and selects a configuration -void on_usb_set_config(usbd_device *usbd_dev, __attribute__((unused)) uint16_t wValue) { - usb_dev_register_bos(usb_device, bos_descs, sizeof(bos_descs) / sizeof(bos_descs[0]), - msos_desc_set, MSOS_VENDOR_CODE); - - // register control request handler for vendor specific requests (used for test) - usbd_register_control_callback(usbd_dev, USB_REQ_TYPE_VENDOR | USB_REQ_TYPE_INTERFACE, - USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, on_vendor_control_request); - - // register control request handler for class specific requests (used for CDC ACM) - usbd_register_control_callback(usbd_dev, USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, - USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, on_class_control_request); - - usbd_ep_setup(usbd_dev, EP_CDC_COMM, USB_ENDPOINT_ATTR_INTERRUPT, INTR_MAX_PACKET_SIZE, - on_usb_serial_notif_transmitted); - usbd_ep_setup(usbd_dev, EP_CDC_DATA_RX, USB_ENDPOINT_ATTR_BULK, BULK_MAX_PACKET_SIZE, on_usb_serial_received); - usbd_ep_setup(usbd_dev, EP_CDC_DATA_TX, USB_ENDPOINT_ATTR_BULK, BULK_MAX_PACKET_SIZE, on_usb_serial_transmitted); - - usbd_ep_setup(usbd_dev, EP_LOOPBACK_RX, USB_ENDPOINT_ATTR_BULK, BULK_MAX_PACKET_SIZE, on_usb_loopback_received); - usbd_ep_setup(usbd_dev, EP_LOOPBACK_TX, USB_ENDPOINT_ATTR_BULK, BULK_MAX_PACKET_SIZE, on_usb_loopback_transmitted); - usbd_ep_setup(usbd_dev, EP_ECHO_RX, USB_ENDPOINT_ATTR_INTERRUPT, INTR_MAX_PACKET_SIZE, on_usb_echo_received); - usbd_ep_setup(usbd_dev, EP_ECHO_TX, USB_ENDPOINT_ATTR_INTERRUPT, INTR_MAX_PACKET_SIZE, on_usb_echo_transmitted); - - loopback_buffer.reset(); - is_loopback_tx = false; - is_loopback_rx_nak = false; - num_echos_left = 0; - echo_msg_len = 0; - saved_value = 0; - serial_buffer.reset(); - is_serial_tx = false; - is_serial_rx_nak = false; -} - -// Called when loopback data has been received -void on_usb_loopback_received(usbd_device *usbd_dev, uint8_t ep) { - // Retrieve USB data (has side effect of setting endpoint to VALID) - uint8_t packet[BULK_MAX_PACKET_SIZE] __attribute__((aligned(4))); - int len = usbd_ep_read_packet(usbd_dev, ep, packet, sizeof(packet)); - - // copy data into circular buffer - loopback_buffer.add_data(packet, len); -} - -// Called when loopback data has been transmitted -void on_usb_loopback_transmitted(__attribute__((unused)) usbd_device *usbd_dev, __attribute__((unused)) uint8_t ep) { - is_loopback_tx = false; -} - -void check_loopback_buffers() { - // If RX is stopped and there is sufficient space in the buffer, resume it - if (is_loopback_rx_nak) { - if (loopback_buffer.avail_size() >= MIN_FREE_SPACE) { - usbd_ep_nak_set(usb_device, EP_LOOPBACK_RX, 0); - is_loopback_rx_nak = false; - } - - // If RX is enabled but the space in the buffer is low, stop it - } else { - // check if there is space for less than 2 packets - if (loopback_buffer.avail_size() < MIN_FREE_SPACE) { - // set endpoint from VALID to NAK - usbd_ep_nak_set(usb_device, EP_LOOPBACK_RX, 1); - is_loopback_rx_nak = true; - } - } - - // If no data is being transmitted and there is data in the buffer, transmit a packet - if (!is_loopback_tx && loopback_buffer.data_size() >= 0) { - uint8_t packet[BULK_MAX_PACKET_SIZE] __attribute__((aligned(4))); - int len = loopback_buffer.get_data(packet, BULK_MAX_PACKET_SIZE); - usbd_ep_write_packet(usb_device, EP_LOOPBACK_TX, packet, len); - is_loopback_tx = true; - } -} - -// Called when echo data has been received -void on_usb_echo_received(usbd_device *usbd_dev, uint8_t ep) { - // Retrieve USB data (has side effect of setting endpoint to VALID) - echo_msg_len = usbd_ep_read_packet(usbd_dev, EP_ECHO_RX, echo_msg, sizeof(echo_msg)); - usbd_ep_nak_set(usbd_dev, ep, 1); - - usbd_ep_write_packet(usbd_dev, EP_ECHO_TX, echo_msg, echo_msg_len); - num_echos_left = 2; -} - -// Called when echo data has been transmitted -void on_usb_echo_transmitted(__attribute__((unused)) usbd_device *usbd_dev, __attribute__((unused)) uint8_t ep) { - num_echos_left--; - if (num_echos_left > 0) { - usbd_ep_write_packet(usbd_dev, ep, echo_msg, echo_msg_len); - } else { - usbd_ep_nak_set(usbd_dev, EP_ECHO_RX, 0); - } -} - -// Called when serial data has been received -void on_usb_serial_received(usbd_device *usbd_dev, uint8_t ep) { - // Retrieve USB data (has side effect of setting endpoint to VALID) - uint8_t packet[BULK_MAX_PACKET_SIZE] __attribute__((aligned(4))); - int len = usbd_ep_read_packet(usbd_dev, ep, packet, sizeof(packet)); - - // copy data into circular buffer - serial_buffer.add_data(packet, len); -} - -// Called when serial data has been transmitted -void on_usb_serial_transmitted(__attribute__((unused)) usbd_device *usbd_dev, __attribute__((unused)) uint8_t ep) { - is_serial_tx = false; -} - -// Called when a serial state notification has been transmitted -void on_usb_serial_notif_transmitted(__attribute__((unused)) usbd_device *usbd_dev, - __attribute__((unused)) uint8_t ep) { - // not used -} - -void check_serial_buffers() { - // If RX is stopped and there is sufficient space in the buffer, resume it - if (is_serial_rx_nak) { - if (serial_buffer.avail_size() >= MIN_FREE_SPACE) { - usbd_ep_nak_set(usb_device, EP_CDC_DATA_RX, 0); - is_serial_rx_nak = false; - } - - // If RX is enabled but the space in the buffer is low, stop it - } else { - // check if there is space for less than 2 packets - if (serial_buffer.avail_size() < MIN_FREE_SPACE) { - // set endpoint from VALID to NAK - usbd_ep_nak_set(usb_device, EP_CDC_DATA_RX, 1); - is_serial_rx_nak = true; - } - } - - // If no data is being transmitted and there is data in the buffer, transmit a packet - if (!is_serial_tx && serial_buffer.data_size() >= 0) { - uint8_t packet[BULK_MAX_PACKET_SIZE] __attribute__((aligned(4))); - int len = serial_buffer.get_data(packet, BULK_MAX_PACKET_SIZE); - usbd_ep_write_packet(usb_device, EP_CDC_DATA_TX, packet, len); - is_serial_tx = true; - } -} - -usbd_request_return_codes on_vendor_control_request(__attribute__((unused)) usbd_device *usbd_dev, usb_setup_data *req, - uint8_t **buf, uint16_t *len, - __attribute__((unused)) usbd_control_complete_callback *complete) { - switch (req->bRequest) { - case 1: - if (req->wIndex == 2 && req->wLength == 0) { - saved_value = req->wValue; - return USBD_REQ_HANDLED; - } else { - return USBD_REQ_NOTSUPP; - } - break; - - case 2: - if (req->wIndex == 2 && req->wLength == 4) { - uint32_t *value = reinterpret_cast(*buf); - saved_value = *value; - return USBD_REQ_HANDLED; - } else { - return USBD_REQ_NOTSUPP; - } - break; - - case 3: - if (req->wIndex == 2) { - uint8_t *value = reinterpret_cast(&saved_value); - *len = std::min(*len, (uint16_t)4); - memcpy(*buf, value, *len); - return USBD_REQ_HANDLED; - } else { - return USBD_REQ_NOTSUPP; - } - break; - - default:; // fall through - } - - return USBD_REQ_NEXT_CALLBACK; -} - -// Process ACM requests on control endpoint -usbd_request_return_codes on_class_control_request(__attribute__((unused)) usbd_device *usbd_dev, usb_setup_data *req, - uint8_t **buf, uint16_t *len, - __attribute__((unused)) usbd_control_complete_callback *complete) { - switch (req->bRequest) { - case USB_CDC_REQ_SET_LINE_CODING: - if (*len < sizeof(struct usb_cdc_line_coding)) - return USBD_REQ_NOTSUPP; - - return USBD_REQ_HANDLED; - - case USB_CDC_REQ_GET_LINE_CODING: { - if (*len < sizeof(struct usb_cdc_line_coding)) - return USBD_REQ_NOTSUPP; - - struct usb_cdc_line_coding *line_coding = (struct usb_cdc_line_coding *)*buf; - line_coding->dwDTERate = 115200; - line_coding->bDataBits = 8; - line_coding->bParityType = 0; - line_coding->bCharFormat = 0; - - *len = sizeof(struct usb_cdc_line_coding); - return USBD_REQ_HANDLED; - } - - case USB_CDC_REQ_SET_CONTROL_LINE_STATE: - return USBD_REQ_HANDLED; - } - return USBD_REQ_NEXT_CALLBACK; -} - -int main() { - init(); - usb_init(); - - while (true) { - usbd_poll(usb_device); - check_loopback_buffers(); - check_serial_buffers(); - } -} diff --git a/test-devices/composite-stm32/src/usb_bos.cpp b/test-devices/composite-stm32/src/usb_bos.cpp deleted file mode 100644 index 020c9da8..00000000 --- a/test-devices/composite-stm32/src/usb_bos.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// -// Java Does USB -// Loopback device for testing -// -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// -// USB binary device object store (BOS) -// - -#include "usb_bos.h" -#include - -static uint32_t build_descriptor(uint8_t* buf); -static enum usbd_request_return_codes on_bos_control_request( - usbd_device* usbd_dev, struct usb_setup_data* req, - uint8_t** buf, uint16_t* len, usbd_control_complete_callback* complete); - -static inline int imin(int a, int b) { return a < b ? a : b; } -static inline int descriptor_type(uint16_t wValue) { return wValue >> 8; } -static inline int descriptor_index(uint16_t wValue) { return wValue & 0xFF; } - -static const usb_bos_device_capability_desc* const* bos_descs; -static int num_bos_descs; -static const usb_msos20_desc_set_header* msos_desc_set; -static uint8_t msos_vendor_code; - - -void usb_dev_register_bos(usbd_device* device, - const usb_bos_device_capability_desc* const * descs, int num_descs, - const usb_msos20_desc_set_header* msos_set, uint8_t vendor_code) { - - bos_descs = descs; - num_bos_descs = num_descs; - msos_desc_set = msos_set; - msos_vendor_code = vendor_code; - - usbd_register_control_callback(device, USB_REQ_TYPE_IN | USB_REQ_TYPE_DEVICE, - USB_REQ_TYPE_DIRECTION | USB_REQ_TYPE_RECIPIENT, on_bos_control_request); -} - -#define APPEND_TO_DESC(data_ptr, data_len) \ - memcpy(buf_end, data_ptr, data_len); \ - buf_end += data_len; - -uint32_t build_descriptor(uint8_t* buf) { - uint8_t* buf_end = buf; - - usb_bos_desc root_desc = { - .bLength = sizeof(usb_bos_desc), - .bDescriptorType = USB_DT_BOS, - .wTotalLength = 0, - .bNumDeviceCaps = (uint8_t) num_bos_descs - }; - - APPEND_TO_DESC(&root_desc, sizeof(root_desc)) - - for (int i = 0; i < num_bos_descs; i++) { - APPEND_TO_DESC(bos_descs[i], bos_descs[i]->bLength) - } - - uint32_t length = buf_end - buf; - - // use memcpy() as buffer might not be word aligned - memcpy(buf + 2, &length, sizeof(uint16_t)); - - return length; -} - -#undef APPEND_TO_DESC - -enum usbd_request_return_codes on_bos_control_request( - __attribute__((unused)) usbd_device* dev, struct usb_setup_data* req, uint8_t** buf, uint16_t* len, - __attribute__((unused)) usbd_control_complete_callback* complete) { - - if (req->bmRequestType == (USB_REQ_TYPE_IN | USB_REQ_TYPE_STANDARD | USB_REQ_TYPE_DEVICE)) { - - if (req->bRequest == USB_REQ_GET_DESCRIPTOR && descriptor_type(req->wValue) == USB_DT_BOS) { - - // USB BOS descriptor (incl. Microsoft OS 2.0 platform capability descriptor) - if (descriptor_index(req->wValue) != 0) - return USBD_REQ_NOTSUPP; - *len = imin(*len, build_descriptor(*buf)); - return USBD_REQ_HANDLED; - } - - } else if (req->bmRequestType == (USB_REQ_TYPE_IN | USB_REQ_TYPE_VENDOR | USB_REQ_TYPE_DEVICE)) { - // Microsoft OS 2.0 descriptor - if (req->bRequest == msos_vendor_code && req->wValue == 0 && req->wIndex == USB_MSOS20_CTRL_INDEX_DESC) { - memcpy(*buf, msos_desc_set, msos_desc.wMSOSDescriptorSetTotalLength); - *len = imin(*len, msos_desc.wMSOSDescriptorSetTotalLength); - return USBD_REQ_HANDLED; - } - } - - return USBD_REQ_NEXT_CALLBACK; -} diff --git a/test-devices/composite-stm32/src/usb_bos_desc.c b/test-devices/composite-stm32/src/usb_bos_desc.c deleted file mode 100644 index eeb6039d..00000000 --- a/test-devices/composite-stm32/src/usb_bos_desc.c +++ /dev/null @@ -1,89 +0,0 @@ -// -// Java Does USB -// Loopback device for testing -// -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// -// USB BOS descriptor to install WinUSB for loopback test interface -// - -#include "usb_bos_desc.h" - -#define DEV_INTF_GUID_NAME u"DeviceInterfaceGUIDs" -#define DEV_INTF_GUID_DATA u"{049CD59E-33EE-4CB2-B0BB-1C49F3CB6358}" - -const struct msos20_desc_set { - usb_msos20_desc_set_header descSetHeader; - usb_msos20_desc_subset_header_config descSubsetConfig; - usb_msos20_desc_subset_header_function descSubsetFunction; - usb_msos20_desc_compatible_id descCompId; - struct { - uint16_t wLength; - uint16_t wDescriptorType; - uint16_t wPropertyDataType; - uint16_t wPropertyNameLength; - uint16_t propertyName[sizeof(DEV_INTF_GUID_NAME) / 2]; - uint16_t wPropertyDataLength; - uint16_t propertyData[sizeof(DEV_INTF_GUID_DATA) / 2 + 1]; - } devIntfGuid; - -} msos_set = { - - .descSetHeader = { - .wLength = sizeof(usb_msos20_desc_set_header), - .wDescriptorType = USB_MSOS20_DT_SET_HEADER_DESCRIPTOR, - .dwWindowsVersion = USB_MSOS20_WIN_VER_8_1, - .wTotalLength = sizeof(msos_set) - }, - .descSubsetConfig = { - .wLength = sizeof(usb_msos20_desc_subset_header_config), - .wDescriptorType = USB_MSOS20_DT_SUBSET_HEADER_CONFIGURATION, - .bConfigurationValue = 0, - .wTotalLength = sizeof(msos_set.descSubsetConfig) + sizeof(msos_set.descSubsetFunction) + - sizeof(msos_set.descCompId) + sizeof(msos_set.devIntfGuid), - }, - .descSubsetFunction = { - .wLength = sizeof(usb_msos20_desc_subset_header_function), - .wDescriptorType = USB_MSOS20_DT_SUBSET_HEADER_FUNCTION, - .bFirstInterface = 2, - .wTotalLength = sizeof(msos_set.descSubsetFunction) + sizeof(msos_set.descCompId) + - sizeof(msos_set.devIntfGuid), - }, - .descCompId = { - .wLength = sizeof(usb_msos20_desc_compatible_id), - .wDescriptorType = USB_MSOS20_DT_FEATURE_COMPATBLE_ID, - .compatibleID = "WINUSB\0\0", - .subCompatibleID = "\0\0\0\0\0\0\0\0" - }, - .devIntfGuid = { - .wLength = sizeof(msos_set.devIntfGuid), - .wDescriptorType = USB_MSOS20_DT_FEATURE_REG_PROPERTY, - .wPropertyDataType = USB_MSOS20_PROP_DATA_TYPE_STRING_MULTI, - .wPropertyNameLength = sizeof(msos_set.devIntfGuid.propertyName), - .propertyName = DEV_INTF_GUID_NAME, - .wPropertyDataLength = sizeof(msos_set.devIntfGuid.propertyData), - .propertyData = DEV_INTF_GUID_DATA "\0" - } -}; - -const usb_msos20_desc_set_header* msos_desc_set = &msos_set.descSetHeader; - -const usb_msos20_platform_desc msos_desc = { - .bLength = sizeof(usb_msos20_platform_desc), - .bDescriptorType = USB_DT_DEVICE_CAPABILITY, - .bDevCapabilityType = USB_DEV_CAPA_PLATFORM, - .bReserved = 0, - .platformCapabilityUUID = USB_PLATFORM_CAPABILITY_MICROSOFT_OS20_UUID, - .dwWindowsVersion = USB_MSOS20_WIN_VER_8_1, - .wMSOSDescriptorSetTotalLength = sizeof(msos_set), - .bMS_VendorCode = MSOS_VENDOR_CODE, - .bAltEnumCode = 0 -}; - -// BOS device capability descriptors -const usb_bos_device_capability_desc* const bos_descs[] = { - // Microsoft OS 2.0 descriptor (for autmatic WinUSB installation) - (const usb_bos_device_capability_desc*)&msos_desc -}; diff --git a/test-devices/composite-stm32/src/usb_descriptor.cpp b/test-devices/composite-stm32/src/usb_descriptor.cpp deleted file mode 100644 index 05827bf5..00000000 --- a/test-devices/composite-stm32/src/usb_descriptor.cpp +++ /dev/null @@ -1,308 +0,0 @@ -// -// Java Does USB -// Loopback device for testing -// -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// -// USB descriptor -// - -#include "usb_descriptor.h" - -#include -#include - -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) - -static void put_hex(uint32_t value, char *buf, int len); - -#define USB_VID 0xcafe // Vendor ID -#define USB_PID 0xcea0 // Product ID -#define USB_DEVICE_REL 0x0034 // release 0.3.3 - -// Interface index -#define INTF_CDC_COMM 0 -#define INTF_CDC_DATA 1 -#define INTF_LOOPBACK 2 - -static char serial_num[13]; - -const char *const usb_desc_strings[] = { - "JavaDoesUSB", // USB Manufacturer - "Loopback", // USB Product - serial_num, // Serial number, - "Loopback Serial Port" // Function description -}; - -enum usb_strings_index { // Index of USB strings. Must sync with above, starts from 1. - USB_STRINGS_MANUFACTURER_ID = 1, - USB_STRINGS_PRODUCT_ID, - USB_STRINGS_SERIAL_NUMBER_ID, - USB_STRINGS_SERIAL_PORT_ID, -}; - -typedef struct { - struct usb_cdc_header_descriptor header; - struct usb_cdc_call_management_descriptor call_mgmt; - struct usb_cdc_acm_descriptor acm; - struct usb_cdc_union_descriptor cdc_union; -} __attribute__((packed)) cdcacm_functional_descriptors; - -// --- CDC ACM --- - -// CDC communicatoin endpoint -static const usb_endpoint_descriptor cdc_comm_ep_desc[] = { - { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_CDC_COMM, - .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, - .wMaxPacketSize = INTR_MAX_PACKET_SIZE, - .bInterval = 32, - .extra = nullptr, - .extralen = 0, - } -}; - -// CDC data endpoints -static const usb_endpoint_descriptor cdc_data_ep_desc[] = { - { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_CDC_DATA_RX, - .bmAttributes = USB_ENDPOINT_ATTR_BULK, - .wMaxPacketSize = BULK_MAX_PACKET_SIZE, - .bInterval = 1, - .extra = nullptr, - .extralen = 0, - }, - { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_CDC_DATA_TX, - .bmAttributes = USB_ENDPOINT_ATTR_BULK, - .wMaxPacketSize = BULK_MAX_PACKET_SIZE, - .bInterval = 1, - .extra = nullptr, - .extralen = 0, - } -}; - -// CDC ACM function descriptor -static const cdcacm_functional_descriptors cdc_func_desc = { - .header = { - .bFunctionLength = sizeof(usb_cdc_header_descriptor), - .bDescriptorType = CS_INTERFACE, - .bDescriptorSubtype = USB_CDC_TYPE_HEADER, - .bcdCDC = 0x0110, - }, - .call_mgmt = { - // see chapter 5.3.1 in PSTN120 - .bFunctionLength = sizeof(usb_cdc_call_management_descriptor), - .bDescriptorType = CS_INTERFACE, - .bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT, - .bmCapabilities = 0, // no call management - .bDataInterface = INTF_CDC_DATA, - }, - .acm = { - // see chapter 5.3.2 in PSTN120 - .bFunctionLength = sizeof(usb_cdc_acm_descriptor), - .bDescriptorType = CS_INTERFACE, - .bDescriptorSubtype = USB_CDC_TYPE_ACM, - .bmCapabilities = 2, // device supports the request combination of Set_Line_Coding, Set_Control_Line_State, - // Get_Line_Coding, and the notification Serial_State - }, - .cdc_union = { - .bFunctionLength = sizeof(usb_cdc_union_descriptor), - .bDescriptorType = CS_INTERFACE, - .bDescriptorSubtype = USB_CDC_TYPE_UNION, - .bControlInterface = INTF_CDC_COMM, - .bSubordinateInterface0 = INTF_CDC_DATA, - } -}; - -// CDC interfaces descriptors -static const usb_interface_descriptor cdc_comm_if_desc[] = { - { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = INTF_CDC_COMM, - .bAlternateSetting = 0, - .bNumEndpoints = ARRAY_SIZE(cdc_comm_ep_desc), - .bInterfaceClass = USB_CLASS_CDC, - .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, - .bInterfaceProtocol = USB_CDC_PROTOCOL_AT, - .iInterface = 0, - .endpoint = cdc_comm_ep_desc, - .extra = &cdc_func_desc, - .extralen = sizeof(cdc_func_desc), - } -}; - -static const usb_interface_descriptor cdc_data_if_desc[] = { - { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = INTF_CDC_DATA, - .bAlternateSetting = 0, - .bNumEndpoints = ARRAY_SIZE(cdc_data_ep_desc), - .bInterfaceClass = USB_CLASS_DATA, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = 0, - .endpoint = cdc_data_ep_desc, - .extra = nullptr, - .extralen = 0, - } -}; - -// CDC interface association -static const usb_iface_assoc_descriptor cdc_assoc_desc = { - .bLength = USB_DT_INTERFACE_ASSOCIATION_SIZE, - .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, - .bFirstInterface = INTF_CDC_COMM, - .bInterfaceCount = 2, - .bFunctionClass = USB_CLASS_CDC, - .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, - .bFunctionProtocol = USB_CDC_PROTOCOL_AT, - .iFunction = USB_STRINGS_SERIAL_PORT_ID, -}; - -// --- Loopback test interface --- - -// Test endpoints -static const struct usb_endpoint_descriptor loopback_endpoint_descs[] = { - { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_LOOPBACK_RX, - .bmAttributes = USB_ENDPOINT_ATTR_BULK, - .wMaxPacketSize = BULK_MAX_PACKET_SIZE, - .bInterval = 0, - .extra = nullptr, - .extralen = 0, - }, - { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_LOOPBACK_TX, - .bmAttributes = USB_ENDPOINT_ATTR_BULK, - .wMaxPacketSize = BULK_MAX_PACKET_SIZE, - .bInterval = 0, - .extra = nullptr, - .extralen = 0, - }, - { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_ECHO_RX, - .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, - .wMaxPacketSize = INTR_MAX_PACKET_SIZE, - .bInterval = 16, - .extra = nullptr, - .extralen = 0, - }, - { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_ECHO_TX, - .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, - .wMaxPacketSize = INTR_MAX_PACKET_SIZE, - .bInterval = 16, - .extra = nullptr, - .extralen = 0, - }, -}; - -// Test interface -static const struct usb_interface_descriptor loopback_if_descs[] = { - { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = INTF_LOOPBACK, - .bAlternateSetting = 0, - .bNumEndpoints = ARRAY_SIZE(loopback_endpoint_descs), - .bInterfaceClass = USB_CLASS_VENDOR, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, // vendor specific - .iInterface = 0, - .endpoint = loopback_endpoint_descs, - .extra = nullptr, - .extralen = 0, - }, -}; - -static const struct usb_interface usb_interfaces[] = { - { - .cur_altsetting = nullptr, - .num_altsetting = ARRAY_SIZE(cdc_comm_if_desc), - .iface_assoc = &cdc_assoc_desc, - .altsetting = cdc_comm_if_desc, - }, - { - .cur_altsetting = nullptr, - .num_altsetting = ARRAY_SIZE(cdc_data_if_desc), - .iface_assoc = nullptr, - .altsetting = cdc_data_if_desc, - }, - { - .cur_altsetting = nullptr, - .num_altsetting = ARRAY_SIZE(loopback_if_descs), - .iface_assoc = nullptr, - .altsetting = loopback_if_descs, - }, -}; - -const struct usb_config_descriptor usb_config_descs[] = { - { - .bLength = USB_DT_CONFIGURATION_SIZE, - .bDescriptorType = USB_DT_CONFIGURATION, - .wTotalLength = 0, - .bNumInterfaces = ARRAY_SIZE(usb_interfaces), - .bConfigurationValue = 1, - .iConfiguration = 0, - .bmAttributes = 0x80, // bus-powered, i.e. it draws power from USB bus - .bMaxPower = 0xfa, // 500 mA - .interface = usb_interfaces, - }, -}; - -const struct usb_device_descriptor usb_device_desc = { - .bLength = USB_DT_DEVICE_SIZE, - .bDescriptorType = USB_DT_DEVICE, - .bcdUSB = 0x0210, // USB version 2.1.0 (minimum version for BOS) - .bDeviceClass = 0xef, - .bDeviceSubClass = 0x02, - .bDeviceProtocol = 0x01, - .bMaxPacketSize0 = 64, - .idVendor = USB_VID, - .idProduct = USB_PID, - .bcdDevice = USB_DEVICE_REL, - .iManufacturer = USB_STRINGS_MANUFACTURER_ID, - .iProduct = USB_STRINGS_PRODUCT_ID, - .iSerialNumber = USB_STRINGS_SERIAL_NUMBER_ID, - .bNumConfigurations = ARRAY_SIZE(usb_config_descs), -}; - -void usb_init_serial_num() { - uint32_t id0 = DESIG_UNIQUE_ID0; - uint32_t id1 = DESIG_UNIQUE_ID1; - uint32_t id2 = DESIG_UNIQUE_ID2; - - id0 += id2; - - put_hex(id0, serial_num, 8); - put_hex(id1, serial_num + 8, 4); - serial_num[12] = 0; -} - -const static char HEX_DIGITS[] = "0123456789ABCDEF"; - -void put_hex(uint32_t value, char *buf, int len) { - for (int idx = 0; idx < len; idx++) { - buf[idx] = HEX_DIGITS[value >> 28]; - value = value << 4; - } -} diff --git a/test-devices/composite-stm32/src/usb_descriptors.c b/test-devices/composite-stm32/src/usb_descriptors.c new file mode 100644 index 00000000..f45bfa1d --- /dev/null +++ b/test-devices/composite-stm32/src/usb_descriptors.c @@ -0,0 +1,175 @@ +// +// Java Does USB +// Loopback device for testing +// +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Board specific function (HAL) +// + +#include "usb_descriptors.h" + +#include "board.h" +#include "tusb.h" +#include "class/cdc/cdc.h" +#include "vendor_custom.h" + + +// --- Device Descriptor --- + +tusb_desc_device_t const desc_device = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0210, // 2.1.0 (minimum for BOS) + + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = 0xCAFE, + .idProduct = 0xCEA0, + .bcdDevice = 0x0036, // version 0.3.6 + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01 +}; + +// Invoked when a GET DEVICE DESCRIPTOR request is received. +// Return a pointer to the descriptor. +uint8_t const* tud_descriptor_device_cb(void) { + return (uint8_t const*)&desc_device; +} + + +// --- Configuration Descriptor --- + +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + 8 + 9 + 9 + 7 + 7) + +uint8_t const desc_configuration[] = { + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, INTF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 500), + // CDC interfaces + TUD_CDC_DESCRIPTOR(INTF_CDC_COMM, 0, EP_CDC_COMM, 8, EP_CDC_DATA_RX, EP_CDC_DATA_TX, BULK_MAX_PACKET_SIZE), + // Interface association descriptor (IAD) + CUSTOM_VENDOR_INTERFACE_ASSOCIATION(INTF_LOOPBACK_CTRL, 2, 0x04), + // Echo interface (no endpoint, just control messages) + CUSTOM_VENDOR_INTERFACE(INTF_LOOPBACK_CTRL, 0), + // Loopback interface + CUSTOM_VENDOR_INTERFACE(INTF_LOOPBACK, 2), + // Loopback endpoint OUT + CUSTOM_VENDOR_BULK_ENDPOINT(EP_LOOPBACK_RX, BULK_MAX_PACKET_SIZE), + // Loopback endpoint IN + CUSTOM_VENDOR_BULK_ENDPOINT(EP_LOOPBACK_TX, BULK_MAX_PACKET_SIZE) +}; + +// Invoked when a GET CONFIGURATION DESCRIPTOR request is recieved. +// Return a pointer to descriptor. +// Descriptor contents must exist long enough for transfer to complete +uint8_t const* tud_descriptor_configuration_cb(uint8_t configuration_index) { + (void)configuration_index; + return desc_configuration; +} + + +// --- BOS Descriptor --- + +#if CFG_WINUSB == OPT_WINUSB_MSOS20 + +#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN) + +#define MS_OS_20_DESC_LEN 0xB2 + +// BOS Descriptor is required for webUSB +uint8_t const desc_bos[] = { + // total length, number of device caps + TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 1), + + // Microsoft OS 2.0 descriptor + TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, MSOS_VENDOR_CODE) +}; + +uint8_t const * tud_descriptor_bos_cb(void) { + return desc_bos; +} + + +uint8_t const desc_ms_os_20[] = { + // Set header: length, type, windows version, total length + U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(MS_OS_20_DESC_LEN), + + // Configuration subset header: length, type, configuration index, reserved, configuration total length + U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_CONFIGURATION), 0, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A), + + // Function Subset header: length, type, first interface, reserved, subset length + U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION), INTF_LOOPBACK_CTRL, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08), + + // MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID + U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sub-compatible + + // MS OS 2.0 Registry property descriptor: length, type + U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08-0x08-0x14), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY), + U16_TO_U8S_LE(0x0007), U16_TO_U8S_LE(0x002A), // wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUIDs\0" in UTF-16 + 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00, + 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 0x00, + U16_TO_U8S_LE(0x0050), // wPropertyDataLength + //bPropertyData: “{82DF5D1A-BD37-431C-81B7-52EB2093B98F}”. + '{', 0x00, '8', 0x00, '2', 0x00, 'D', 0x00, 'F', 0x00, '5', 0x00, 'D', 0x00, '1', 0x00, 'A', 0x00, '-', 0x00, + 'B', 0x00, 'D', 0x00, '3', 0x00, '7', 0x00, '-', 0x00, '4', 0x00, '3', 0x00, '1', 0x00, 'C', 0x00, '-', 0x00, + '8', 0x00, '1', 0x00, 'B', 0x00, '7', 0x00, '-', 0x00, '5', 0x00, '2', 0x00, 'E', 0x00, 'B', 0x00, '2', 0x00, + '0', 0x00, '9', 0x00, '3', 0x00, 'B', 0x00, '9', 0x00, '8', 0x00, 'F', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +TU_VERIFY_STATIC(sizeof(desc_ms_os_20) == MS_OS_20_DESC_LEN, "Incorrect size"); + +#endif + + +// --- String Descriptors --- + +// table with strings +const char* const string_table[] = { + 0, // 0 - supported languages (see below) + "JavaDoesUSB", // 1 - manufacturer + "Composite", // 2 - product + board_serial_num, // 3 - serial number + "Loopback IAD" // 4 - interface association descriptor +}; + + +static uint16_t str_desc_buf[32]; + +// Invoked when a GET STRING DESCRIPTOR request is received. +// Return pointer to string descriptor. +uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) { + (void)langid; + + int str_len; + + if (index == 0) { + str_desc_buf[1] = 0x0409; // US English + str_len = 1; + + } else { + if (index >= TU_ARRAY_SIZE(string_table)) + return NULL; + + const char* str = string_table[index]; + str_len = strlen(str); + + // Convert ASCII to UTF-16 + for (uint8_t i = 0; i < str_len; i++) + str_desc_buf[1 + i] = str[i]; + } + + // first byte is length (including header), second byte is string type + str_desc_buf[0] = (uint16_t)((2 * str_len + 2) | (TUSB_DESC_STRING << 8)); + + return str_desc_buf; +} diff --git a/test-devices/composite-stm32/src/usb_descriptors.h b/test-devices/composite-stm32/src/usb_descriptors.h new file mode 100644 index 00000000..1d6474c3 --- /dev/null +++ b/test-devices/composite-stm32/src/usb_descriptors.h @@ -0,0 +1,52 @@ +// +// Java Does USB +// Loopback device for testing +// +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// USB descriptor +// + +#pragma once + +#include + +#define OPT_WINUSB_NONE 0 +#define OPT_WINUSB_MSOS20 2 + +#ifndef CFG_WINUSB +#define CFG_WINUSB OPT_WINUSB_MSOS20 +#endif + + +// interfaces +enum { + INTF_CDC_COMM = 0, + INTF_CDC_DATA, + INTF_LOOPBACK_CTRL, + INTF_LOOPBACK, + INTF_NUM_TOTAL +}; + + +#define INTR_MAX_PACKET_SIZE 16 +#define BULK_MAX_PACKET_SIZE 64 + +// Endpoints +#define EP_CDC_COMM 0x83 +#define EP_CDC_DATA_RX 0x02 +#define EP_CDC_DATA_TX 0x81 + +#define EP_LOOPBACK_RX 0x01 +#define EP_LOOPBACK_TX 0x82 + +#if CFG_WINUSB == OPT_WINUSB_MSOS20 + +#define MSOS_VENDOR_CODE 0x44 +extern uint8_t const desc_ms_os_20[]; + +#endif + +void usb_init_serial_num(); diff --git a/test-devices/composite-stm32/src/vendor_custom.c b/test-devices/composite-stm32/src/vendor_custom.c new file mode 100644 index 00000000..98c6c399 --- /dev/null +++ b/test-devices/composite-stm32/src/vendor_custom.c @@ -0,0 +1,130 @@ +// +// Java Does USB +// Loopback device for testing +// +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// USB driver for interfaces with vendor specific class. +// The interface can have any number of Bulk and Interrupt endpoints. +// + +#include "tusb_option.h" + +#if (CFG_TUD_ENABLED && CFG_VENDOR_ADVANCED) + +#include "device/usbd.h" +#include "vendor_custom.h" + + +static void cv_init(void); +static void cv_reset(uint8_t rhport); +static uint16_t cv_open(uint8_t rhport, tusb_desc_interface_t const* desc_intf, uint16_t max_len); +static bool cv_control_xfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); +static bool cv_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); + +const usbd_class_driver_t cust_vendor_driver = { + .init = cv_init, + .reset = cv_reset, + .open = cv_open, + .control_xfer_cb = cv_control_xfer, + .xfer_cb = cv_xfer_cb, + .sof = NULL +}; + + +void cv_init(void) { + // nothing to do +} + +void cv_reset(uint8_t rhport) { + // nothing to do +} + +uint16_t cv_open(uint8_t rhport, tusb_desc_interface_t const *desc_intf, uint16_t max_len) { + // return 0 if interface class is not "vendor specific" + TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == desc_intf->bInterfaceClass, 0); + + uint8_t const *p_desc = (uint8_t const *)desc_intf; + uint8_t const *p_end = p_desc + max_len; + + for (int i = 0; i < CFG_VENDOR_ADVANCED_NUM_INTF; i++) { + TU_VERIFY(p_desc + sizeof(tusb_desc_interface_t) <= p_end, 0); + + tusb_desc_interface_t const *intf = (tusb_desc_interface_t const *)p_desc; + int num_endpoints = intf->bNumEndpoints; + + tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *)(p_desc + sizeof(tusb_desc_interface_t)); + TU_VERIFY((uint8_t const *)(desc_ep + num_endpoints) <= p_end, 0); + + // open all endpoints + for (int i = 0; i < num_endpoints; i++) + TU_ASSERT(usbd_edpt_open(rhport, desc_ep + i)); + + p_desc = (uint8_t const *)(desc_ep + num_endpoints); + } + + uint16_t processed_bytes = p_desc - (uint8_t const *)desc_intf; + + cust_vendor_intf_open_cb(desc_intf->bInterfaceNumber); + + return processed_bytes; +} + +bool cv_control_xfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) { + TU_VERIFY(TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type); + + if (request->bRequest == TUSB_REQ_CLEAR_FEATURE + && request->wValue == TUSB_REQ_FEATURE_EDPT_HALT + && request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_ENDPOINT + && cust_vendor_halt_cleared_cb != NULL) { + uint8_t const ep_addr = tu_u16_low(request->wIndex); + cust_vendor_halt_cleared_cb(ep_addr); + return true; + } + + return false; +} + +bool cv_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { + + if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { + cust_vendor_tx_cb(ep_addr, xferred_bytes); + + } else { + cust_vendor_rx_cb(ep_addr, xferred_bytes); + } + + return true; +} + +void cust_vendor_prepare_recv(uint8_t ep_addr, void* buf, uint32_t buf_len) { + + uint8_t const rhport = 0; + + if (usbd_edpt_busy(rhport, ep_addr)) + return; + + usbd_edpt_xfer(rhport, ep_addr, buf, buf_len); +} + +void cust_vendor_start_transmit(uint8_t ep_addr, void const * data, uint32_t data_len) { + + uint8_t const rhport = 0; + + if (usbd_edpt_busy(rhport, ep_addr)) + return; + + usbd_edpt_xfer(rhport, ep_addr, (void*) data, data_len); +} + +bool cust_vendor_is_receiving(uint8_t ep_addr) { + return usbd_edpt_busy(0, ep_addr); +} + +bool cust_vendor_is_transmitting(uint8_t ep_addr) { + return usbd_edpt_busy(0, ep_addr); +} + +#endif diff --git a/test-devices/composite-stm32/src/vendor_custom.h b/test-devices/composite-stm32/src/vendor_custom.h new file mode 100644 index 00000000..da0618c0 --- /dev/null +++ b/test-devices/composite-stm32/src/vendor_custom.h @@ -0,0 +1,128 @@ +// +// Java Does USB +// Loopback device for testing +// +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// USB driver for interfaces with vendor specific class. +// The interface can have any number of bulk and interrupt endpoints. +// + +#pragma once + +#include "common/tusb_common.h" +#include "device/usbd_pvt.h" + +// --- Macro to create USB configuration descriptor + +// Interface descriptor: interface number, number of endponts +#define CUSTOM_VENDOR_INTERFACE(_itfnum, _numeps) \ + /* Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, _numeps, TUSB_CLASS_VENDOR_SPECIFIC, 0x00, 0x00, 0 + +// Bulk endpoint descriptor: endpoint address, packet size +#define CUSTOM_VENDOR_BULK_ENDPOINT(_epaddr, _packetsize) \ + /* Endpoint */\ + 7, TUSB_DESC_ENDPOINT, _epaddr, TUSB_XFER_BULK, U16_TO_U8S_LE(_packetsize), 0 + +// Interrupt endpoint descriptor: endpoint address, packet size, interval +#define CUSTOM_VENDOR_INTERRUPT_ENDPOINT(_epaddr, _packetsize, _interval) \ + /* Endpoint */\ + 7, TUSB_DESC_ENDPOINT, _epaddr, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_packetsize), _interval + +// Interface association descriptor: first interface index, number of interfaces, string index of description +#define CUSTOM_VENDOR_INTERFACE_ASSOCIATION(_firstintf, _numintf, _strIndex) \ + /* Interface Association */\ + 8, TUSB_DESC_INTERFACE_ASSOCIATION, _firstintf, _numintf, TUSB_CLASS_VENDOR_SPECIFIC, 0x00, 0x00, _strIndex + +// --- Application API + +/** + * @brief Prepares to recieve data on an OUT endpoint. + * + * The buffer must stay valid until the `cust_vendor_rx_cb()` callback + * reports that data has been received. + * + * If the OUT endpoint is already receiving data, this function does nothing. + * + * @param ep_addr endpoint address (1 to 127) + * @param buf pointer to buffer for received data + * @param buf_len length of buffer + */ +void cust_vendor_prepare_recv(uint8_t ep_addr, void* buf, uint32_t buf_len); + +/** + * @brief Gets if the endpoint is busy receiving data. + * + * @param ep_addr endpoint address (1 to 127) + * @return true if the endpoint is busy receiving + * @return false it the endpoint is idle + */ +bool cust_vendor_is_receiving(uint8_t ep_addr); + +/** + * @brief Transmits data on an IN endpoint. + * + * The data must stay valid until the `cust_vendor_tx_cb()` callback + * reports that the data has been transmitted. + * + * If the IN endpoint is already transmitting data, this function does nothing. + * + * @param ep_addr endpoint address (129 to 255) + * @param data pointer to data to transmit + * @param data_len number of bytes to transmit + */ +void cust_vendor_start_transmit(uint8_t ep_addr, void const * data, uint32_t data_len); + +/** + * @brief Gets if the endpoint is busy transmitting data. + * + * @param ep_addr endpoint address (129 to 255) + * @return true if the endpoint is busy transmitting + * @return false it the endpoint is idle + */ +bool cust_vendor_is_transmitting(uint8_t ep_addr); + + +// --- Application Callback API + +/** + * @brief Invoked when new data has been received on an OUT endpoint. + * + * @param ep_addr endpoint address (1 to 127) + * @param recv_bytes number of received bytes + */ +TU_ATTR_WEAK void cust_vendor_rx_cb(uint8_t ep_addr, uint32_t recv_bytes); + +/** + * @brief Invoked when data has been transmitted on an IN endpoint. + * + * @param ep_addr endpoint address (129 to 255) + * @param sent_bytes number of sent bytes + */ +TU_ATTR_WEAK void cust_vendor_tx_cb(uint8_t ep_addr, uint32_t sent_bytes); + +/** + * @brief Invoked when an interface of this class has been opened. + * + * This function is called as part of a SET CONFIGURATION control request. + * + * @param intf interface number + */ +TU_ATTR_WEAK void cust_vendor_intf_open_cb(uint8_t intf); + +/** + * @brief Invoked when an endpoint's halt condition has been cleared. + * + * This function is called as part of a SET FEATURE control request. + * + * @param ep_addr endpoint address + */ +TU_ATTR_WEAK void cust_vendor_halt_cleared_cb(uint8_t ep_addr); + + +// --- Driver to be registered in usbd_app_driver_get_cb() + +const usbd_class_driver_t cust_vendor_driver; diff --git a/test-devices/loopback-stm32/.vscode/settings.json b/test-devices/loopback-stm32/.vscode/settings.json new file mode 100644 index 00000000..11ada5d0 --- /dev/null +++ b/test-devices/loopback-stm32/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "files.associations": { + "usbd.h": "c", + "usbd_pvt.h": "c", + "dwc2_stm32.h": "c", + "stm32f7xx.h": "c", + "stm32f4xx.h": "c", + "stm32f401xc.h": "c", + "stm32f401xe.h": "c", + "tusb_option.h": "c" + } +} \ No newline at end of file diff --git a/test-devices/loopback-stm32/README.md b/test-devices/loopback-stm32/README.md index 29105953..45332099 100644 --- a/test-devices/loopback-stm32/README.md +++ b/test-devices/loopback-stm32/README.md @@ -1,21 +1,30 @@ # Loopback Test Device -For testing the *Java Does USB* library, a dedicated USB test device is needed. This is the code for an STM32F103C8 microcontroller. This microcontroller is found on many inexpensive development board, most notabily on the so called *Blue Pill*. Such boards are available for about 3 USD, including the required ST-Link programmer. +For testing the *Java Does USB* library, a dedicated USB test device is needed. This is the code for STM32 microcontroller. This microcontroller is found on many inexpensive development board, most notabily on the so called *Blue Pill* and *Black Pill* boards. They are available for about 3 USD. + +## Supported boards + +- BlackPill with STM32F401CC microcontroller +- BlackPill with STM32F411CE microcontroller +- BluePill with STM32F103C8 microcontroller +- STM32F723 Discovery board + +To upload the firmware, the STM32F4x microcontroller have a built-in USB bootloader. The STM32F1x microcontrollers need an ST-Link debug adapter (or a USB-to-serial converter). The STM32F723 Discovery board has a built-in ST-Link programmer. ## Test features ### Endpoints -| Endpoint | Transfer Type | Direction | Packet Size | Function | -| - | - | - | - | - | -| 0x00 | Control | Bidirectional | | See *Control requests* below | -| 0x01 | Bulk | Host to device | 64 bytes | Loopback: all data received on this endpoint are then transmitted on endpoint 0x82. | -| 0x82 | Bulk | Device to host | 64 bytes | Loopback: Transmits the data received on endpoint 0x01. | -| 0x03 | Interrupt | Host to device | 16 bytes | Echo: All packets received on this endpoint are transmitted twice on endpoint 0x83. | -| 0x83 | Interrupt | Device to host | 16 bytes | Echo: Transmits all packets received on endpoint 0x03 twice. | +| Endpoint | Transfer Type | Direction | Packet Size | Interface | Function | +| - | - | - | - | - | - | +| 0x00 | Control | Bidirectional | | 0 | See *Control requests* below | +| 0x01 | Bulk | Host to device | 64 bytes | 0 | Loopback: all data received on this endpoint are then transmitted on endpoint 0x82. | +| 0x82 | Bulk | Device to host | 64 bytes | 0 | Loopback: Transmits the data received on endpoint 0x01. | +| 0x03 | Interrupt | Host to device | 16 bytes | 0 | Echo: All packets received on this endpoint are transmitted twice on endpoint 0x83. | +| 0x83 | Interrupt | Device to host | 16 bytes | 0 | Echo: Transmits all packets received on endpoint 0x03 twice. | -The bulk endpoints 0x01 and 0x82 use an internal buffer of about 1000 bytes. Data up to this amount can be sent and received sequentially. If more data is sent without receiving at the same time, flow control kicks in and endpoint 0x01 will stop receiving data until there is room in the buffer. +The bulk endpoints 0x01 and 0x82 use an internal buffer of about 500 bytes. Data up to this amount can be sent and received sequentially. If more data is sent without receiving at the same time, flow control kicks in and endpoint 0x01 will stop receiving data until there is room in the buffer. ### Control requests @@ -27,28 +36,93 @@ Several vendor-specific control requests are supported for testing: | 0x41 | 0x01 | *value* | 0 | 0 | none | Host to device: *value* is saved in device | | 0x41 | 0x02 | 0 | 0 | 4 | *value* (32-bit LE) | Host to device: *value* is saved in device | | 0xC1 | 0x03 | 0 | 0 | 4 | *value* (32-bit LE) | Device to host: saved *value* is transmitted | +| 0x41 | 0x04 | 0 | 0 | 0 | none | Reset internal buffers. Used to put device in a predicatable state. | + +### Alternate interfaces + +Two alternate interfaces are implemented: + +- Alternate 0 (default): all endpoints are available +- Alternate 1: only the control endpoint and the bulk endpoints (0x01 and 0x82) are available + + +### Suspend / resume +The device can be put into suspend mode by the host. It will go into a low-power mode. This is indicated by the user LED turning off. The power LED will stay on. The device can be woken up by the host. -## Building +To put the device into suspended mode, put the host computer to sleep or supended mode. To wake it up, wake up the host computer. + +NOTE: *Due to a limitation of TinyUSB, the device will only go into suspended mode if the host has set a USB configuration. Usually it means that an application has communicated with the device after it was plugged in. The LED blinks as long as no USB configuration has been set.* + +NOTE: *Suspend/resume has not been implemented for the STM32F723 Discovery board.* + + + +## Building the firmware This project requires [PlatformIO](https://platformio.org/). The easiest way to get up and running is to use Visual Studio Code and then install the [PlatformIO IDE extension](https://marketplace.visualstudio.com/items?itemName=platformio.platformio-ide). -After the extension is installed, open this folder and then click checkbox icon (*Build* action) in the status bar. +After the extension is installed, open this folder and select your board type by clicking on "Default (tinyusb-stm32)" in the status bar. Wait until the status bar no longer indicates activity. Then click the checkbox icon (*Build* action) in the status bar. + +To load the firmware onto the board, either connect it via ST-Link programmer to the 4 pins on the short side of the board or use the built-in USB bootloader (BlackPill only). -To upload the code to the microcontroller: +For upload with a ST-Link programmer: - Connect the ST-Link programmer to the development board - Connect the ST-Link programmer to your computer - Click the arrow icon (*Upload* action) in the status bar +For other means of upload, see *Binary releases* below. + + +## Binary releases -## Binary release +The directory `bin` contains a pre-built firmware: -The directory `bin` contains a pre-built firmware. In order to upload it, a utility is needed, either [STM32CubeProgrammer](https://www.st.com/en/development-tools/stm32cubeprog.html) (requires an STM account, does not properly work on macOS) or the [open-source ST-Link command line utility](https://github.com/stlink-org/stlink). See the respective web site for installation instructions. +- `blackpill-f401cc.bin`: Firmware for BlackPill with STM32F401CC microcontroller +- `blackpill-f411ce.bin`: Firmware for BlackPill with STM32F411CE microcontroller +- `bluepill-f103c8.bin`: Firmware for BluePill with STM32F103C8 microcontroller +- `disco_f723ie.bin`: Firmware for STM32F723 Discovery board -If the commmand line utility is used, run these commands to upload it: +### Upload using built-in bootloader + +To upload using the BlackPill's built-in bootloader: + +1. Install the *dfu-util* command-line utility (typically using a package manager like *HomeBrew* on macOS, *Chocolatey* on Windows, or *Apt* on Linux). +2. Press the *Boot* button while connecting the board via USB to your computer. By pressing the *Boot* button, the device enters bootloader mode. +3. Verify with `dfu-util --list` that the bootloader is available via USB. If not unplug the device and repeat step 2. +4. Run the below command from the project directory. +5. Unplug and reconnect the board from your computer. The LED should now blink about twice a second. ``` -cd stm32-loopback/bin -st-flash write firmware.bin 0x08000000 +dfu-util --device 0483:df11 --alt 0 --dfuse-address 0x08000000 --reset --download bin/blackpill-fxxx.bin ``` + +Make sure you change the filename `blackpill-fxxx.bin` to the name matching your board. + +If you built the firmware yourself, you will find the firmware file in `.pio/build/blackpill-f401cc/firmware.bin` (and similar for other boards). + +### Upload using ST-Link programmer + +In order to upload it using the ST-Link programmer: + +1. Install the *stlink* command-line utility (typically using a package manager like *HomeBrew* on macOS, *Chocolatey* on Windows, or *Apt* on Linux). +2. Unplug the microcontroller board from your computer (in case it is connected). +3. Connect the ST-Link via jumper wires to your board (4 pins on the short side of the board). +4. Connect the ST-Link via USB cable to your computer. +5. Run the below command from the project directory. +6. Unplug the microcontroller board from the ST-Link and connect it to your computer (via USB). + +``` +st-flash write bin/bluepill-f103c8.bin 0x08000000 +``` + +Make sure you change the filename `bluepill-f103c8.bin` to the name matching your board. + +If you built the firmware yourself, you will find the firmware file in `.pio/build/bluepill-f103c8/firmware.bin` (and similar for other boards). + +## Implementation + +This code uses the CMSIS 5 library (mainly for startup code and register definitions) and TinyUSB for USB. For easier use with PlatformIO, a copy of TinyUSB is integrated into the project. The used TinyUSB code in `lib/tinyusb` is an unmodified subset of the library. + +Since the official TinyUSB vendor class is rather limited, an alternative implementation is provided (see [vendor_custom.h](src/vendor_custom.h) and [vendor_custom.c](src/vendor_custom.c)). \ No newline at end of file diff --git a/test-devices/loopback-stm32/bin/blackpill-f401cc.bin b/test-devices/loopback-stm32/bin/blackpill-f401cc.bin new file mode 100755 index 00000000..f4ab388c Binary files /dev/null and b/test-devices/loopback-stm32/bin/blackpill-f401cc.bin differ diff --git a/test-devices/loopback-stm32/bin/blackpill-f411ce.bin b/test-devices/loopback-stm32/bin/blackpill-f411ce.bin new file mode 100755 index 00000000..da3a8766 Binary files /dev/null and b/test-devices/loopback-stm32/bin/blackpill-f411ce.bin differ diff --git a/test-devices/loopback-stm32/bin/bluepill-f103c8.bin b/test-devices/loopback-stm32/bin/bluepill-f103c8.bin new file mode 100755 index 00000000..9f0eaf0b Binary files /dev/null and b/test-devices/loopback-stm32/bin/bluepill-f103c8.bin differ diff --git a/test-devices/loopback-stm32/bin/disco_f723ie.bin b/test-devices/loopback-stm32/bin/disco_f723ie.bin new file mode 100755 index 00000000..b8c2ab31 Binary files /dev/null and b/test-devices/loopback-stm32/bin/disco_f723ie.bin differ diff --git a/test-devices/loopback-stm32/bin/firmware.bin b/test-devices/loopback-stm32/bin/firmware.bin deleted file mode 100755 index b691999d..00000000 Binary files a/test-devices/loopback-stm32/bin/firmware.bin and /dev/null differ diff --git a/test-devices/loopback-stm32/bin/save_bin.sh b/test-devices/loopback-stm32/bin/save_bin.sh deleted file mode 100755 index 0e134cfb..00000000 --- a/test-devices/loopback-stm32/bin/save_bin.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -cp ../.pio/build/loopback-stm32/firmware.bin . diff --git a/test-devices/loopback-stm32/bin/upload_firmware.sh b/test-devices/loopback-stm32/bin/upload_firmware.sh deleted file mode 100755 index 20ad778e..00000000 --- a/test-devices/loopback-stm32/bin/upload_firmware.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -st-flash write firmware.bin 0x08000000 diff --git a/test-devices/loopback-stm32/copy_tinyusb.sh b/test-devices/loopback-stm32/copy_tinyusb.sh new file mode 100755 index 00000000..3021ce18 --- /dev/null +++ b/test-devices/loopback-stm32/copy_tinyusb.sh @@ -0,0 +1,15 @@ +#!/bin/sh +TINYUSB_DIR=../../../tinyusb +rm -rf lib/tinyusb/* +mkdir lib/tinyusb/osal +mkdir lib/tinyusb/portable +mkdir lib/tinyusb/portable/synopsys +mkdir lib/tinyusb/portable/st +cp -R $TINYUSB_DIR/src/common lib/tinyusb +cp -R $TINYUSB_DIR/src/device lib/tinyusb +cp $TINYUSB_DIR/src/osal/osal.h lib/tinyusb/osal +cp $TINYUSB_DIR/src/osal/osal_none.h lib/tinyusb/osal +cp -R $TINYUSB_DIR/src/portable/synopsys/dwc2 lib/tinyusb/portable/synopsys +cp -R $TINYUSB_DIR/src/portable/st/stm32_fsdev lib/tinyusb/portable/st +cp $TINYUSB_DIR/src/*.c lib/tinyusb +cp $TINYUSB_DIR/src/*.h lib/tinyusb diff --git a/test-devices/loopback-stm32/include/circ_buf.h b/test-devices/loopback-stm32/include/circ_buf.h deleted file mode 100644 index 4d9a6970..00000000 --- a/test-devices/loopback-stm32/include/circ_buf.h +++ /dev/null @@ -1,155 +0,0 @@ -// -// Java Does USB -// Loopback device for testing -// -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// -// Circular buffer for raw binary data. -// -// The circular buffer allows a reader and writer to -// use the buffer concurrently. -// - -#pragma once - -#include -#include - -#include - -/** - * Circular buffer for raw binary data. - * - * The circular buffer allows a reader and writer to - * use the buffer concurrently. - * - * @param N number of bytes that fit into the buffer - */ -template -struct circ_buf { - private: - static constexpr int BUF_SIZE = N + 1; - - // 0 <= head < BUF_SIZE - // 0 <= tail < BUF_SIZE - // head == tail: buffer is empty - // Therefore, the buffer must never be filled completely. - volatile int buf_head = 0; // updated when adding data - volatile int buf_tail = 0; // updated when removing data - - uint8_t buffer[BUF_SIZE]; - - public: - /// Creates a new instance - circ_buf(); - - /// Returns the maximum number of bytes that can be added to the buffer - int avail_size(); - - /// Returns the number of bytes in the buffer - int data_size(); - - /** - * Gets the oldest data from the buffer and removes it. - * @param buf buffer to copy data to - * @param max_len maximum number of bytes to copy - * @return the effective number of bytes - */ - int get_data(uint8_t *buf, int max_len); - - /** - * Adds data to the buffer - * - * @param buf the buffer with the data - * @param len the number of bytes to add - */ - void add_data(const uint8_t *buf, int len); - - /// Resets (empties) the circular buffer - void reset(); -}; - -template -circ_buf::circ_buf() : buf_head(0), buf_tail(0) {} - -template -int circ_buf::avail_size() { - int head = buf_head; - int tail = buf_tail; - - if (head >= tail) { - return BUF_SIZE - (head - tail) - 1; - } else { - return tail - head - 1; - } -} - -template -int circ_buf::data_size() { - int head = buf_head; - int tail = buf_tail; - - if (head >= tail) { - return head - tail; - } else { - return BUF_SIZE - (tail - head); - } -} - -template -int circ_buf::get_data(uint8_t *buf, int max_len) { - int tail = buf_tail; - int head = buf_head; - - if (tail == head) - return 0; - - // get available data (without wrap around) - int len = (head > tail ? head : BUF_SIZE) - tail; - - // limit data to max_len - len = std::min(len, max_len); - - // copy data - memcpy(buf, buffer + tail, len); - - // update tail - tail += len; - if (tail >= BUF_SIZE) - tail -= BUF_SIZE; - buf_tail = tail; - - // sufficient data or no more data - if (len == max_len || tail != 0) - return len; - - // copy more data - return get_data(buf + len, max_len - len) + len; -} - -template -void circ_buf::add_data(const uint8_t *buf, int len) { - int head = buf_head; - - // copy first part (from head to end of circular buffer) - int n = std::min(len, BUF_SIZE - head); - memcpy(buffer + head, buf, n); - - // copy second part if needed (to start of circular buffer) - if (n < len) - memcpy(buffer, buf + n, len - n); - - // update head - head += len; - if (head >= BUF_SIZE) - head -= BUF_SIZE; - buf_head = head; -} - -template -void circ_buf::reset() { - buf_head = 0; - buf_tail = 0; -} diff --git a/test-devices/loopback-stm32/include/common.h b/test-devices/loopback-stm32/include/common.h deleted file mode 100644 index 427b2eb4..00000000 --- a/test-devices/loopback-stm32/include/common.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// Java Does USB -// Loopback device for testing -// -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// -// Commmon functions -// - -#pragma once - -#include - -/** - * @brief Initializes systick services - */ -void systick_init(); - -/** - * @brief Gets the time. - * - * @return number of milliseconds since a fixed time in the past - */ -uint32_t millis(); - -/** - * @brief Delays execution (busy wait) - * @param ms delay length, in milliseconds - */ -void delay(uint32_t ms); diff --git a/test-devices/loopback-stm32/include/wcid.h b/test-devices/loopback-stm32/include/wcid.h deleted file mode 100644 index 2c1041ef..00000000 --- a/test-devices/loopback-stm32/include/wcid.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// Java Does USB -// Loopback device for testing -// -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// -// Microsoft WCID descriptors -// - -#pragma once - -#include - -// Register control request handlers for Microsoft WCID descriptors -void register_wcid_desc(usbd_device *usb_dev); diff --git a/test-devices/loopback-stm32/lib/config/tusb_config.h b/test-devices/loopback-stm32/lib/config/tusb_config.h new file mode 100644 index 00000000..1b5a6a01 --- /dev/null +++ b/test-devices/loopback-stm32/lib/config/tusb_config.h @@ -0,0 +1,122 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Board Specific Configuration +//--------------------------------------------------------------------+ + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_TUD_RHPORT +#define BOARD_TUD_RHPORT 0 +#endif + +// RHPort max operational speed can defined by board.mk +#ifndef BOARD_TUD_MAX_SPEED +#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU +#error CFG_TUSB_MCU must be defined +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +#ifndef CFG_TUSB_DEBUG +#define CFG_TUSB_DEBUG 0 +#endif + +// Enable Device stack +#define CFG_TUD_ENABLED 1 + +// Default is max speed that hardware controller could support with on-chip PHY +#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE +#define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +//------------- CLASS -------------// +#ifndef CFG_TUD_CDC +#define CFG_TUD_CDC 0 +#endif +#ifndef CFG_TUD_MSC +#define CFG_TUD_MSC 0 +#endif +#ifndef CFG_TUD_HID +#define CFG_TUD_HID 0 +#endif +#ifndef CFG_TUD_MIDI +#define CFG_TUD_MIDI 0 +#endif +#ifndef CFG_TUD_VENDOR +#define CFG_TUD_VENDOR 0 +#endif + +// HID buffer size Should be sufficient to hold ID (if any) + Data +#define CFG_TUD_HID_EP_BUFSIZE 64 + +// Vendor FIFO size of TX and RX +// If not configured vendor endpoints will not be buffered +#define CFG_TUD_VENDOR_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_VENDOR_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/test-devices/loopback-stm32/lib/tinyusb/common/tusb_common.h b/test-devices/loopback-stm32/lib/tinyusb/common/tusb_common.h new file mode 100644 index 00000000..0d4082c0 --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/common/tusb_common.h @@ -0,0 +1,316 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_COMMON_H_ +#define _TUSB_COMMON_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Macros Helper +//--------------------------------------------------------------------+ +#define TU_ARRAY_SIZE(_arr) ( sizeof(_arr) / sizeof(_arr[0]) ) +#define TU_MIN(_x, _y) ( ( (_x) < (_y) ) ? (_x) : (_y) ) +#define TU_MAX(_x, _y) ( ( (_x) > (_y) ) ? (_x) : (_y) ) +#define TU_DIV_CEIL(n, d) (((n) + (d) - 1) / (d)) + +#define TU_U16(_high, _low) ((uint16_t) (((_high) << 8) | (_low))) +#define TU_U16_HIGH(_u16) ((uint8_t) (((_u16) >> 8) & 0x00ff)) +#define TU_U16_LOW(_u16) ((uint8_t) ((_u16) & 0x00ff)) +#define U16_TO_U8S_BE(_u16) TU_U16_HIGH(_u16), TU_U16_LOW(_u16) +#define U16_TO_U8S_LE(_u16) TU_U16_LOW(_u16), TU_U16_HIGH(_u16) + +#define TU_U32_BYTE3(_u32) ((uint8_t) ((((uint32_t) _u32) >> 24) & 0x000000ff)) // MSB +#define TU_U32_BYTE2(_u32) ((uint8_t) ((((uint32_t) _u32) >> 16) & 0x000000ff)) +#define TU_U32_BYTE1(_u32) ((uint8_t) ((((uint32_t) _u32) >> 8) & 0x000000ff)) +#define TU_U32_BYTE0(_u32) ((uint8_t) (((uint32_t) _u32) & 0x000000ff)) // LSB + +#define U32_TO_U8S_BE(_u32) TU_U32_BYTE3(_u32), TU_U32_BYTE2(_u32), TU_U32_BYTE1(_u32), TU_U32_BYTE0(_u32) +#define U32_TO_U8S_LE(_u32) TU_U32_BYTE0(_u32), TU_U32_BYTE1(_u32), TU_U32_BYTE2(_u32), TU_U32_BYTE3(_u32) + +#define TU_BIT(n) (1UL << (n)) + +// Generate a mask with bit from high (31) to low (0) set, e.g TU_GENMASK(3, 0) = 0b1111 +#define TU_GENMASK(h, l) ( (UINT32_MAX << (l)) & (UINT32_MAX >> (31 - (h))) ) + +//--------------------------------------------------------------------+ +// Includes +//--------------------------------------------------------------------+ + +// Standard Headers +#include +#include +#include +#include +#include +#include + +// Tinyusb Common Headers +#include "tusb_option.h" +#include "tusb_compiler.h" +#include "tusb_verify.h" +#include "tusb_types.h" +#include "tusb_debug.h" + +//--------------------------------------------------------------------+ +// Optional API implemented by application if needed +// TODO move to a more ovious place/file +//--------------------------------------------------------------------+ + +// flush data cache +TU_ATTR_WEAK extern void tusb_app_dcache_flush(uintptr_t addr, uint32_t data_size); + +// invalidate data cache +TU_ATTR_WEAK extern void tusb_app_dcache_invalidate(uintptr_t addr, uint32_t data_size); + +// Optional physical <-> virtual address translation +TU_ATTR_WEAK extern void* tusb_app_virt_to_phys(void *virt_addr); +TU_ATTR_WEAK extern void* tusb_app_phys_to_virt(void *phys_addr); + +//--------------------------------------------------------------------+ +// Internal Inline Functions +//--------------------------------------------------------------------+ + +//------------- Mem -------------// +#define tu_memclr(buffer, size) memset((buffer), 0, (size)) +#define tu_varclr(_var) tu_memclr(_var, sizeof(*(_var))) + +// This is a backport of memset_s from c11 +TU_ATTR_ALWAYS_INLINE static inline int tu_memset_s(void *dest, size_t destsz, int ch, size_t count) { + // TODO may check if desst and src is not NULL + if ( count > destsz ) { + return -1; + } + memset(dest, ch, count); + return 0; +} + +// This is a backport of memcpy_s from c11 +TU_ATTR_ALWAYS_INLINE static inline int tu_memcpy_s(void *dest, size_t destsz, const void *src, size_t count) { + // TODO may check if desst and src is not NULL + if ( count > destsz ) { + return -1; + } + memcpy(dest, src, count); + return 0; +} + + +//------------- Bytes -------------// +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_u32(uint8_t b3, uint8_t b2, uint8_t b1, uint8_t b0) { + return ( ((uint32_t) b3) << 24) | ( ((uint32_t) b2) << 16) | ( ((uint32_t) b1) << 8) | b0; +} + +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_u16(uint8_t high, uint8_t low) { + return (uint16_t) ((((uint16_t) high) << 8) | low); +} + +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte3(uint32_t ui32) { return TU_U32_BYTE3(ui32); } +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte2(uint32_t ui32) { return TU_U32_BYTE2(ui32); } +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte1(uint32_t ui32) { return TU_U32_BYTE1(ui32); } +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte0(uint32_t ui32) { return TU_U32_BYTE0(ui32); } + +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_u32_high16(uint32_t ui32) { return (uint16_t) (ui32 >> 16); } +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_u32_low16 (uint32_t ui32) { return (uint16_t) (ui32 & 0x0000ffffu); } + +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u16_high(uint16_t ui16) { return TU_U16_HIGH(ui16); } +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u16_low (uint16_t ui16) { return TU_U16_LOW(ui16); } + +//------------- Bits -------------// +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_bit_set (uint32_t value, uint8_t pos) { return value | TU_BIT(pos); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_bit_clear(uint32_t value, uint8_t pos) { return value & (~TU_BIT(pos)); } +TU_ATTR_ALWAYS_INLINE static inline bool tu_bit_test (uint32_t value, uint8_t pos) { return (value & TU_BIT(pos)) ? true : false; } + +//------------- Min -------------// +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_min8 (uint8_t x, uint8_t y ) { return (x < y) ? x : y; } +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_min16 (uint16_t x, uint16_t y) { return (x < y) ? x : y; } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_min32 (uint32_t x, uint32_t y) { return (x < y) ? x : y; } + +//------------- Max -------------// +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_max8 (uint8_t x, uint8_t y ) { return (x > y) ? x : y; } +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_max16 (uint16_t x, uint16_t y) { return (x > y) ? x : y; } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_max32 (uint32_t x, uint32_t y) { return (x > y) ? x : y; } + +//------------- Align -------------// +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align(uint32_t value, uint32_t alignment) { + return value & ((uint32_t) ~(alignment-1)); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align4 (uint32_t value) { return (value & 0xFFFFFFFCUL); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align8 (uint32_t value) { return (value & 0xFFFFFFF8UL); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align16 (uint32_t value) { return (value & 0xFFFFFFF0UL); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align32 (uint32_t value) { return (value & 0xFFFFFFE0UL); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align4k (uint32_t value) { return (value & 0xFFFFF000UL); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_offset4k(uint32_t value) { return (value & 0xFFFUL); } + +TU_ATTR_ALWAYS_INLINE static inline bool tu_is_aligned32(uint32_t value) { return (value & 0x1FUL) == 0; } +TU_ATTR_ALWAYS_INLINE static inline bool tu_is_aligned64(uint64_t value) { return (value & 0x3FUL) == 0; } + +//------------- Mathematics -------------// +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_div_ceil(uint32_t v, uint32_t d) { return (v + d -1)/d; } + +// log2 of a value is its MSB's position +// TODO use clz TODO remove +static inline uint8_t tu_log2(uint32_t value) +{ + uint8_t result = 0; + while (value >>= 1) { result++; } + return result; +} + +//static inline uint8_t tu_log2(uint32_t value) +//{ +// return sizeof(uint32_t) * CHAR_BIT - __builtin_clz(x) - 1; +//} + +static inline bool tu_is_power_of_two(uint32_t value) +{ + return (value != 0) && ((value & (value - 1)) == 0); +} + +//------------- Unaligned Access -------------// +#if TUP_ARCH_STRICT_ALIGN + +// Rely on compiler to generate correct code for unaligned access +typedef struct { uint16_t val; } TU_ATTR_PACKED tu_unaligned_uint16_t; +typedef struct { uint32_t val; } TU_ATTR_PACKED tu_unaligned_uint32_t; + +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_unaligned_read32(const void* mem) +{ + tu_unaligned_uint32_t const* ua32 = (tu_unaligned_uint32_t const*) mem; + return ua32->val; +} + +TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write32(void* mem, uint32_t value) +{ + tu_unaligned_uint32_t* ua32 = (tu_unaligned_uint32_t*) mem; + ua32->val = value; +} + +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_unaligned_read16(const void* mem) +{ + tu_unaligned_uint16_t const* ua16 = (tu_unaligned_uint16_t const*) mem; + return ua16->val; +} + +TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16(void* mem, uint16_t value) +{ + tu_unaligned_uint16_t* ua16 = (tu_unaligned_uint16_t*) mem; + ua16->val = value; +} + +#elif TUP_MCU_STRICT_ALIGN + +// MCU such as LPC_IP3511 Highspeed cannot access unaligned memory on USB_RAM although it is ARM M4. +// We have to manually pick up bytes since tu_unaligned_uint32_t will still generate unaligned code +// NOTE: volatile cast to memory to prevent compiler to optimize and generate unaligned code +// TODO Big Endian may need minor changes +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_unaligned_read32(const void* mem) +{ + volatile uint8_t const* buf8 = (uint8_t const*) mem; + return tu_u32(buf8[3], buf8[2], buf8[1], buf8[0]); +} + +TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write32(void* mem, uint32_t value) +{ + volatile uint8_t* buf8 = (uint8_t*) mem; + buf8[0] = tu_u32_byte0(value); + buf8[1] = tu_u32_byte1(value); + buf8[2] = tu_u32_byte2(value); + buf8[3] = tu_u32_byte3(value); +} + +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_unaligned_read16(const void* mem) +{ + volatile uint8_t const* buf8 = (uint8_t const*) mem; + return tu_u16(buf8[1], buf8[0]); +} + +TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16(void* mem, uint16_t value) +{ + volatile uint8_t* buf8 = (uint8_t*) mem; + buf8[0] = tu_u16_low(value); + buf8[1] = tu_u16_high(value); +} + + +#else + +// MCU that could access unaligned memory natively +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_unaligned_read32(const void *mem) { + return *((uint32_t const *) mem); +} + +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_unaligned_read16(const void *mem) { + return *((uint16_t const *) mem); +} + +TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write32(void *mem, uint32_t value) { + *((uint32_t *) mem) = value; +} + +TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16(void *mem, uint16_t value) { + *((uint16_t *) mem) = value; +} + +#endif + +// To be removed +//------------- Binary constant -------------// +#if defined(__GNUC__) && !defined(__CC_ARM) + +#define TU_BIN8(x) ((uint8_t) (0b##x)) +#define TU_BIN16(b1, b2) ((uint16_t) (0b##b1##b2)) +#define TU_BIN32(b1, b2, b3, b4) ((uint32_t) (0b##b1##b2##b3##b4)) + +#else + +// internal macro of B8, B16, B32 +#define _B8__(x) (((x&0x0000000FUL)?1:0) \ + +((x&0x000000F0UL)?2:0) \ + +((x&0x00000F00UL)?4:0) \ + +((x&0x0000F000UL)?8:0) \ + +((x&0x000F0000UL)?16:0) \ + +((x&0x00F00000UL)?32:0) \ + +((x&0x0F000000UL)?64:0) \ + +((x&0xF0000000UL)?128:0)) + +#define TU_BIN8(d) ((uint8_t) _B8__(0x##d##UL)) +#define TU_BIN16(dmsb,dlsb) (((uint16_t)TU_BIN8(dmsb)<<8) + TU_BIN8(dlsb)) +#define TU_BIN32(dmsb,db2,db3,dlsb) \ + (((uint32_t)TU_BIN8(dmsb)<<24) \ + + ((uint32_t)TU_BIN8(db2)<<16) \ + + ((uint32_t)TU_BIN8(db3)<<8) \ + + TU_BIN8(dlsb)) +#endif + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_COMMON_H_ */ diff --git a/test-devices/loopback-stm32/lib/tinyusb/common/tusb_compiler.h b/test-devices/loopback-stm32/lib/tinyusb/common/tusb_compiler.h new file mode 100644 index 00000000..0d5570b1 --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/common/tusb_compiler.h @@ -0,0 +1,298 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +/** \ingroup Group_Common + * \defgroup Group_Compiler Compiler + * \brief Group_Compiler brief + * @{ */ + +#ifndef _TUSB_COMPILER_H_ +#define _TUSB_COMPILER_H_ + +#define TU_TOKEN(x) x +#define TU_STRING(x) #x ///< stringify without expand +#define TU_XSTRING(x) TU_STRING(x) ///< expand then stringify + +#define TU_STRCAT(a, b) a##b ///< concat without expand +#define TU_STRCAT3(a, b, c) a##b##c ///< concat without expand + +#define TU_XSTRCAT(a, b) TU_STRCAT(a, b) ///< expand then concat +#define TU_XSTRCAT3(a, b, c) TU_STRCAT3(a, b, c) ///< expand then concat 3 tokens + +#define TU_INCLUDE_PATH(_dir,_file) TU_XSTRING( TU_TOKEN(_dir)TU_TOKEN(_file) ) + +#if defined __COUNTER__ && __COUNTER__ != __COUNTER__ + #define _TU_COUNTER_ __COUNTER__ +#else + #define _TU_COUNTER_ __LINE__ +#endif + +// Compile-time Assert +#if defined (__cplusplus) && __cplusplus >= 201103L + #define TU_VERIFY_STATIC static_assert +#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define TU_VERIFY_STATIC _Static_assert +#elif defined(__CCRX__) + #define TU_VERIFY_STATIC(const_expr, _mess) typedef char TU_XSTRCAT(_verify_static_, _TU_COUNTER_)[(const_expr) ? 1 : 0]; +#else + #define TU_VERIFY_STATIC(const_expr, _mess) enum { TU_XSTRCAT(_verify_static_, _TU_COUNTER_) = 1/(!!(const_expr)) } +#endif + +/* --------------------- Fuzzing types -------------------------------------- */ +#ifdef _FUZZ + #define tu_static static __thread +#else + #define tu_static static +#endif + +// for declaration of reserved field, make use of _TU_COUNTER_ +#define TU_RESERVED TU_XSTRCAT(reserved, _TU_COUNTER_) + +#define TU_LITTLE_ENDIAN (0x12u) +#define TU_BIG_ENDIAN (0x21u) + +/*------------------------------------------------------------------*/ +/* Count number of arguments of __VA_ARGS__ + * - reference https://stackoverflow.com/questions/2124339/c-preprocessor-va-args-number-of-arguments + * - _GET_NTH_ARG() takes args >= N (64) but only expand to Nth one (64th) + * - _RSEQ_N() is reverse sequential to N to add padding to have + * Nth position is the same as the number of arguments + * - ##__VA_ARGS__ is used to deal with 0 paramerter (swallows comma) + *------------------------------------------------------------------*/ +#if !defined(__CCRX__) +#define TU_ARGS_NUM(...) _TU_NARG(_0, ##__VA_ARGS__, _RSEQ_N()) +#else +#define TU_ARGS_NUM(...) _TU_NARG(_0, __VA_ARGS__, _RSEQ_N()) +#endif + +#define _TU_NARG(...) _GET_NTH_ARG(__VA_ARGS__) +#define _GET_NTH_ARG( \ + _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,_34,_35,_36,_37,_38,_39,_40, \ + _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ + _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ + _61,_62,_63,N,...) N +#define _RSEQ_N() \ + 62,61,60, \ + 59,58,57,56,55,54,53,52,51,50, \ + 49,48,47,46,45,44,43,42,41,40, \ + 39,38,37,36,35,34,33,32,31,30, \ + 29,28,27,26,25,24,23,22,21,20, \ + 19,18,17,16,15,14,13,12,11,10, \ + 9,8,7,6,5,4,3,2,1,0 + +// Apply an macro X to each of the arguments with an separated of choice +#define TU_ARGS_APPLY(_X, _s, ...) TU_XSTRCAT(_TU_ARGS_APPLY_, TU_ARGS_NUM(__VA_ARGS__))(_X, _s, __VA_ARGS__) + +#define _TU_ARGS_APPLY_1(_X, _s, _a1) _X(_a1) +#define _TU_ARGS_APPLY_2(_X, _s, _a1, _a2) _X(_a1) _s _X(_a2) +#define _TU_ARGS_APPLY_3(_X, _s, _a1, _a2, _a3) _X(_a1) _s _TU_ARGS_APPLY_2(_X, _s, _a2, _a3) +#define _TU_ARGS_APPLY_4(_X, _s, _a1, _a2, _a3, _a4) _X(_a1) _s _TU_ARGS_APPLY_3(_X, _s, _a2, _a3, _a4) +#define _TU_ARGS_APPLY_5(_X, _s, _a1, _a2, _a3, _a4, _a5) _X(_a1) _s _TU_ARGS_APPLY_4(_X, _s, _a2, _a3, _a4, _a5) +#define _TU_ARGS_APPLY_6(_X, _s, _a1, _a2, _a3, _a4, _a5, _a6) _X(_a1) _s _TU_ARGS_APPLY_5(_X, _s, _a2, _a3, _a4, _a5, _a6) +#define _TU_ARGS_APPLY_7(_X, _s, _a1, _a2, _a3, _a4, _a5, _a6, _a7) _X(_a1) _s _TU_ARGS_APPLY_6(_X, _s, _a2, _a3, _a4, _a5, _a6, _a7) +#define _TU_ARGS_APPLY_8(_X, _s, _a1, _a2, _a3, _a4, _a5, _a6, _a7, _a8) _X(_a1) _s _TU_ARGS_APPLY_7(_X, _s, _a2, _a3, _a4, _a5, _a6, _a7, _a8) + +//--------------------------------------------------------------------+ +// Compiler porting with Attribute and Endian +//--------------------------------------------------------------------+ + +// TODO refactor since __attribute__ is supported across many compiler +#if defined(__GNUC__) + #define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes))) + #define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name))) + #define TU_ATTR_PACKED __attribute__ ((packed)) + #define TU_ATTR_WEAK __attribute__ ((weak)) + #ifndef TU_ATTR_ALWAYS_INLINE // allow to override for debug + #define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline)) + #endif + #define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used + #define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused + #define TU_ATTR_USED __attribute__ ((used)) // Function/Variable is meant to be used + + #define TU_ATTR_PACKED_BEGIN + #define TU_ATTR_PACKED_END + #define TU_ATTR_BIT_FIELD_ORDER_BEGIN + #define TU_ATTR_BIT_FIELD_ORDER_END + + #if __GNUC__ < 5 + #define TU_ATTR_FALLTHROUGH do {} while (0) /* fallthrough */ + #else + #if __has_attribute(__fallthrough__) + #define TU_ATTR_FALLTHROUGH __attribute__((fallthrough)) + #else + #define TU_ATTR_FALLTHROUGH do {} while (0) /* fallthrough */ + #endif + #endif + + // Endian conversion use well-known host to network (big endian) naming + #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + #define TU_BYTE_ORDER TU_LITTLE_ENDIAN + #else + #define TU_BYTE_ORDER TU_BIG_ENDIAN + #endif + + // Unfortunately XC16 doesn't provide builtins for 32bit endian conversion + #if defined(__XC16) + #define TU_BSWAP16(u16) (__builtin_swap(u16)) + #define TU_BSWAP32(u32) ((((u32) & 0xff000000) >> 24) | \ + (((u32) & 0x00ff0000) >> 8) | \ + (((u32) & 0x0000ff00) << 8) | \ + (((u32) & 0x000000ff) << 24)) + #else + #define TU_BSWAP16(u16) (__builtin_bswap16(u16)) + #define TU_BSWAP32(u32) (__builtin_bswap32(u32)) + #endif + + #ifndef __ARMCC_VERSION + // List of obsolete callback function that is renamed and should not be defined. + // Put it here since only gcc support this pragma + #pragma GCC poison tud_vendor_control_request_cb + #endif + +#elif defined(__TI_COMPILER_VERSION__) + #define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes))) + #define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name))) + #define TU_ATTR_PACKED __attribute__ ((packed)) + #define TU_ATTR_WEAK __attribute__ ((weak)) + #define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline)) + #define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used + #define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused + #define TU_ATTR_USED __attribute__ ((used)) + #define TU_ATTR_FALLTHROUGH __attribute__((fallthrough)) + + #define TU_ATTR_PACKED_BEGIN + #define TU_ATTR_PACKED_END + #define TU_ATTR_BIT_FIELD_ORDER_BEGIN + #define TU_ATTR_BIT_FIELD_ORDER_END + + // __BYTE_ORDER is defined in the TI ARM compiler, but not MSP430 (which is little endian) + #if ((__BYTE_ORDER__) == (__ORDER_LITTLE_ENDIAN__)) || defined(__MSP430__) + #define TU_BYTE_ORDER TU_LITTLE_ENDIAN + #else + #define TU_BYTE_ORDER TU_BIG_ENDIAN + #endif + + #define TU_BSWAP16(u16) (__builtin_bswap16(u16)) + #define TU_BSWAP32(u32) (__builtin_bswap32(u32)) + +#elif defined(__ICCARM__) + #include + #define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes))) + #define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name))) + #define TU_ATTR_PACKED __attribute__ ((packed)) + #define TU_ATTR_WEAK __attribute__ ((weak)) + #ifndef TU_ATTR_ALWAYS_INLINE // allow to override for debug + #define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline)) + #endif + #define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used + #define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused + #define TU_ATTR_USED __attribute__ ((used)) // Function/Variable is meant to be used + #define TU_ATTR_FALLTHROUGH do {} while (0) /* fallthrough */ + + #define TU_ATTR_PACKED_BEGIN + #define TU_ATTR_PACKED_END + #define TU_ATTR_BIT_FIELD_ORDER_BEGIN + #define TU_ATTR_BIT_FIELD_ORDER_END + + // Endian conversion use well-known host to network (big endian) naming + #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + #define TU_BYTE_ORDER TU_LITTLE_ENDIAN + #else + #define TU_BYTE_ORDER TU_BIG_ENDIAN + #endif + + #define TU_BSWAP16(u16) (__iar_builtin_REV16(u16)) + #define TU_BSWAP32(u32) (__iar_builtin_REV(u32)) + +#elif defined(__CCRX__) + #define TU_ATTR_ALIGNED(Bytes) + #define TU_ATTR_SECTION(sec_name) + #define TU_ATTR_PACKED + #define TU_ATTR_WEAK + #define TU_ATTR_ALWAYS_INLINE + #define TU_ATTR_DEPRECATED(mess) + #define TU_ATTR_UNUSED + #define TU_ATTR_USED + #define TU_ATTR_FALLTHROUGH do {} while (0) /* fallthrough */ + + #define TU_ATTR_PACKED_BEGIN _Pragma("pack") + #define TU_ATTR_PACKED_END _Pragma("packoption") + #define TU_ATTR_BIT_FIELD_ORDER_BEGIN _Pragma("bit_order right") + #define TU_ATTR_BIT_FIELD_ORDER_END _Pragma("bit_order") + + // Endian conversion use well-known host to network (big endian) naming + #if defined(__LIT) + #define TU_BYTE_ORDER TU_LITTLE_ENDIAN + #else + #define TU_BYTE_ORDER TU_BIG_ENDIAN + #endif + + #define TU_BSWAP16(u16) ((unsigned short)_builtin_revw((unsigned long)u16)) + #define TU_BSWAP32(u32) (_builtin_revl(u32)) + +#else + #error "Compiler attribute porting is required" +#endif + + +#if (TU_BYTE_ORDER == TU_LITTLE_ENDIAN) + + #define tu_htons(u16) (TU_BSWAP16(u16)) + #define tu_ntohs(u16) (TU_BSWAP16(u16)) + + #define tu_htonl(u32) (TU_BSWAP32(u32)) + #define tu_ntohl(u32) (TU_BSWAP32(u32)) + + #define tu_htole16(u16) (u16) + #define tu_le16toh(u16) (u16) + + #define tu_htole32(u32) (u32) + #define tu_le32toh(u32) (u32) + +#elif (TU_BYTE_ORDER == TU_BIG_ENDIAN) + + #define tu_htons(u16) (u16) + #define tu_ntohs(u16) (u16) + + #define tu_htonl(u32) (u32) + #define tu_ntohl(u32) (u32) + + #define tu_htole16(u16) (TU_BSWAP16(u16)) + #define tu_le16toh(u16) (TU_BSWAP16(u16)) + + #define tu_htole32(u32) (TU_BSWAP32(u32)) + #define tu_le32toh(u32) (TU_BSWAP32(u32)) + +#else + #error Byte order is undefined +#endif + +#endif /* _TUSB_COMPILER_H_ */ + +/// @} diff --git a/test-devices/loopback-stm32/lib/tinyusb/common/tusb_debug.h b/test-devices/loopback-stm32/lib/tinyusb/common/tusb_debug.h new file mode 100644 index 00000000..2e9f1d9c --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/common/tusb_debug.h @@ -0,0 +1,171 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022, Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_DEBUG_H_ +#define _TUSB_DEBUG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Debug +//--------------------------------------------------------------------+ + +// CFG_TUSB_DEBUG for debugging +// 0 : no debug +// 1 : print error +// 2 : print warning +// 3 : print info +#if CFG_TUSB_DEBUG + +// Enum to String for debugging purposes +#if CFG_TUSB_DEBUG >= CFG_TUH_LOG_LEVEL || CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL +extern char const* const tu_str_speed[]; +extern char const* const tu_str_std_request[]; +extern char const* const tu_str_xfer_result[]; +#endif + +void tu_print_mem(void const *buf, uint32_t count, uint8_t indent); + +#ifdef CFG_TUSB_DEBUG_PRINTF + extern int CFG_TUSB_DEBUG_PRINTF(const char *format, ...); + #define tu_printf CFG_TUSB_DEBUG_PRINTF +#else + #define tu_printf printf +#endif + +static inline void tu_print_buf(uint8_t const* buf, uint32_t bufsize) { + for(uint32_t i=0; i= 2 + #define TU_LOG2 TU_LOG1 + #define TU_LOG2_MEM TU_LOG1_MEM + #define TU_LOG2_BUF TU_LOG1_BUF + #define TU_LOG2_INT TU_LOG1_INT + #define TU_LOG2_HEX TU_LOG1_HEX +#endif + +// Log Level 3: Info +#if CFG_TUSB_DEBUG >= 3 + #define TU_LOG3 TU_LOG1 + #define TU_LOG3_MEM TU_LOG1_MEM + #define TU_LOG3_BUF TU_LOG1_BUF + #define TU_LOG3_INT TU_LOG1_INT + #define TU_LOG3_HEX TU_LOG1_HEX +#endif + +typedef struct { + uint32_t key; + const char* data; +} tu_lookup_entry_t; + +typedef struct { + uint16_t count; + tu_lookup_entry_t const* items; +} tu_lookup_table_t; + +static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint32_t key) { + tu_static char not_found[11]; + + for(uint16_t i=0; icount; i++) { + if (p_table->items[i].key == key) return p_table->items[i].data; + } + + // not found return the key value in hex + snprintf(not_found, sizeof(not_found), "0x%08lX", (unsigned long) key); + + return not_found; +} + +#endif // CFG_TUSB_DEBUG + +#ifndef TU_LOG + #define TU_LOG(n, ...) + #define TU_LOG_MEM(n, ...) + #define TU_LOG_BUF(n, ...) + #define TU_LOG_INT(n, ...) + #define TU_LOG_HEX(n, ...) + #define TU_LOG_LOCATION() + #define TU_LOG_FAILED() +#endif + +// TODO replace all TU_LOGn with TU_LOG(n) + +#define TU_LOG0(...) +#define TU_LOG0_MEM(...) +#define TU_LOG0_BUF(...) +#define TU_LOG0_INT(...) +#define TU_LOG0_HEX(...) + +#ifndef TU_LOG1 + #define TU_LOG1(...) + #define TU_LOG1_MEM(...) + #define TU_LOG1_BUF(...) + #define TU_LOG1_INT(...) + #define TU_LOG1_HEX(...) +#endif + +#ifndef TU_LOG2 + #define TU_LOG2(...) + #define TU_LOG2_MEM(...) + #define TU_LOG2_BUF(...) + #define TU_LOG2_INT(...) + #define TU_LOG2_HEX(...) +#endif + +#ifndef TU_LOG3 + #define TU_LOG3(...) + #define TU_LOG3_MEM(...) + #define TU_LOG3_BUF(...) + #define TU_LOG3_INT(...) + #define TU_LOG3_HEX(...) +#endif + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_DEBUG_H_ */ diff --git a/test-devices/loopback-stm32/lib/tinyusb/common/tusb_fifo.c b/test-devices/loopback-stm32/lib/tinyusb/common/tusb_fifo.c new file mode 100644 index 00000000..76696396 --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/common/tusb_fifo.c @@ -0,0 +1,1066 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * Copyright (c) 2020 Reinhard Panhuber - rework to unmasked pointers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "osal/osal.h" +#include "tusb_fifo.h" + +#define TU_FIFO_DBG 0 + +// Suppress IAR warning +// Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this statement +#if defined(__ICCARM__) +#pragma diag_suppress = Pa082 +#endif + +#if OSAL_MUTEX_REQUIRED + +TU_ATTR_ALWAYS_INLINE static inline void _ff_lock(osal_mutex_t mutex) +{ + if (mutex) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER); +} + +TU_ATTR_ALWAYS_INLINE static inline void _ff_unlock(osal_mutex_t mutex) +{ + if (mutex) osal_mutex_unlock(mutex); +} + +#else + +#define _ff_lock(_mutex) +#define _ff_unlock(_mutex) + +#endif + +/** \enum tu_fifo_copy_mode_t + * \brief Write modes intended to allow special read and write functions to be able to + * copy data to and from USB hardware FIFOs as needed for e.g. STM32s and others + */ +typedef enum +{ + TU_FIFO_COPY_INC, ///< Copy from/to an increasing source/destination address - default mode + TU_FIFO_COPY_CST_FULL_WORDS, ///< Copy from/to a constant source/destination address - required for e.g. STM32 to write into USB hardware FIFO +} tu_fifo_copy_mode_t; + +bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable) +{ + // Limit index space to 2*depth - this allows for a fast "modulo" calculation + // but limits the maximum depth to 2^16/2 = 2^15 and buffer overflows are detectable + // only if overflow happens once (important for unsupervised DMA applications) + if (depth > 0x8000) return false; + + _ff_lock(f->mutex_wr); + _ff_lock(f->mutex_rd); + + f->buffer = (uint8_t*) buffer; + f->depth = depth; + f->item_size = (uint16_t) (item_size & 0x7FFF); + f->overwritable = overwritable; + f->rd_idx = 0; + f->wr_idx = 0; + + _ff_unlock(f->mutex_wr); + _ff_unlock(f->mutex_rd); + + return true; +} + +//--------------------------------------------------------------------+ +// Pull & Push +//--------------------------------------------------------------------+ + +// Intended to be used to read from hardware USB FIFO in e.g. STM32 where all data is read from a constant address +// Code adapted from dcd_synopsys.c +// TODO generalize with configurable 1 byte or 4 byte each read +static void _ff_push_const_addr(uint8_t * ff_buf, const void * app_buf, uint16_t len) +{ + volatile const uint32_t * reg_rx = (volatile const uint32_t *) app_buf; + + // Reading full available 32 bit words from const app address + uint16_t full_words = len >> 2; + while(full_words--) + { + tu_unaligned_write32(ff_buf, *reg_rx); + ff_buf += 4; + } + + // Read the remaining 1-3 bytes from const app address + uint8_t const bytes_rem = len & 0x03; + if ( bytes_rem ) + { + uint32_t tmp32 = *reg_rx; + memcpy(ff_buf, &tmp32, bytes_rem); + } +} + +// Intended to be used to write to hardware USB FIFO in e.g. STM32 +// where all data is written to a constant address in full word copies +static void _ff_pull_const_addr(void * app_buf, const uint8_t * ff_buf, uint16_t len) +{ + volatile uint32_t * reg_tx = (volatile uint32_t *) app_buf; + + // Write full available 32 bit words to const address + uint16_t full_words = len >> 2; + while(full_words--) + { + *reg_tx = tu_unaligned_read32(ff_buf); + ff_buf += 4; + } + + // Write the remaining 1-3 bytes into const address + uint8_t const bytes_rem = len & 0x03; + if ( bytes_rem ) + { + uint32_t tmp32 = 0; + memcpy(&tmp32, ff_buf, bytes_rem); + + *reg_tx = tmp32; + } +} + +// send one item to fifo WITHOUT updating write pointer +static inline void _ff_push(tu_fifo_t* f, void const * app_buf, uint16_t rel) +{ + memcpy(f->buffer + (rel * f->item_size), app_buf, f->item_size); +} + +// send n items to fifo WITHOUT updating write pointer +static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t wr_ptr, tu_fifo_copy_mode_t copy_mode) +{ + uint16_t const lin_count = f->depth - wr_ptr; + uint16_t const wrap_count = n - lin_count; + + uint16_t lin_bytes = lin_count * f->item_size; + uint16_t wrap_bytes = wrap_count * f->item_size; + + // current buffer of fifo + uint8_t* ff_buf = f->buffer + (wr_ptr * f->item_size); + + switch (copy_mode) + { + case TU_FIFO_COPY_INC: + if(n <= lin_count) + { + // Linear only + memcpy(ff_buf, app_buf, n*f->item_size); + } + else + { + // Wrap around + + // Write data to linear part of buffer + memcpy(ff_buf, app_buf, lin_bytes); + + // Write data wrapped around + // TU_ASSERT(nWrap_bytes <= f->depth, ); + memcpy(f->buffer, ((uint8_t const*) app_buf) + lin_bytes, wrap_bytes); + } + break; + + case TU_FIFO_COPY_CST_FULL_WORDS: + // Intended for hardware buffers from which it can be read word by word only + if(n <= lin_count) + { + // Linear only + _ff_push_const_addr(ff_buf, app_buf, n*f->item_size); + } + else + { + // Wrap around case + + // Write full words to linear part of buffer + uint16_t nLin_4n_bytes = lin_bytes & 0xFFFC; + _ff_push_const_addr(ff_buf, app_buf, nLin_4n_bytes); + ff_buf += nLin_4n_bytes; + + // There could be odd 1-3 bytes before the wrap-around boundary + uint8_t rem = lin_bytes & 0x03; + if (rem > 0) + { + volatile const uint32_t * rx_fifo = (volatile const uint32_t *) app_buf; + + uint8_t remrem = (uint8_t) tu_min16(wrap_bytes, 4-rem); + wrap_bytes -= remrem; + + uint32_t tmp32 = *rx_fifo; + uint8_t * src_u8 = ((uint8_t *) &tmp32); + + // Write 1-3 bytes before wrapped boundary + while(rem--) *ff_buf++ = *src_u8++; + + // Read more bytes to beginning to complete a word + ff_buf = f->buffer; + while(remrem--) *ff_buf++ = *src_u8++; + } + else + { + ff_buf = f->buffer; // wrap around to beginning + } + + // Write data wrapped part + if (wrap_bytes > 0) _ff_push_const_addr(ff_buf, app_buf, wrap_bytes); + } + break; + default: break; + } +} + +// get one item from fifo WITHOUT updating read pointer +static inline void _ff_pull(tu_fifo_t* f, void * app_buf, uint16_t rel) +{ + memcpy(app_buf, f->buffer + (rel * f->item_size), f->item_size); +} + +// get n items from fifo WITHOUT updating read pointer +static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rd_ptr, tu_fifo_copy_mode_t copy_mode) +{ + uint16_t const lin_count = f->depth - rd_ptr; + uint16_t const wrap_count = n - lin_count; // only used if wrapped + + uint16_t lin_bytes = lin_count * f->item_size; + uint16_t wrap_bytes = wrap_count * f->item_size; + + // current buffer of fifo + uint8_t* ff_buf = f->buffer + (rd_ptr * f->item_size); + + switch (copy_mode) + { + case TU_FIFO_COPY_INC: + if ( n <= lin_count ) + { + // Linear only + memcpy(app_buf, ff_buf, n*f->item_size); + } + else + { + // Wrap around + + // Read data from linear part of buffer + memcpy(app_buf, ff_buf, lin_bytes); + + // Read data wrapped part + memcpy((uint8_t*) app_buf + lin_bytes, f->buffer, wrap_bytes); + } + break; + + case TU_FIFO_COPY_CST_FULL_WORDS: + if ( n <= lin_count ) + { + // Linear only + _ff_pull_const_addr(app_buf, ff_buf, n*f->item_size); + } + else + { + // Wrap around case + + // Read full words from linear part of buffer + uint16_t lin_4n_bytes = lin_bytes & 0xFFFC; + _ff_pull_const_addr(app_buf, ff_buf, lin_4n_bytes); + ff_buf += lin_4n_bytes; + + // There could be odd 1-3 bytes before the wrap-around boundary + uint8_t rem = lin_bytes & 0x03; + if (rem > 0) + { + volatile uint32_t * reg_tx = (volatile uint32_t *) app_buf; + + uint8_t remrem = (uint8_t) tu_min16(wrap_bytes, 4-rem); + wrap_bytes -= remrem; + + uint32_t tmp32=0; + uint8_t * dst_u8 = (uint8_t *)&tmp32; + + // Read 1-3 bytes before wrapped boundary + while(rem--) *dst_u8++ = *ff_buf++; + + // Read more bytes from beginning to complete a word + ff_buf = f->buffer; + while(remrem--) *dst_u8++ = *ff_buf++; + + *reg_tx = tmp32; + } + else + { + ff_buf = f->buffer; // wrap around to beginning + } + + // Read data wrapped part + if (wrap_bytes > 0) _ff_pull_const_addr(app_buf, ff_buf, wrap_bytes); + } + break; + + default: break; + } +} + +//--------------------------------------------------------------------+ +// Helper +//--------------------------------------------------------------------+ + +// return only the index difference and as such can be used to determine an overflow i.e overflowable count +TU_ATTR_ALWAYS_INLINE static inline +uint16_t _ff_count(uint16_t depth, uint16_t wr_idx, uint16_t rd_idx) +{ + // In case we have non-power of two depth we need a further modification + if (wr_idx >= rd_idx) + { + return (uint16_t) (wr_idx - rd_idx); + } else + { + return (uint16_t) (2*depth - (rd_idx - wr_idx)); + } +} + +// return remaining slot in fifo +TU_ATTR_ALWAYS_INLINE static inline +uint16_t _ff_remaining(uint16_t depth, uint16_t wr_idx, uint16_t rd_idx) +{ + uint16_t const count = _ff_count(depth, wr_idx, rd_idx); + return (depth > count) ? (depth - count) : 0; +} + +//--------------------------------------------------------------------+ +// Index Helper +//--------------------------------------------------------------------+ + +// Advance an absolute index +// "absolute" index is only in the range of [0..2*depth) +static uint16_t advance_index(uint16_t depth, uint16_t idx, uint16_t offset) +{ + // We limit the index space of p such that a correct wrap around happens + // Check for a wrap around or if we are in unused index space - This has to be checked first!! + // We are exploiting the wrap around to the correct index + uint16_t new_idx = (uint16_t) (idx + offset); + if ( (idx > new_idx) || (new_idx >= 2*depth) ) + { + uint16_t const non_used_index_space = (uint16_t) (UINT16_MAX - (2*depth-1)); + new_idx = (uint16_t) (new_idx + non_used_index_space); + } + + return new_idx; +} + +#if 0 // not used but +// Backward an absolute index +static uint16_t backward_index(uint16_t depth, uint16_t idx, uint16_t offset) +{ + // We limit the index space of p such that a correct wrap around happens + // Check for a wrap around or if we are in unused index space - This has to be checked first!! + // We are exploiting the wrap around to the correct index + uint16_t new_idx = (uint16_t) (idx - offset); + if ( (idx < new_idx) || (new_idx >= 2*depth) ) + { + uint16_t const non_used_index_space = (uint16_t) (UINT16_MAX - (2*depth-1)); + new_idx = (uint16_t) (new_idx - non_used_index_space); + } + + return new_idx; +} +#endif + +// index to pointer, simply an modulo with minus. +TU_ATTR_ALWAYS_INLINE static inline +uint16_t idx2ptr(uint16_t depth, uint16_t idx) +{ + // Only run at most 3 times since index is limit in the range of [0..2*depth) + while ( idx >= depth ) idx -= depth; + return idx; +} + +// Works on local copies of w +// When an overwritable fifo is overflowed, rd_idx will be re-index so that it forms +// an full fifo i.e _ff_count() = depth +TU_ATTR_ALWAYS_INLINE static inline +uint16_t _ff_correct_read_index(tu_fifo_t* f, uint16_t wr_idx) +{ + uint16_t rd_idx; + if ( wr_idx >= f->depth ) + { + rd_idx = wr_idx - f->depth; + }else + { + rd_idx = wr_idx + f->depth; + } + + f->rd_idx = rd_idx; + + return rd_idx; +} + +// Works on local copies of w and r +// Must be protected by mutexes since in case of an overflow read pointer gets modified +static bool _tu_fifo_peek(tu_fifo_t* f, void * p_buffer, uint16_t wr_idx, uint16_t rd_idx) +{ + uint16_t cnt = _ff_count(f->depth, wr_idx, rd_idx); + + // nothing to peek + if ( cnt == 0 ) return false; + + // Check overflow and correct if required + if ( cnt > f->depth ) + { + rd_idx = _ff_correct_read_index(f, wr_idx); + cnt = f->depth; + } + + uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); + + // Peek data + _ff_pull(f, p_buffer, rd_ptr); + + return true; +} + +// Works on local copies of w and r +// Must be protected by mutexes since in case of an overflow read pointer gets modified +static uint16_t _tu_fifo_peek_n(tu_fifo_t* f, void * p_buffer, uint16_t n, uint16_t wr_idx, uint16_t rd_idx, tu_fifo_copy_mode_t copy_mode) +{ + uint16_t cnt = _ff_count(f->depth, wr_idx, rd_idx); + + // nothing to peek + if ( cnt == 0 ) return 0; + + // Check overflow and correct if required + if ( cnt > f->depth ) + { + rd_idx = _ff_correct_read_index(f, wr_idx); + cnt = f->depth; + } + + // Check if we can read something at and after offset - if too less is available we read what remains + if ( cnt < n ) n = cnt; + + uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); + + // Peek data + _ff_pull_n(f, p_buffer, n, rd_ptr, copy_mode); + + return n; +} + +static uint16_t _tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n, tu_fifo_copy_mode_t copy_mode) +{ + if ( n == 0 ) return 0; + + _ff_lock(f->mutex_wr); + + uint16_t wr_idx = f->wr_idx; + uint16_t rd_idx = f->rd_idx; + + uint8_t const* buf8 = (uint8_t const*) data; + + TU_LOG(TU_FIFO_DBG, "rd = %3u, wr = %3u, count = %3u, remain = %3u, n = %3u: ", + rd_idx, wr_idx, _ff_count(f->depth, wr_idx, rd_idx), _ff_remaining(f->depth, wr_idx, rd_idx), n); + + if ( !f->overwritable ) + { + // limit up to full + uint16_t const remain = _ff_remaining(f->depth, wr_idx, rd_idx); + n = tu_min16(n, remain); + } + else + { + // In over-writable mode, fifo_write() is allowed even when fifo is full. In such case, + // oldest data in fifo i.e at read pointer data will be overwritten + // Note: we can modify read buffer contents but we must not modify the read index itself within a write function! + // Since it would end up in a race condition with read functions! + if ( n >= f->depth ) + { + // Only copy last part + if ( copy_mode == TU_FIFO_COPY_INC ) + { + buf8 += (n - f->depth) * f->item_size; + }else + { + // TODO should read from hw fifo to discard data, however reading an odd number could + // accidentally discard data. + } + + n = f->depth; + + // We start writing at the read pointer's position since we fill the whole buffer + wr_idx = rd_idx; + } + else + { + uint16_t const overflowable_count = _ff_count(f->depth, wr_idx, rd_idx); + if (overflowable_count + n >= 2*f->depth) + { + // Double overflowed + // Index is bigger than the allowed range [0,2*depth) + // re-position write index to have a full fifo after pushed + wr_idx = advance_index(f->depth, rd_idx, f->depth - n); + + // TODO we should also shift out n bytes from read index since we avoid changing rd index !! + // However memmove() is expensive due to actual copying + wrapping consideration. + // Also race condition could happen anyway if read() is invoke while moving result in corrupted memory + // currently deliberately not implemented --> result in incorrect data read back + }else + { + // normal + single overflowed: + // Index is in the range of [0,2*depth) and thus detect and recoverable. Recovering is handled in read() + // Therefore we just increase write index + // we will correct (re-position) read index later on in fifo_read() function + } + } + } + + if (n) + { + uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); + + TU_LOG(TU_FIFO_DBG, "actual_n = %u, wr_ptr = %u", n, wr_ptr); + + // Write data + _ff_push_n(f, buf8, n, wr_ptr, copy_mode); + + // Advance index + f->wr_idx = advance_index(f->depth, wr_idx, n); + + TU_LOG(TU_FIFO_DBG, "\tnew_wr = %u\r\n", f->wr_idx); + } + + _ff_unlock(f->mutex_wr); + + return n; +} + +static uint16_t _tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n, tu_fifo_copy_mode_t copy_mode) +{ + _ff_lock(f->mutex_rd); + + // Peek the data + // f->rd_idx might get modified in case of an overflow so we can not use a local variable + n = _tu_fifo_peek_n(f, buffer, n, f->wr_idx, f->rd_idx, copy_mode); + + // Advance read pointer + f->rd_idx = advance_index(f->depth, f->rd_idx, n); + + _ff_unlock(f->mutex_rd); + return n; +} + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ + +/******************************************************************************/ +/*! + @brief Get number of items in FIFO. + + As this function only reads the read and write pointers once, this function is + reentrant and thus thread and ISR save without any mutexes. In case an + overflow occurred, this function return f.depth at maximum. Overflows are + checked and corrected for in the read functions! + + @param[in] f + Pointer to the FIFO buffer to manipulate + + @returns Number of items in FIFO + */ +/******************************************************************************/ +uint16_t tu_fifo_count(tu_fifo_t* f) +{ + return tu_min16(_ff_count(f->depth, f->wr_idx, f->rd_idx), f->depth); +} + +/******************************************************************************/ +/*! + @brief Check if FIFO is empty. + + As this function only reads the read and write pointers once, this function is + reentrant and thus thread and ISR save without any mutexes. + + @param[in] f + Pointer to the FIFO buffer to manipulate + + @returns Number of items in FIFO + */ +/******************************************************************************/ +bool tu_fifo_empty(tu_fifo_t* f) +{ + return f->wr_idx == f->rd_idx; +} + +/******************************************************************************/ +/*! + @brief Check if FIFO is full. + + As this function only reads the read and write pointers once, this function is + reentrant and thus thread and ISR save without any mutexes. + + @param[in] f + Pointer to the FIFO buffer to manipulate + + @returns Number of items in FIFO + */ +/******************************************************************************/ +bool tu_fifo_full(tu_fifo_t* f) +{ + return _ff_count(f->depth, f->wr_idx, f->rd_idx) >= f->depth; +} + +/******************************************************************************/ +/*! + @brief Get remaining space in FIFO. + + As this function only reads the read and write pointers once, this function is + reentrant and thus thread and ISR save without any mutexes. + + @param[in] f + Pointer to the FIFO buffer to manipulate + + @returns Number of items in FIFO + */ +/******************************************************************************/ +uint16_t tu_fifo_remaining(tu_fifo_t* f) +{ + return _ff_remaining(f->depth, f->wr_idx, f->rd_idx); +} + +/******************************************************************************/ +/*! + @brief Check if overflow happened. + + BE AWARE - THIS FUNCTION MIGHT NOT GIVE A CORRECT ANSWERE IN CASE WRITE POINTER "OVERFLOWS" + Only one overflow is allowed for this function to work e.g. if depth = 100, you must not + write more than 2*depth-1 items in one rush without updating write pointer. Otherwise + write pointer wraps and your pointer states are messed up. This can only happen if you + use DMAs, write functions do not allow such an error. Avoid such nasty things! + + All reading functions (read, peek) check for overflows and correct read pointer on their own such + that latest items are read. + If required (e.g. for DMA use) you can also correct the read pointer by + tu_fifo_correct_read_pointer(). + + @param[in] f + Pointer to the FIFO buffer to manipulate + + @returns True if overflow happened + */ +/******************************************************************************/ +bool tu_fifo_overflowed(tu_fifo_t* f) +{ + return _ff_count(f->depth, f->wr_idx, f->rd_idx) > f->depth; +} + +// Only use in case tu_fifo_overflow() returned true! +void tu_fifo_correct_read_pointer(tu_fifo_t* f) +{ + _ff_lock(f->mutex_rd); + _ff_correct_read_index(f, f->wr_idx); + _ff_unlock(f->mutex_rd); +} + +/******************************************************************************/ +/*! + @brief Read one element out of the buffer. + + This function will return the element located at the array index of the + read pointer, and then increment the read pointer index. + This function checks for an overflow and corrects read pointer if required. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] buffer + Pointer to the place holder for data read from the buffer + + @returns TRUE if the queue is not empty + */ +/******************************************************************************/ +bool tu_fifo_read(tu_fifo_t* f, void * buffer) +{ + _ff_lock(f->mutex_rd); + + // Peek the data + // f->rd_idx might get modified in case of an overflow so we can not use a local variable + bool ret = _tu_fifo_peek(f, buffer, f->wr_idx, f->rd_idx); + + // Advance pointer + f->rd_idx = advance_index(f->depth, f->rd_idx, ret); + + _ff_unlock(f->mutex_rd); + return ret; +} + +/******************************************************************************/ +/*! + @brief This function will read n elements from the array index specified by + the read pointer and increment the read index. + This function checks for an overflow and corrects read pointer if required. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] buffer + The pointer to data location + @param[in] n + Number of element that buffer can afford + + @returns number of items read from the FIFO + */ +/******************************************************************************/ +uint16_t tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n) +{ + return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_INC); +} + +uint16_t tu_fifo_read_n_const_addr_full_words(tu_fifo_t* f, void * buffer, uint16_t n) +{ + return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_CST_FULL_WORDS); +} + +/******************************************************************************/ +/*! + @brief Read one item without removing it from the FIFO. + This function checks for an overflow and corrects read pointer if required. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] p_buffer + Pointer to the place holder for data read from the buffer + + @returns TRUE if the queue is not empty + */ +/******************************************************************************/ +bool tu_fifo_peek(tu_fifo_t* f, void * p_buffer) +{ + _ff_lock(f->mutex_rd); + bool ret = _tu_fifo_peek(f, p_buffer, f->wr_idx, f->rd_idx); + _ff_unlock(f->mutex_rd); + return ret; +} + +/******************************************************************************/ +/*! + @brief Read n items without removing it from the FIFO + This function checks for an overflow and corrects read pointer if required. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] p_buffer + Pointer to the place holder for data read from the buffer + @param[in] n + Number of items to peek + + @returns Number of bytes written to p_buffer + */ +/******************************************************************************/ +uint16_t tu_fifo_peek_n(tu_fifo_t* f, void * p_buffer, uint16_t n) +{ + _ff_lock(f->mutex_rd); + uint16_t ret = _tu_fifo_peek_n(f, p_buffer, n, f->wr_idx, f->rd_idx, TU_FIFO_COPY_INC); + _ff_unlock(f->mutex_rd); + return ret; +} + +/******************************************************************************/ +/*! + @brief Write one element into the buffer. + + This function will write one element into the array index specified by + the write pointer and increment the write index. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] data + The byte to add to the FIFO + + @returns TRUE if the data was written to the FIFO (overwrittable + FIFO will always return TRUE) + */ +/******************************************************************************/ +bool tu_fifo_write(tu_fifo_t* f, const void * data) +{ + _ff_lock(f->mutex_wr); + + bool ret; + uint16_t const wr_idx = f->wr_idx; + + if ( tu_fifo_full(f) && !f->overwritable ) + { + ret = false; + }else + { + uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); + + // Write data + _ff_push(f, data, wr_ptr); + + // Advance pointer + f->wr_idx = advance_index(f->depth, wr_idx, 1); + + ret = true; + } + + _ff_unlock(f->mutex_wr); + + return ret; +} + +/******************************************************************************/ +/*! + @brief This function will write n elements into the array index specified by + the write pointer and increment the write index. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] data + The pointer to data to add to the FIFO + @param[in] count + Number of element + @return Number of written elements + */ +/******************************************************************************/ +uint16_t tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n) +{ + return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_INC); +} + +/******************************************************************************/ +/*! + @brief This function will write n elements into the array index specified by + the write pointer and increment the write index. The source address will + not be incremented which is useful for reading from registers. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] data + The pointer to data to add to the FIFO + @param[in] count + Number of element + @return Number of written elements + */ +/******************************************************************************/ +uint16_t tu_fifo_write_n_const_addr_full_words(tu_fifo_t* f, const void * data, uint16_t n) +{ + return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_CST_FULL_WORDS); +} + +/******************************************************************************/ +/*! + @brief Clear the fifo read and write pointers + + @param[in] f + Pointer to the FIFO buffer to manipulate + */ +/******************************************************************************/ +bool tu_fifo_clear(tu_fifo_t *f) +{ + _ff_lock(f->mutex_wr); + _ff_lock(f->mutex_rd); + + f->rd_idx = 0; + f->wr_idx = 0; + + _ff_unlock(f->mutex_wr); + _ff_unlock(f->mutex_rd); + return true; +} + +/******************************************************************************/ +/*! + @brief Change the fifo mode to overwritable or not overwritable + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] overwritable + Overwritable mode the fifo is set to + */ +/******************************************************************************/ +bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable) +{ + _ff_lock(f->mutex_wr); + _ff_lock(f->mutex_rd); + + f->overwritable = overwritable; + + _ff_unlock(f->mutex_wr); + _ff_unlock(f->mutex_rd); + + return true; +} + +/******************************************************************************/ +/*! + @brief Advance write pointer - intended to be used in combination with DMA. + It is possible to fill the FIFO by use of a DMA in circular mode. Within + DMA ISRs you may update the write pointer to be able to read from the FIFO. + As long as the DMA is the only process writing into the FIFO this is safe + to use. + + USE WITH CARE - WE DO NOT CONDUCT SAFETY CHECKS HERE! + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] n + Number of items the write pointer moves forward + */ +/******************************************************************************/ +void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n) +{ + f->wr_idx = advance_index(f->depth, f->wr_idx, n); +} + +/******************************************************************************/ +/*! + @brief Advance read pointer - intended to be used in combination with DMA. + It is possible to read from the FIFO by use of a DMA in linear mode. Within + DMA ISRs you may update the read pointer to be able to again write into the + FIFO. As long as the DMA is the only process reading from the FIFO this is + safe to use. + + USE WITH CARE - WE DO NOT CONDUCT SAFETY CHECKS HERE! + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] n + Number of items the read pointer moves forward + */ +/******************************************************************************/ +void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n) +{ + f->rd_idx = advance_index(f->depth, f->rd_idx, n); +} + +/******************************************************************************/ +/*! + @brief Get read info + + Returns the length and pointer from which bytes can be read in a linear manner. + This is of major interest for DMA transmissions. If returned length is zero the + corresponding pointer is invalid. + The read pointer does NOT get advanced, use tu_fifo_advance_read_pointer() to + do so! + @param[in] f + Pointer to FIFO + @param[out] *info + Pointer to struct which holds the desired infos + */ +/******************************************************************************/ +void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) +{ + // Operate on temporary values in case they change in between + uint16_t wr_idx = f->wr_idx; + uint16_t rd_idx = f->rd_idx; + + uint16_t cnt = _ff_count(f->depth, wr_idx, rd_idx); + + // Check overflow and correct if required - may happen in case a DMA wrote too fast + if (cnt > f->depth) + { + _ff_lock(f->mutex_rd); + rd_idx = _ff_correct_read_index(f, wr_idx); + _ff_unlock(f->mutex_rd); + + cnt = f->depth; + } + + // Check if fifo is empty + if (cnt == 0) + { + info->len_lin = 0; + info->len_wrap = 0; + info->ptr_lin = NULL; + info->ptr_wrap = NULL; + return; + } + + // Get relative pointers + uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); + uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); + + // Copy pointer to buffer to start reading from + info->ptr_lin = &f->buffer[rd_ptr]; + + // Check if there is a wrap around necessary + if (wr_ptr > rd_ptr) + { + // Non wrapping case + info->len_lin = cnt; + + info->len_wrap = 0; + info->ptr_wrap = NULL; + } + else + { + info->len_lin = f->depth - rd_ptr; // Also the case if FIFO was full + + info->len_wrap = cnt - info->len_lin; + info->ptr_wrap = f->buffer; + } +} + +/******************************************************************************/ +/*! + @brief Get linear write info + + Returns the length and pointer to which bytes can be written into FIFO in a linear manner. + This is of major interest for DMA transmissions not using circular mode. If a returned length is zero the + corresponding pointer is invalid. The returned lengths summed up are the currently free space in the FIFO. + The write pointer does NOT get advanced, use tu_fifo_advance_write_pointer() to do so! + TAKE CARE TO NOT OVERFLOW THE BUFFER MORE THAN TWO TIMES THE FIFO DEPTH - IT CAN NOT RECOVERE OTHERWISE! + @param[in] f + Pointer to FIFO + @param[out] *info + Pointer to struct which holds the desired infos + */ +/******************************************************************************/ +void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) +{ + uint16_t wr_idx = f->wr_idx; + uint16_t rd_idx = f->rd_idx; + uint16_t remain = _ff_remaining(f->depth, wr_idx, rd_idx); + + if (remain == 0) + { + info->len_lin = 0; + info->len_wrap = 0; + info->ptr_lin = NULL; + info->ptr_wrap = NULL; + return; + } + + // Get relative pointers + uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); + uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); + + // Copy pointer to buffer to start writing to + info->ptr_lin = &f->buffer[wr_ptr]; + + if (wr_ptr < rd_ptr) + { + // Non wrapping case + info->len_lin = rd_ptr-wr_ptr; + info->len_wrap = 0; + info->ptr_wrap = NULL; + } + else + { + info->len_lin = f->depth - wr_ptr; + info->len_wrap = remain - info->len_lin; // Remaining length - n already was limited to remain or FIFO depth + info->ptr_wrap = f->buffer; // Always start of buffer + } +} diff --git a/test-devices/loopback-stm32/lib/tinyusb/common/tusb_fifo.h b/test-devices/loopback-stm32/lib/tinyusb/common/tusb_fifo.h new file mode 100644 index 00000000..2d9f5e66 --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/common/tusb_fifo.h @@ -0,0 +1,195 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * Copyright (c) 2020 Reinhard Panhuber - rework to unmasked pointers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_FIFO_H_ +#define _TUSB_FIFO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// Due to the use of unmasked pointers, this FIFO does not suffer from losing +// one item slice. Furthermore, write and read operations are completely +// decoupled as write and read functions do not modify a common state. Henceforth, +// writing or reading from the FIFO within an ISR is safe as long as no other +// process (thread or ISR) interferes. +// Also, this FIFO is ready to be used in combination with a DMA as the write and +// read pointers can be updated from within a DMA ISR. Overflows are detectable +// within a certain number (see tu_fifo_overflow()). + +#include "common/tusb_common.h" +#include "osal/osal.h" + +// mutex is only needed for RTOS +// for OS None, we don't get preempted +#define CFG_FIFO_MUTEX OSAL_MUTEX_REQUIRED + +/* Write/Read index is always in the range of: + * 0 .. 2*depth-1 + * The extra window allow us to determine the fifo state of empty or full with only 2 indices + * Following are examples with depth = 3 + * + * - empty: W = R + * | + * ------------------------- + * | 0 | RW| 2 | 3 | 4 | 5 | + * + * - full 1: W > R + * | + * ------------------------- + * | 0 | R | 2 | 3 | W | 5 | + * + * - full 2: W < R + * | + * ------------------------- + * | 0 | 1 | W | 3 | 4 | R | + * + * - Number of items in the fifo can be determined in either cases: + * - case W >= R: Count = W - R + * - case W < R: Count = 2*depth - (R - W) + * + * In non-overwritable mode, computed Count (in above 2 cases) is at most equal to depth. + * However, in over-writable mode, write index can be repeatedly increased and count can be + * temporarily larger than depth (overflowed condition) e.g + * + * - Overflowed 1: write(3), write(1) + * In this case we will adjust Read index when read()/peek() is called so that count = depth. + * | + * ------------------------- + * | R | 1 | 2 | 3 | W | 5 | + * + * - Double Overflowed i.e index is out of allowed range [0,2*depth) + * This occurs when we continue to write after 1st overflowed to 2nd overflowed. e.g: + * write(3), write(1), write(2) + * This must be prevented since it will cause unrecoverable state, in above example + * if not handled the fifo will be empty instead of continue-to-be full. Since we must not modify + * read index in write() function, which cause race condition. We will re-position write index so that + * after data is written it is a full fifo i.e W = depth - R + * + * re-position W = 1 before write(2) + * Note: we should also move data from mem[3] to read index as well, but deliberately skipped here + * since it is an expensive operation !!! + * | + * ------------------------- + * | R | W | 2 | 3 | 4 | 5 | + * + * perform write(2), result is still a full fifo. + * + * | + * ------------------------- + * | R | 1 | 2 | W | 4 | 5 | + */ +typedef struct { + uint8_t* buffer ; // buffer pointer + uint16_t depth ; // max items + + struct TU_ATTR_PACKED { + uint16_t item_size : 15; // size of each item + bool overwritable : 1 ; // ovwerwritable when full + }; + + volatile uint16_t wr_idx ; // write index + volatile uint16_t rd_idx ; // read index + +#if OSAL_MUTEX_REQUIRED + osal_mutex_t mutex_wr; + osal_mutex_t mutex_rd; +#endif + +} tu_fifo_t; + +typedef struct { + uint16_t len_lin ; ///< linear length in item size + uint16_t len_wrap ; ///< wrapped length in item size + void * ptr_lin ; ///< linear part start pointer + void * ptr_wrap ; ///< wrapped part start pointer +} tu_fifo_buffer_info_t; + +#define TU_FIFO_INIT(_buffer, _depth, _type, _overwritable){\ + .buffer = _buffer, \ + .depth = _depth, \ + .item_size = sizeof(_type), \ + .overwritable = _overwritable, \ +} + +#define TU_FIFO_DEF(_name, _depth, _type, _overwritable) \ + uint8_t _name##_buf[_depth*sizeof(_type)]; \ + tu_fifo_t _name = TU_FIFO_INIT(_name##_buf, _depth, _type, _overwritable) + +bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable); +bool tu_fifo_clear(tu_fifo_t *f); +bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable); + +#if OSAL_MUTEX_REQUIRED + TU_ATTR_ALWAYS_INLINE static inline + void tu_fifo_config_mutex(tu_fifo_t *f, osal_mutex_t wr_mutex, osal_mutex_t rd_mutex) { + f->mutex_wr = wr_mutex; + f->mutex_rd = rd_mutex; + } +#else + #define tu_fifo_config_mutex(_f, _wr_mutex, _rd_mutex) +#endif + +bool tu_fifo_write (tu_fifo_t* f, void const * p_data); +uint16_t tu_fifo_write_n (tu_fifo_t* f, void const * p_data, uint16_t n); +uint16_t tu_fifo_write_n_const_addr_full_words (tu_fifo_t* f, const void * data, uint16_t n); + +bool tu_fifo_read (tu_fifo_t* f, void * p_buffer); +uint16_t tu_fifo_read_n (tu_fifo_t* f, void * p_buffer, uint16_t n); +uint16_t tu_fifo_read_n_const_addr_full_words (tu_fifo_t* f, void * buffer, uint16_t n); + +bool tu_fifo_peek (tu_fifo_t* f, void * p_buffer); +uint16_t tu_fifo_peek_n (tu_fifo_t* f, void * p_buffer, uint16_t n); + +uint16_t tu_fifo_count (tu_fifo_t* f); +uint16_t tu_fifo_remaining (tu_fifo_t* f); +bool tu_fifo_empty (tu_fifo_t* f); +bool tu_fifo_full (tu_fifo_t* f); +bool tu_fifo_overflowed (tu_fifo_t* f); +void tu_fifo_correct_read_pointer (tu_fifo_t* f); + +TU_ATTR_ALWAYS_INLINE static inline +uint16_t tu_fifo_depth(tu_fifo_t* f) { + return f->depth; +} + +// Pointer modifications intended to be used in combinations with DMAs. +// USE WITH CARE - NO SAFETY CHECKS CONDUCTED HERE! NOT MUTEX PROTECTED! +void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n); +void tu_fifo_advance_read_pointer (tu_fifo_t *f, uint16_t n); + +// If you want to read/write from/to the FIFO by use of a DMA, you may need to conduct two copies +// to handle a possible wrapping part. These functions deliver a pointer to start +// reading/writing from/to and a valid linear length along which no wrap occurs. +void tu_fifo_get_read_info (tu_fifo_t *f, tu_fifo_buffer_info_t *info); +void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info); + +#ifdef __cplusplus +} +#endif + +#endif /* _TUSB_FIFO_H_ */ diff --git a/test-devices/loopback-stm32/lib/tinyusb/common/tusb_mcu.h b/test-devices/loopback-stm32/lib/tinyusb/common/tusb_mcu.h new file mode 100644 index 00000000..5a567f2d --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/common/tusb_mcu.h @@ -0,0 +1,451 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021, Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef TUSB_MCU_H_ +#define TUSB_MCU_H_ + +//--------------------------------------------------------------------+ +// Port/Platform Specific +// TUP stand for TinyUSB Port/Platform (can be renamed) +//--------------------------------------------------------------------+ + +//------------- Unaligned Memory Access -------------// + +#ifdef __ARM_ARCH + // ARM Architecture set __ARM_FEATURE_UNALIGNED to 1 for mcu supports unaligned access + #if defined(__ARM_FEATURE_UNALIGNED) && __ARM_FEATURE_UNALIGNED == 1 + #define TUP_ARCH_STRICT_ALIGN 0 + #else + #define TUP_ARCH_STRICT_ALIGN 1 + #endif +#else + // TODO default to strict align for others + // Should investigate other architecture such as risv, xtensa, mips for optimal setting + #define TUP_ARCH_STRICT_ALIGN 1 +#endif + +/* USB Controller Attributes for Device, Host or MCU (both) + * - ENDPOINT_MAX: max (logical) number of endpoint + * - ENDPOINT_EXCLUSIVE_NUMBER: endpoint number with different direction IN and OUT aren't allowed, + * e.g EP1 OUT & EP1 IN cannot exist together + * - RHPORT_HIGHSPEED: support highspeed with on-chip PHY + */ + +//--------------------------------------------------------------------+ +// NXP +//--------------------------------------------------------------------+ +#if TU_CHECK_MCU(OPT_MCU_LPC11UXX, OPT_MCU_LPC13XX, OPT_MCU_LPC15XX) + #define TUP_USBIP_IP3511 + #define TUP_DCD_ENDPOINT_MAX 5 + +#elif TU_CHECK_MCU(OPT_MCU_LPC175X_6X, OPT_MCU_LPC177X_8X, OPT_MCU_LPC40XX) + #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_USBIP_OHCI + #define TUP_OHCI_RHPORTS 2 + +#elif TU_CHECK_MCU(OPT_MCU_LPC51UXX) + #define TUP_USBIP_IP3511 + #define TUP_DCD_ENDPOINT_MAX 5 + +#elif TU_CHECK_MCU(OPT_MCU_LPC54) + // TODO USB0 has 5, USB1 has 6 + #define TUP_USBIP_IP3511 + #define TUP_DCD_ENDPOINT_MAX 6 + +#elif TU_CHECK_MCU(OPT_MCU_LPC55) + // TODO USB0 has 5, USB1 has 6 + #define TUP_USBIP_IP3511 + #define TUP_DCD_ENDPOINT_MAX 6 + +#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX) + // USB0 has 6 with HS PHY, USB1 has 4 only FS + #define TUP_USBIP_CHIPIDEA_HS + #define TUP_USBIP_EHCI + + #define TUP_DCD_ENDPOINT_MAX 6 + #define TUP_RHPORT_HIGHSPEED 1 + +#elif TU_CHECK_MCU(OPT_MCU_MCXN9) + // USB0 is chipidea FS + #define TUP_USBIP_CHIPIDEA_FS + #define TUP_USBIP_CHIPIDEA_FS_MCX + + // USB1 is chipidea HS + #define TUP_USBIP_CHIPIDEA_HS + #define TUP_USBIP_EHCI + + #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_RHPORT_HIGHSPEED 1 + +#elif TU_CHECK_MCU(OPT_MCU_MCXA15) + // USB0 is chipidea FS + #define TUP_USBIP_CHIPIDEA_FS + #define TUP_USBIP_CHIPIDEA_FS_MCX + + #define TUP_DCD_ENDPOINT_MAX 16 + +#elif TU_CHECK_MCU(OPT_MCU_MIMXRT1XXX) + #define TUP_USBIP_CHIPIDEA_HS + #define TUP_USBIP_EHCI + + #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_RHPORT_HIGHSPEED 1 + +#elif TU_CHECK_MCU(OPT_MCU_KINETIS_KL, OPT_MCU_KINETIS_K32L, OPT_MCU_KINETIS_K) + #define TUP_USBIP_CHIPIDEA_FS + #define TUP_USBIP_CHIPIDEA_FS_KINETIS + #define TUP_DCD_ENDPOINT_MAX 16 + +#elif TU_CHECK_MCU(OPT_MCU_MM32F327X) + #define TUP_DCD_ENDPOINT_MAX 16 + +//--------------------------------------------------------------------+ +// Nordic +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_NRF5X) + // 8 CBI + 1 ISO + #define TUP_DCD_ENDPOINT_MAX 9 + +//--------------------------------------------------------------------+ +// Microchip +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_SAMD21, OPT_MCU_SAMD51, OPT_MCU_SAME5X) || \ + TU_CHECK_MCU(OPT_MCU_SAMD11, OPT_MCU_SAML21, OPT_MCU_SAML22) + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_SAMG) + #define TUP_DCD_ENDPOINT_MAX 6 + #define TUP_DCD_ENDPOINT_EXCLUSIVE_NUMBER + +#elif TU_CHECK_MCU(OPT_MCU_SAMX7X) + #define TUP_DCD_ENDPOINT_MAX 10 + #define TUP_RHPORT_HIGHSPEED 1 + #define TUP_DCD_ENDPOINT_EXCLUSIVE_NUMBER + +#elif TU_CHECK_MCU(OPT_MCU_PIC32MZ) + #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_EXCLUSIVE_NUMBER + +#elif TU_CHECK_MCU(OPT_MCU_PIC32MX, OPT_MCU_PIC32MM, OPT_MCU_PIC32MK) || \ + TU_CHECK_MCU(OPT_MCU_PIC24, OPT_MCU_DSPIC33) + #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_DCD_ENDPOINT_EXCLUSIVE_NUMBER + +//--------------------------------------------------------------------+ +// ST +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_STM32F0) + #define TUP_USBIP_FSDEV + #define TUP_USBIP_FSDEV_STM32 + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_STM32F1) + // - F102, F103 use fsdev + // - F105, F107 use dwc2 + #if defined (STM32F105x8) || defined (STM32F105xB) || defined (STM32F105xC) || \ + defined (STM32F107xB) || defined (STM32F107xC) + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_STM32 + + #define TUP_DCD_ENDPOINT_MAX 4 + #elif defined(STM32F102x6) || defined(STM32F102xB) || \ + defined(STM32F103x6) || defined(STM32F103xB) || defined(STM32F103xE) || defined(STM32F103xG) + #define TUP_USBIP_FSDEV + #define TUP_USBIP_FSDEV_STM32 + #define TUP_DCD_ENDPOINT_MAX 8 + #else + #error "Unsupported STM32F1 mcu" + #endif + +#elif TU_CHECK_MCU(OPT_MCU_STM32F2) + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_STM32 + + // FS has 4 ep, HS has 5 ep + #define TUP_DCD_ENDPOINT_MAX 6 + +#elif TU_CHECK_MCU(OPT_MCU_STM32F3) + #define TUP_USBIP_FSDEV + #define TUP_USBIP_FSDEV_STM32 + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_STM32F4) + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_STM32 + + // For most mcu, FS has 4, HS has 6. TODO 446/469/479 HS has 9 + #define TUP_DCD_ENDPOINT_MAX 6 + +#elif TU_CHECK_MCU(OPT_MCU_STM32F7) + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_STM32 + + // FS has 6, HS has 9 + #define TUP_DCD_ENDPOINT_MAX 9 + + // MCU with on-chip HS Phy + #if defined(STM32F723xx) || defined(STM32F730xx) || defined(STM32F733xx) + #define TUP_RHPORT_HIGHSPEED 1 // Port0: FS, Port1: HS + #endif + +#elif TU_CHECK_MCU(OPT_MCU_STM32H7) + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_STM32 + + #define TUP_DCD_ENDPOINT_MAX 9 + +#elif TU_CHECK_MCU(OPT_MCU_STM32H5) + #define TUP_USBIP_FSDEV + #define TUP_USBIP_FSDEV_STM32 + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_STM32G4) + // Device controller + #define TUP_USBIP_FSDEV + #define TUP_USBIP_FSDEV_STM32 + + // TypeC controller + #define TUP_USBIP_TYPEC_STM32 + #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_TYPEC_RHPORTS_NUM 1 + +#elif TU_CHECK_MCU(OPT_MCU_STM32G0) + #define TUP_USBIP_FSDEV + #define TUP_USBIP_FSDEV_STM32 + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_STM32L0, OPT_MCU_STM32L1) + #define TUP_USBIP_FSDEV + #define TUP_USBIP_FSDEV_STM32 + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_STM32L4) + // - L4x2, L4x3 use fsdev + // - L4x4, L4x6, L4x7, L4x9 use dwc2 + #if defined (STM32L475xx) || defined (STM32L476xx) || \ + defined (STM32L485xx) || defined (STM32L486xx) || defined (STM32L496xx) || \ + defined (STM32L4A6xx) || defined (STM32L4P5xx) || defined (STM32L4Q5xx) || \ + defined (STM32L4R5xx) || defined (STM32L4R7xx) || defined (STM32L4R9xx) || \ + defined (STM32L4S5xx) || defined (STM32L4S7xx) || defined (STM32L4S9xx) + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_STM32 + + #define TUP_DCD_ENDPOINT_MAX 6 + #elif defined(STM32L412xx) || defined(STM32L422xx) || defined(STM32L432xx) || defined(STM32L433xx) || \ + defined(STM32L442xx) || defined(STM32L443xx) || defined(STM32L452xx) || defined(STM32L462xx) + #define TUP_USBIP_FSDEV + #define TUP_USBIP_FSDEV_STM32 + #define TUP_DCD_ENDPOINT_MAX 8 + #else + #error "Unsupported STM32L4 mcu" + #endif + +#elif TU_CHECK_MCU(OPT_MCU_STM32WB) + #define TUP_USBIP_FSDEV + #define TUP_USBIP_FSDEV_STM32 + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_STM32U5) + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_STM32 + + // U59x/5Ax/5Fx/5Gx are highspeed with built-in HS PHY + #if defined(STM32U595xx) || defined(STM32U599xx) || defined(STM32U5A5xx) || defined(STM32U5A9xx) || \ + defined(STM32U5F7xx) || defined(STM32U5F9xx) || defined(STM32U5G7xx) || defined(STM32U5G9xx) + #define TUP_DCD_ENDPOINT_MAX 9 + #define TUP_RHPORT_HIGHSPEED 1 + #else + #define TUP_DCD_ENDPOINT_MAX 6 + #endif + +#elif TU_CHECK_MCU(OPT_MCU_STM32L5) + #define TUP_USBIP_FSDEV + #define TUP_USBIP_FSDEV_STM32 + #define TUP_DCD_ENDPOINT_MAX 8 + +//--------------------------------------------------------------------+ +// Sony +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_CXD56) + #define TUP_DCD_ENDPOINT_MAX 7 + #define TUP_RHPORT_HIGHSPEED 1 + #define TUP_DCD_ENDPOINT_EXCLUSIVE_NUMBER + +//--------------------------------------------------------------------+ +// TI +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_MSP430x5xx) + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_MSP432E4, OPT_MCU_TM4C123, OPT_MCU_TM4C129) + #define TUP_DCD_ENDPOINT_MAX 8 + +//--------------------------------------------------------------------+ +// ValentyUSB (Litex) +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_VALENTYUSB_EPTRI) + #define TUP_DCD_ENDPOINT_MAX 16 + +//--------------------------------------------------------------------+ +// Nuvoton +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_NUC121, OPT_MCU_NUC126) + #define TUP_DCD_ENDPOINT_MAX 8 + +#elif TU_CHECK_MCU(OPT_MCU_NUC120) + #define TUP_DCD_ENDPOINT_MAX 6 + +#elif TU_CHECK_MCU(OPT_MCU_NUC505) + #define TUP_DCD_ENDPOINT_MAX 12 + #define TUP_RHPORT_HIGHSPEED 1 + +//--------------------------------------------------------------------+ +// Espressif +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) + #define TUP_USBIP_DWC2 + #define TUP_DCD_ENDPOINT_MAX 6 + +#elif TU_CHECK_MCU(OPT_MCU_ESP32) && (CFG_TUD_ENABLED || !(defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421)) + #error "MCUs are only supported with CFG_TUH_MAX3421 enabled" + +//--------------------------------------------------------------------+ +// Dialog +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_DA1469X) + #define TUP_DCD_ENDPOINT_MAX 4 + +//--------------------------------------------------------------------+ +// Raspberry Pi +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_RP2040) + #define TUP_DCD_ENDPOINT_MAX 16 + + #define TU_ATTR_FAST_FUNC __attribute__((section(".time_critical.tinyusb"))) + +//--------------------------------------------------------------------+ +// Silabs +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_EFM32GG) + #define TUP_USBIP_DWC2 + #define TUP_DCD_ENDPOINT_MAX 7 + +//--------------------------------------------------------------------+ +// Renesas +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_RX63X, OPT_MCU_RX65X, OPT_MCU_RX72N, OPT_MCU_RAXXX) + #define TUP_USBIP_RUSB2 + #define TUP_DCD_ENDPOINT_MAX 10 + +//--------------------------------------------------------------------+ +// GigaDevice +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_GD32VF103) + #define TUP_USBIP_DWC2 + #define TUP_DCD_ENDPOINT_MAX 4 + +//--------------------------------------------------------------------+ +// Broadcom +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_BCM2711, OPT_MCU_BCM2835, OPT_MCU_BCM2837) + #define TUP_USBIP_DWC2 + #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_RHPORT_HIGHSPEED 1 + +//--------------------------------------------------------------------+ +// Infineon +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_XMC4000) + #define TUP_USBIP_DWC2 + #define TUP_DCD_ENDPOINT_MAX 8 + +//--------------------------------------------------------------------+ +// BridgeTek +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_FT90X) + #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_RHPORT_HIGHSPEED 1 + +#elif TU_CHECK_MCU(OPT_MCU_FT93X) + #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_RHPORT_HIGHSPEED 1 + +//--------------------------------------------------------------------+ +// Allwinner +//--------------------------------------------------------------------+ +#elif TU_CHECK_MCU(OPT_MCU_F1C100S) + #define TUP_DCD_ENDPOINT_MAX 4 + +//------------- WCH -------------// +#elif TU_CHECK_MCU(OPT_MCU_CH32V307) + #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_RHPORT_HIGHSPEED 1 + +#elif TU_CHECK_MCU(OPT_MCU_CH32F20X) + #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_RHPORT_HIGHSPEED 1 +#endif + + +//--------------------------------------------------------------------+ +// External USB controller +//--------------------------------------------------------------------+ + +#if defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421 + #ifndef CFG_TUH_MAX3421_ENDPOINT_TOTAL + #define CFG_TUH_MAX3421_ENDPOINT_TOTAL (8 + 4*(CFG_TUH_DEVICE_MAX-1)) + #endif +#endif + + +//--------------------------------------------------------------------+ +// Default Values +//--------------------------------------------------------------------+ + +#ifndef TUP_MCU_MULTIPLE_CORE +#define TUP_MCU_MULTIPLE_CORE 0 +#endif + +#if !defined(TUP_DCD_ENDPOINT_MAX) && defined(CFG_TUD_ENABLED) && CFG_TUD_ENABLED +#warning "TUP_DCD_ENDPOINT_MAX is not defined for this MCU, default to 8" + #define TUP_DCD_ENDPOINT_MAX 8 +#endif + +// Default to fullspeed if not defined +#ifndef TUP_RHPORT_HIGHSPEED + #define TUP_RHPORT_HIGHSPEED 0 +#endif + +// fast function, normally mean placing function in SRAM +#ifndef TU_ATTR_FAST_FUNC + #define TU_ATTR_FAST_FUNC +#endif + +#if defined(TUP_USBIP_DWC2) || defined(TUP_USBIP_FSDEV) + #define TUP_DCD_EDPT_ISO_ALLOC +#endif + +#endif diff --git a/test-devices/loopback-stm32/lib/tinyusb/common/tusb_private.h b/test-devices/loopback-stm32/lib/tinyusb/common/tusb_private.h new file mode 100644 index 00000000..373a5025 --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/common/tusb_private.h @@ -0,0 +1,177 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022, Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + + +#ifndef _TUSB_PRIVATE_H_ +#define _TUSB_PRIVATE_H_ + +// Internal Helper used by Host and Device Stack + +#ifdef __cplusplus + extern "C" { +#endif + +typedef struct TU_ATTR_PACKED +{ + volatile uint8_t busy : 1; + volatile uint8_t stalled : 1; + volatile uint8_t claimed : 1; +}tu_edpt_state_t; + +typedef struct { + bool is_host; // host or device most + union { + uint8_t daddr; + uint8_t rhport; + uint8_t hwid; + }; + uint8_t ep_addr; + uint8_t ep_speed; + + uint16_t ep_packetsize; + uint16_t ep_bufsize; + + // TODO xfer_fifo can skip this buffer + uint8_t* ep_buf; + + tu_fifo_t ff; + + // mutex: read if ep rx, write if e tx + OSAL_MUTEX_DEF(ff_mutexdef); + +}tu_edpt_stream_t; + +//--------------------------------------------------------------------+ +// Endpoint +//--------------------------------------------------------------------+ + +// Check if endpoint descriptor is valid per USB specs +bool tu_edpt_validate(tusb_desc_endpoint_t const * desc_ep, tusb_speed_t speed); + +// Bind all endpoint of a interface descriptor to class driver +void tu_edpt_bind_driver(uint8_t ep2drv[][2], tusb_desc_interface_t const* p_desc, uint16_t desc_len, uint8_t driver_id); + +// Calculate total length of n interfaces (depending on IAD) +uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const* desc_itf, uint8_t itf_count, uint16_t max_len); + +// Claim an endpoint with provided mutex +bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex); + +// Release an endpoint with provided mutex +bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex); + +//--------------------------------------------------------------------+ +// Endpoint Stream +//--------------------------------------------------------------------+ + +// Init an endpoint stream +bool tu_edpt_stream_init(tu_edpt_stream_t* s, bool is_host, bool is_tx, bool overwritable, + void* ff_buf, uint16_t ff_bufsize, uint8_t* ep_buf, uint16_t ep_bufsize); + +// Deinit an endpoint stream +bool tu_edpt_stream_deinit(tu_edpt_stream_t* s); + +// Open an stream for an endpoint +// hwid is either device address (host mode) or rhport (device mode) +TU_ATTR_ALWAYS_INLINE static inline +void tu_edpt_stream_open(tu_edpt_stream_t* s, uint8_t hwid, tusb_desc_endpoint_t const *desc_ep) { + tu_fifo_clear(&s->ff); + s->hwid = hwid; + s->ep_addr = desc_ep->bEndpointAddress; + s->ep_packetsize = tu_edpt_packet_size(desc_ep); +} + +TU_ATTR_ALWAYS_INLINE static inline +void tu_edpt_stream_close(tu_edpt_stream_t* s) { + s->hwid = 0; + s->ep_addr = 0; +} + +// Clear fifo +TU_ATTR_ALWAYS_INLINE static inline +bool tu_edpt_stream_clear(tu_edpt_stream_t* s) { + return tu_fifo_clear(&s->ff); +} + +//--------------------------------------------------------------------+ +// Stream Write +//--------------------------------------------------------------------+ + +// Write to stream +uint32_t tu_edpt_stream_write(tu_edpt_stream_t* s, void const *buffer, uint32_t bufsize); + +// Start an usb transfer if endpoint is not busy +uint32_t tu_edpt_stream_write_xfer(tu_edpt_stream_t* s); + +// Start an zero-length packet if needed +bool tu_edpt_stream_write_zlp_if_needed(tu_edpt_stream_t* s, uint32_t last_xferred_bytes); + +// Get the number of bytes available for writing +TU_ATTR_ALWAYS_INLINE static inline +uint32_t tu_edpt_stream_write_available(tu_edpt_stream_t* s) { + return (uint32_t) tu_fifo_remaining(&s->ff); +} + +//--------------------------------------------------------------------+ +// Stream Read +//--------------------------------------------------------------------+ + +// Read from stream +uint32_t tu_edpt_stream_read(tu_edpt_stream_t* s, void* buffer, uint32_t bufsize); + +// Start an usb transfer if endpoint is not busy +uint32_t tu_edpt_stream_read_xfer(tu_edpt_stream_t* s); + +// Must be called in the transfer complete callback +TU_ATTR_ALWAYS_INLINE static inline +void tu_edpt_stream_read_xfer_complete(tu_edpt_stream_t* s, uint32_t xferred_bytes) { + tu_fifo_write_n(&s->ff, s->ep_buf, (uint16_t) xferred_bytes); +} + +// Same as tu_edpt_stream_read_xfer_complete but skip the first n bytes +TU_ATTR_ALWAYS_INLINE static inline +void tu_edpt_stream_read_xfer_complete_offset(tu_edpt_stream_t* s, uint32_t xferred_bytes, uint32_t skip_offset) { + if (skip_offset < xferred_bytes) { + tu_fifo_write_n(&s->ff, s->ep_buf + skip_offset, (uint16_t) (xferred_bytes - skip_offset)); + } +} + +// Get the number of bytes available for reading +TU_ATTR_ALWAYS_INLINE static inline +uint32_t tu_edpt_stream_read_available(tu_edpt_stream_t* s) { + return (uint32_t) tu_fifo_count(&s->ff); +} + +TU_ATTR_ALWAYS_INLINE static inline +bool tu_edpt_stream_peek(tu_edpt_stream_t* s, uint8_t* ch) { + return tu_fifo_peek(&s->ff, ch); +} + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_PRIVATE_H_ */ diff --git a/test-devices/loopback-stm32/lib/tinyusb/common/tusb_types.h b/test-devices/loopback-stm32/lib/tinyusb/common/tusb_types.h new file mode 100644 index 00000000..b571f9b7 --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/common/tusb_types.h @@ -0,0 +1,535 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef TUSB_TYPES_H_ +#define TUSB_TYPES_H_ + +#include +#include +#include "tusb_compiler.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/*------------------------------------------------------------------*/ +/* CONSTANTS + *------------------------------------------------------------------*/ + +/// defined base on EHCI specs value for Endpoint Speed +typedef enum { + TUSB_SPEED_FULL = 0, + TUSB_SPEED_LOW = 1, + TUSB_SPEED_HIGH = 2, + TUSB_SPEED_INVALID = 0xff, +} tusb_speed_t; + +/// defined base on USB Specs Endpoint's bmAttributes +typedef enum { + TUSB_XFER_CONTROL = 0 , + TUSB_XFER_ISOCHRONOUS , + TUSB_XFER_BULK , + TUSB_XFER_INTERRUPT +} tusb_xfer_type_t; + +typedef enum { + TUSB_DIR_OUT = 0, + TUSB_DIR_IN = 1, + + TUSB_DIR_IN_MASK = 0x80 +} tusb_dir_t; + +enum { + TUSB_EPSIZE_BULK_FS = 64, + TUSB_EPSIZE_BULK_HS = 512, + + TUSB_EPSIZE_ISO_FS_MAX = 1023, + TUSB_EPSIZE_ISO_HS_MAX = 1024, +}; + +/// Isochronous Endpoint Attributes +typedef enum { + TUSB_ISO_EP_ATT_NO_SYNC = 0x00, + TUSB_ISO_EP_ATT_ASYNCHRONOUS = 0x04, + TUSB_ISO_EP_ATT_ADAPTIVE = 0x08, + TUSB_ISO_EP_ATT_SYNCHRONOUS = 0x0C, + TUSB_ISO_EP_ATT_DATA = 0x00, ///< Data End Point + TUSB_ISO_EP_ATT_EXPLICIT_FB = 0x10, ///< Feedback End Point + TUSB_ISO_EP_ATT_IMPLICIT_FB = 0x20, ///< Data endpoint that also serves as an implicit feedback +} tusb_iso_ep_attribute_t; + +/// USB Descriptor Types +typedef enum { + TUSB_DESC_DEVICE = 0x01, + TUSB_DESC_CONFIGURATION = 0x02, + TUSB_DESC_STRING = 0x03, + TUSB_DESC_INTERFACE = 0x04, + TUSB_DESC_ENDPOINT = 0x05, + TUSB_DESC_DEVICE_QUALIFIER = 0x06, + TUSB_DESC_OTHER_SPEED_CONFIG = 0x07, + TUSB_DESC_INTERFACE_POWER = 0x08, + TUSB_DESC_OTG = 0x09, + TUSB_DESC_DEBUG = 0x0A, + TUSB_DESC_INTERFACE_ASSOCIATION = 0x0B, + + TUSB_DESC_BOS = 0x0F, + TUSB_DESC_DEVICE_CAPABILITY = 0x10, + + TUSB_DESC_FUNCTIONAL = 0x21, + + // Class Specific Descriptor + TUSB_DESC_CS_DEVICE = 0x21, + TUSB_DESC_CS_CONFIGURATION = 0x22, + TUSB_DESC_CS_STRING = 0x23, + TUSB_DESC_CS_INTERFACE = 0x24, + TUSB_DESC_CS_ENDPOINT = 0x25, + + TUSB_DESC_SUPERSPEED_ENDPOINT_COMPANION = 0x30, + TUSB_DESC_SUPERSPEED_ISO_ENDPOINT_COMPANION = 0x31 +} tusb_desc_type_t; + +typedef enum { + TUSB_REQ_GET_STATUS = 0 , + TUSB_REQ_CLEAR_FEATURE = 1 , + TUSB_REQ_RESERVED = 2 , + TUSB_REQ_SET_FEATURE = 3 , + TUSB_REQ_RESERVED2 = 4 , + TUSB_REQ_SET_ADDRESS = 5 , + TUSB_REQ_GET_DESCRIPTOR = 6 , + TUSB_REQ_SET_DESCRIPTOR = 7 , + TUSB_REQ_GET_CONFIGURATION = 8 , + TUSB_REQ_SET_CONFIGURATION = 9 , + TUSB_REQ_GET_INTERFACE = 10 , + TUSB_REQ_SET_INTERFACE = 11 , + TUSB_REQ_SYNCH_FRAME = 12 +} tusb_request_code_t; + +typedef enum { + TUSB_REQ_FEATURE_EDPT_HALT = 0, + TUSB_REQ_FEATURE_REMOTE_WAKEUP = 1, + TUSB_REQ_FEATURE_TEST_MODE = 2 +} tusb_request_feature_selector_t; + +typedef enum { + TUSB_REQ_TYPE_STANDARD = 0, + TUSB_REQ_TYPE_CLASS, + TUSB_REQ_TYPE_VENDOR, + TUSB_REQ_TYPE_INVALID +} tusb_request_type_t; + +typedef enum { + TUSB_REQ_RCPT_DEVICE =0, + TUSB_REQ_RCPT_INTERFACE, + TUSB_REQ_RCPT_ENDPOINT, + TUSB_REQ_RCPT_OTHER +} tusb_request_recipient_t; + +// https://www.usb.org/defined-class-codes +typedef enum { + TUSB_CLASS_UNSPECIFIED = 0 , + TUSB_CLASS_AUDIO = 1 , + TUSB_CLASS_CDC = 2 , + TUSB_CLASS_HID = 3 , + TUSB_CLASS_RESERVED_4 = 4 , + TUSB_CLASS_PHYSICAL = 5 , + TUSB_CLASS_IMAGE = 6 , + TUSB_CLASS_PRINTER = 7 , + TUSB_CLASS_MSC = 8 , + TUSB_CLASS_HUB = 9 , + TUSB_CLASS_CDC_DATA = 10 , + TUSB_CLASS_SMART_CARD = 11 , + TUSB_CLASS_RESERVED_12 = 12 , + TUSB_CLASS_CONTENT_SECURITY = 13 , + TUSB_CLASS_VIDEO = 14 , + TUSB_CLASS_PERSONAL_HEALTHCARE = 15 , + TUSB_CLASS_AUDIO_VIDEO = 16 , + + TUSB_CLASS_DIAGNOSTIC = 0xDC , + TUSB_CLASS_WIRELESS_CONTROLLER = 0xE0 , + TUSB_CLASS_MISC = 0xEF , + TUSB_CLASS_APPLICATION_SPECIFIC = 0xFE , + TUSB_CLASS_VENDOR_SPECIFIC = 0xFF +} tusb_class_code_t; + +typedef enum +{ + MISC_SUBCLASS_COMMON = 2 +}misc_subclass_type_t; + +typedef enum { + MISC_PROTOCOL_IAD = 1 +} misc_protocol_type_t; + +typedef enum { + APP_SUBCLASS_USBTMC = 0x03, + APP_SUBCLASS_DFU_RUNTIME = 0x01 +} app_subclass_type_t; + +typedef enum { + DEVICE_CAPABILITY_WIRELESS_USB = 0x01, + DEVICE_CAPABILITY_USB20_EXTENSION = 0x02, + DEVICE_CAPABILITY_SUPERSPEED_USB = 0x03, + DEVICE_CAPABILITY_CONTAINER_id = 0x04, + DEVICE_CAPABILITY_PLATFORM = 0x05, + DEVICE_CAPABILITY_POWER_DELIVERY = 0x06, + DEVICE_CAPABILITY_BATTERY_INFO = 0x07, + DEVICE_CAPABILITY_PD_CONSUMER_PORT = 0x08, + DEVICE_CAPABILITY_PD_PROVIDER_PORT = 0x09, + DEVICE_CAPABILITY_SUPERSPEED_PLUS = 0x0A, + DEVICE_CAPABILITY_PRECESION_TIME_MEASUREMENT = 0x0B, + DEVICE_CAPABILITY_WIRELESS_USB_EXT = 0x0C, + DEVICE_CAPABILITY_BILLBOARD = 0x0D, + DEVICE_CAPABILITY_AUTHENTICATION = 0x0E, + DEVICE_CAPABILITY_BILLBOARD_EX = 0x0F, + DEVICE_CAPABILITY_CONFIGURATION_SUMMARY = 0x10 +} device_capability_type_t; + +enum { + TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP = 1u << 5, + TUSB_DESC_CONFIG_ATT_SELF_POWERED = 1u << 6, +}; + +#define TUSB_DESC_CONFIG_POWER_MA(x) ((x)/2) + +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ +typedef enum { + XFER_RESULT_SUCCESS = 0, + XFER_RESULT_FAILED, + XFER_RESULT_STALLED, + XFER_RESULT_TIMEOUT, + XFER_RESULT_INVALID +} xfer_result_t; + +// TODO remove +enum { + DESC_OFFSET_LEN = 0, + DESC_OFFSET_TYPE = 1 +}; + +enum { + INTERFACE_INVALID_NUMBER = 0xff +}; + +typedef enum { + MS_OS_20_SET_HEADER_DESCRIPTOR = 0x00, + MS_OS_20_SUBSET_HEADER_CONFIGURATION = 0x01, + MS_OS_20_SUBSET_HEADER_FUNCTION = 0x02, + MS_OS_20_FEATURE_COMPATBLE_ID = 0x03, + MS_OS_20_FEATURE_REG_PROPERTY = 0x04, + MS_OS_20_FEATURE_MIN_RESUME_TIME = 0x05, + MS_OS_20_FEATURE_MODEL_ID = 0x06, + MS_OS_20_FEATURE_CCGP_DEVICE = 0x07, + MS_OS_20_FEATURE_VENDOR_REVISION = 0x08 +} microsoft_os_20_type_t; + +enum { + CONTROL_STAGE_IDLE, + CONTROL_STAGE_SETUP, + CONTROL_STAGE_DATA, + CONTROL_STAGE_ACK +}; + +enum { + TUSB_INDEX_INVALID_8 = 0xFFu +}; + +//--------------------------------------------------------------------+ +// USB Descriptors +//--------------------------------------------------------------------+ + +// Start of all packed definitions for compiler without per-type packed +TU_ATTR_PACKED_BEGIN +TU_ATTR_BIT_FIELD_ORDER_BEGIN + +/// USB Device Descriptor +typedef struct TU_ATTR_PACKED { + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< DEVICE Descriptor Type. + uint16_t bcdUSB ; ///< BUSB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H). + + uint8_t bDeviceClass ; ///< Class code (assigned by the USB-IF). + uint8_t bDeviceSubClass ; ///< Subclass code (assigned by the USB-IF). + uint8_t bDeviceProtocol ; ///< Protocol code (assigned by the USB-IF). + uint8_t bMaxPacketSize0 ; ///< Maximum packet size for endpoint zero (only 8, 16, 32, or 64 are valid). For HS devices is fixed to 64. + + uint16_t idVendor ; ///< Vendor ID (assigned by the USB-IF). + uint16_t idProduct ; ///< Product ID (assigned by the manufacturer). + uint16_t bcdDevice ; ///< Device release number in binary-coded decimal. + uint8_t iManufacturer ; ///< Index of string descriptor describing manufacturer. + uint8_t iProduct ; ///< Index of string descriptor describing product. + uint8_t iSerialNumber ; ///< Index of string descriptor describing the device's serial number. + + uint8_t bNumConfigurations ; ///< Number of possible configurations. +} tusb_desc_device_t; + +TU_VERIFY_STATIC( sizeof(tusb_desc_device_t) == 18, "size is not correct"); + +// USB Binary Device Object Store (BOS) Descriptor +typedef struct TU_ATTR_PACKED { + uint8_t bLength ; ///< Size of this descriptor in bytes + uint8_t bDescriptorType ; ///< CONFIGURATION Descriptor Type + uint16_t wTotalLength ; ///< Total length of data returned for this descriptor + uint8_t bNumDeviceCaps ; ///< Number of device capability descriptors in the BOS +} tusb_desc_bos_t; + +TU_VERIFY_STATIC( sizeof(tusb_desc_bos_t) == 5, "size is not correct"); + +/// USB Configuration Descriptor +typedef struct TU_ATTR_PACKED { + uint8_t bLength ; ///< Size of this descriptor in bytes + uint8_t bDescriptorType ; ///< CONFIGURATION Descriptor Type + uint16_t wTotalLength ; ///< Total length of data returned for this configuration. Includes the combined length of all descriptors (configuration, interface, endpoint, and class- or vendor-specific) returned for this configuration. + + uint8_t bNumInterfaces ; ///< Number of interfaces supported by this configuration + uint8_t bConfigurationValue ; ///< Value to use as an argument to the SetConfiguration() request to select this configuration. + uint8_t iConfiguration ; ///< Index of string descriptor describing this configuration + uint8_t bmAttributes ; ///< Configuration characteristics \n D7: Reserved (set to one)\n D6: Self-powered \n D5: Remote Wakeup \n D4...0: Reserved (reset to zero) \n D7 is reserved and must be set to one for historical reasons. \n A device configuration that uses power from the bus and a local source reports a non-zero value in bMaxPower to indicate the amount of bus power required and sets D6. The actual power source at runtime may be determined using the GetStatus(DEVICE) request (see USB 2.0 spec Section 9.4.5). \n If a device configuration supports remote wakeup, D5 is set to one. + uint8_t bMaxPower ; ///< Maximum power consumption of the USB device from the bus in this specific configuration when the device is fully operational. Expressed in 2 mA units (i.e., 50 = 100 mA). +} tusb_desc_configuration_t; + +TU_VERIFY_STATIC( sizeof(tusb_desc_configuration_t) == 9, "size is not correct"); + +/// USB Interface Descriptor +typedef struct TU_ATTR_PACKED { + uint8_t bLength ; ///< Size of this descriptor in bytes + uint8_t bDescriptorType ; ///< INTERFACE Descriptor Type + + uint8_t bInterfaceNumber ; ///< Number of this interface. Zero-based value identifying the index in the array of concurrent interfaces supported by this configuration. + uint8_t bAlternateSetting ; ///< Value used to select this alternate setting for the interface identified in the prior field + uint8_t bNumEndpoints ; ///< Number of endpoints used by this interface (excluding endpoint zero). If this value is zero, this interface only uses the Default Control Pipe. + uint8_t bInterfaceClass ; ///< Class code (assigned by the USB-IF). \li A value of zero is reserved for future standardization. \li If this field is set to FFH, the interface class is vendor-specific. \li All other values are reserved for assignment by the USB-IF. + uint8_t bInterfaceSubClass ; ///< Subclass code (assigned by the USB-IF). \n These codes are qualified by the value of the bInterfaceClass field. \li If the bInterfaceClass field is reset to zero, this field must also be reset to zero. \li If the bInterfaceClass field is not set to FFH, all values are reserved for assignment by the USB-IF. + uint8_t bInterfaceProtocol ; ///< Protocol code (assigned by the USB). \n These codes are qualified by the value of the bInterfaceClass and the bInterfaceSubClass fields. If an interface supports class-specific requests, this code identifies the protocols that the device uses as defined by the specification of the device class. \li If this field is reset to zero, the device does not use a class-specific protocol on this interface. \li If this field is set to FFH, the device uses a vendor-specific protocol for this interface. + uint8_t iInterface ; ///< Index of string descriptor describing this interface +} tusb_desc_interface_t; + +TU_VERIFY_STATIC( sizeof(tusb_desc_interface_t) == 9, "size is not correct"); + +/// USB Endpoint Descriptor +typedef struct TU_ATTR_PACKED { + uint8_t bLength ; // Size of this descriptor in bytes + uint8_t bDescriptorType ; // ENDPOINT Descriptor Type + + uint8_t bEndpointAddress ; // The address of the endpoint + + struct TU_ATTR_PACKED { + uint8_t xfer : 2; // Control, ISO, Bulk, Interrupt + uint8_t sync : 2; // None, Asynchronous, Adaptive, Synchronous + uint8_t usage : 2; // Data, Feedback, Implicit feedback + uint8_t : 2; + } bmAttributes; + + uint16_t wMaxPacketSize ; // Bit 10..0 : max packet size, bit 12..11 additional transaction per highspeed micro-frame + uint8_t bInterval ; // Polling interval, in frames or microframes depending on the operating speed +} tusb_desc_endpoint_t; + +TU_VERIFY_STATIC( sizeof(tusb_desc_endpoint_t) == 7, "size is not correct"); + +/// USB Other Speed Configuration Descriptor +typedef struct TU_ATTR_PACKED { + uint8_t bLength ; ///< Size of descriptor + uint8_t bDescriptorType ; ///< Other_speed_Configuration Type + uint16_t wTotalLength ; ///< Total length of data returned + + uint8_t bNumInterfaces ; ///< Number of interfaces supported by this speed configuration + uint8_t bConfigurationValue ; ///< Value to use to select configuration + uint8_t iConfiguration ; ///< Index of string descriptor + uint8_t bmAttributes ; ///< Same as Configuration descriptor + uint8_t bMaxPower ; ///< Same as Configuration descriptor +} tusb_desc_other_speed_t; + +/// USB Device Qualifier Descriptor +typedef struct TU_ATTR_PACKED { + uint8_t bLength ; ///< Size of descriptor + uint8_t bDescriptorType ; ///< Device Qualifier Type + uint16_t bcdUSB ; ///< USB specification version number (e.g., 0200H for V2.00) + + uint8_t bDeviceClass ; ///< Class Code + uint8_t bDeviceSubClass ; ///< SubClass Code + uint8_t bDeviceProtocol ; ///< Protocol Code + + uint8_t bMaxPacketSize0 ; ///< Maximum packet size for other speed + uint8_t bNumConfigurations ; ///< Number of Other-speed Configurations + uint8_t bReserved ; ///< Reserved for future use, must be zero +} tusb_desc_device_qualifier_t; + +TU_VERIFY_STATIC( sizeof(tusb_desc_device_qualifier_t) == 10, "size is not correct"); + +/// USB Interface Association Descriptor (IAD ECN) +typedef struct TU_ATTR_PACKED { + uint8_t bLength ; ///< Size of descriptor + uint8_t bDescriptorType ; ///< Other_speed_Configuration Type + + uint8_t bFirstInterface ; ///< Index of the first associated interface. + uint8_t bInterfaceCount ; ///< Total number of associated interfaces. + + uint8_t bFunctionClass ; ///< Interface class ID. + uint8_t bFunctionSubClass ; ///< Interface subclass ID. + uint8_t bFunctionProtocol ; ///< Interface protocol ID. + + uint8_t iFunction ; ///< Index of the string descriptor describing the interface association. +} tusb_desc_interface_assoc_t; + +TU_VERIFY_STATIC( sizeof(tusb_desc_interface_assoc_t) == 8, "size is not correct"); + +// USB String Descriptor +typedef struct TU_ATTR_PACKED { + uint8_t bLength ; ///< Size of this descriptor in bytes + uint8_t bDescriptorType ; ///< Descriptor Type + uint16_t unicode_string[]; +} tusb_desc_string_t; + +// USB Binary Device Object Store (BOS) +typedef struct TU_ATTR_PACKED { + uint8_t bLength; + uint8_t bDescriptorType ; + uint8_t bDevCapabilityType; + uint8_t bReserved; + uint8_t PlatformCapabilityUUID[16]; + uint8_t CapabilityData[]; +} tusb_desc_bos_platform_t; + +// USB WebUSB URL Descriptor +typedef struct TU_ATTR_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bScheme; + char url[]; +} tusb_desc_webusb_url_t; + +// DFU Functional Descriptor +typedef struct TU_ATTR_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + + union { + struct TU_ATTR_PACKED { + uint8_t bitCanDnload : 1; + uint8_t bitCanUpload : 1; + uint8_t bitManifestationTolerant : 1; + uint8_t bitWillDetach : 1; + uint8_t reserved : 4; + } bmAttributes; + + uint8_t bAttributes; + }; + + uint16_t wDetachTimeOut; + uint16_t wTransferSize; + uint16_t bcdDFUVersion; +} tusb_desc_dfu_functional_t; + +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ + +typedef struct TU_ATTR_PACKED { + union { + struct TU_ATTR_PACKED { + uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t. + uint8_t type : 2; ///< Request type tusb_request_type_t. + uint8_t direction : 1; ///< Direction type. tusb_dir_t + } bmRequestType_bit; + + uint8_t bmRequestType; + }; + + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} tusb_control_request_t; + +TU_VERIFY_STATIC( sizeof(tusb_control_request_t) == 8, "size is not correct"); + +TU_ATTR_PACKED_END // End of all packed definitions +TU_ATTR_BIT_FIELD_ORDER_END + +//--------------------------------------------------------------------+ +// Endpoint helper +//--------------------------------------------------------------------+ + +// Get direction from Endpoint address +TU_ATTR_ALWAYS_INLINE static inline tusb_dir_t tu_edpt_dir(uint8_t addr) { + return (addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT; +} + +// Get Endpoint number from address +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_number(uint8_t addr) { + return (uint8_t)(addr & (~TUSB_DIR_IN_MASK)); +} + +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_addr(uint8_t num, uint8_t dir) { + return (uint8_t)(num | (dir ? TUSB_DIR_IN_MASK : 0)); +} + +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_edpt_packet_size(tusb_desc_endpoint_t const* desc_ep) { + return tu_le16toh(desc_ep->wMaxPacketSize) & 0x7FF; +} + +#if CFG_TUSB_DEBUG +TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_type_str(tusb_xfer_type_t t) { + tu_static const char *str[] = {"control", "isochronous", "bulk", "interrupt"}; + return str[t]; +} +#endif + +//--------------------------------------------------------------------+ +// Descriptor helper +//--------------------------------------------------------------------+ + +// return next descriptor +TU_ATTR_ALWAYS_INLINE static inline uint8_t const * tu_desc_next(void const* desc) { + uint8_t const* desc8 = (uint8_t const*) desc; + return desc8 + desc8[DESC_OFFSET_LEN]; +} + +// get descriptor type +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_type(void const* desc) { + return ((uint8_t const*) desc)[DESC_OFFSET_TYPE]; +} + +// get descriptor length +TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_len(void const* desc) { + return ((uint8_t const*) desc)[DESC_OFFSET_LEN]; +} + +// find descriptor that match byte1 (type) +uint8_t const * tu_desc_find(uint8_t const* desc, uint8_t const* end, uint8_t byte1); + +// find descriptor that match byte1 (type) and byte2 +uint8_t const * tu_desc_find2(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2); + +// find descriptor that match byte1 (type) and byte2 +uint8_t const * tu_desc_find3(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2, uint8_t byte3); + +#ifdef __cplusplus + } +#endif + +#endif // TUSB_TYPES_H_ diff --git a/test-devices/loopback-stm32/lib/tinyusb/common/tusb_verify.h b/test-devices/loopback-stm32/lib/tinyusb/common/tusb_verify.h new file mode 100644 index 00000000..0a9549c9 --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/common/tusb_verify.h @@ -0,0 +1,137 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ +#ifndef TUSB_VERIFY_H_ +#define TUSB_VERIFY_H_ + +#include +#include +#include "tusb_option.h" +#include "tusb_compiler.h" + +/*------------------------------------------------------------------*/ +/* This file use an advanced macro technique to mimic the default parameter + * as C++ for the sake of code simplicity. Beware of a headache macro + * manipulation that you are told to stay away. + * + * This contains macros for both VERIFY and ASSERT: + * + * VERIFY: Used when there is an error condition which is not the + * fault of the MCU. For example, bounds checking on data + * sent to the micro over USB should use this function. + * Another example is checking for buffer overflows, where + * returning from the active function causes a NAK. + * + * ASSERT: Used for error conditions that are caused by MCU firmware + * bugs. This is used to discover bugs in the code more + * quickly. One example would be adding assertions in library + * function calls to confirm a function's (untainted) + * parameters are valid. + * + * The difference in behavior is that ASSERT triggers a breakpoint while + * verify does not. + * + * #define TU_VERIFY(cond) if(cond) return false; + * #define TU_VERIFY(cond,ret) if(cond) return ret; + * + * #define TU_ASSERT(cond) if(cond) {_MESS_FAILED(); TU_BREAKPOINT(), return false;} + * #define TU_ASSERT(cond,ret) if(cond) {_MESS_FAILED(); TU_BREAKPOINT(), return ret;} + *------------------------------------------------------------------*/ + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// TU_VERIFY Helper +//--------------------------------------------------------------------+ + +#if CFG_TUSB_DEBUG + #include + #define _MESS_FAILED() tu_printf("%s %d: ASSERT FAILED\r\n", __func__, __LINE__) +#else + #define _MESS_FAILED() do {} while (0) +#endif + +// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7, M33. M55 +#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__) || defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8_1M_MAIN__) || \ + defined(__ARM7M__) || defined (__ARM7EM__) || defined(__ARM8M_MAINLINE__) || defined(__ARM8EM_MAINLINE__) + #define TU_BREAKPOINT() do \ + { \ + volatile uint32_t* ARM_CM_DHCSR = ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ \ + if ( (*ARM_CM_DHCSR) & 1UL ) __asm("BKPT #0\n"); /* Only halt mcu if debugger is attached */ \ + } while(0) + +#elif defined(__riscv) && !TUP_MCU_ESPRESSIF + #define TU_BREAKPOINT() do { __asm("ebreak\n"); } while(0) + +#elif defined(_mips) + #define TU_BREAKPOINT() do { __asm("sdbbp 0"); } while (0) + +#else + #define TU_BREAKPOINT() do {} while (0) +#endif + +// Helper to implement optional parameter for TU_VERIFY Macro family +#define _GET_3RD_ARG(arg1, arg2, arg3, ...) arg3 + +/*------------------------------------------------------------------*/ +/* TU_VERIFY + * - TU_VERIFY_1ARGS : return false if failed + * - TU_VERIFY_2ARGS : return provided value if failed + *------------------------------------------------------------------*/ +#define TU_VERIFY_DEFINE(_cond, _ret) \ + do { \ + if ( !(_cond) ) { return _ret; } \ + } while(0) + +#define TU_VERIFY_1ARGS(_cond) TU_VERIFY_DEFINE(_cond, false) +#define TU_VERIFY_2ARGS(_cond, _ret) TU_VERIFY_DEFINE(_cond, _ret) + +#define TU_VERIFY(...) _GET_3RD_ARG(__VA_ARGS__, TU_VERIFY_2ARGS, TU_VERIFY_1ARGS, _dummy)(__VA_ARGS__) + +/*------------------------------------------------------------------*/ +/* ASSERT + * basically TU_VERIFY with TU_BREAKPOINT() as handler + * - 1 arg : return false if failed + * - 2 arg : return error if failed + *------------------------------------------------------------------*/ +#define TU_ASSERT_DEFINE(_cond, _ret) \ + do { \ + if ( !(_cond) ) { _MESS_FAILED(); TU_BREAKPOINT(); return _ret; } \ + } while(0) + +#define TU_ASSERT_1ARGS(_cond) TU_ASSERT_DEFINE(_cond, false) +#define TU_ASSERT_2ARGS(_cond, _ret) TU_ASSERT_DEFINE(_cond, _ret) + +#ifndef TU_ASSERT +#define TU_ASSERT(...) _GET_3RD_ARG(__VA_ARGS__, TU_ASSERT_2ARGS, TU_ASSERT_1ARGS, _dummy)(__VA_ARGS__) +#endif + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/test-devices/loopback-stm32/lib/tinyusb/device/dcd.h b/test-devices/loopback-stm32/lib/tinyusb/device/dcd.h new file mode 100644 index 00000000..d4f105aa --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/device/dcd.h @@ -0,0 +1,242 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_DCD_H_ +#define _TUSB_DCD_H_ + +#include "common/tusb_common.h" +#include "osal/osal.h" +#include "common/tusb_fifo.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Configuration +//--------------------------------------------------------------------+ + +#ifndef CFG_TUD_ENDPPOINT_MAX + #define CFG_TUD_ENDPPOINT_MAX TUP_DCD_ENDPOINT_MAX +#endif + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF PROTYPES +//--------------------------------------------------------------------+ + +typedef enum { + DCD_EVENT_INVALID = 0, + DCD_EVENT_BUS_RESET, + DCD_EVENT_UNPLUGGED, + DCD_EVENT_SOF, + DCD_EVENT_SUSPEND, // TODO LPM Sleep L1 support + DCD_EVENT_RESUME, + + DCD_EVENT_SETUP_RECEIVED, + DCD_EVENT_XFER_COMPLETE, + + // Not an DCD event, just a convenient way to defer ISR function + USBD_EVENT_FUNC_CALL, + + DCD_EVENT_COUNT +} dcd_eventid_t; + +typedef struct TU_ATTR_ALIGNED(4) { + uint8_t rhport; + uint8_t event_id; + + union { + // BUS RESET + struct { + tusb_speed_t speed; + } bus_reset; + + // SOF + struct { + uint32_t frame_count; + }sof; + + // SETUP_RECEIVED + tusb_control_request_t setup_received; + + // XFER_COMPLETE + struct { + uint8_t ep_addr; + uint8_t result; + uint32_t len; + }xfer_complete; + + // FUNC_CALL + struct { + void (*func) (void*); + void* param; + }func_call; + }; +} dcd_event_t; + +//TU_VERIFY_STATIC(sizeof(dcd_event_t) <= 12, "size is not correct"); + +//--------------------------------------------------------------------+ +// Memory API +//--------------------------------------------------------------------+ + +// clean/flush data cache: write cache -> memory. +// Required before an DMA TX transfer to make sure data is in memory +void dcd_dcache_clean(void const* addr, uint32_t data_size) TU_ATTR_WEAK; + +// invalidate data cache: mark cache as invalid, next read will read from memory +// Required BOTH before and after an DMA RX transfer +void dcd_dcache_invalidate(void const* addr, uint32_t data_size) TU_ATTR_WEAK; + +// clean and invalidate data cache +// Required before an DMA transfer where memory is both read/write by DMA +void dcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) TU_ATTR_WEAK; + +//--------------------------------------------------------------------+ +// Controller API +//--------------------------------------------------------------------+ + +// Initialize controller to device mode +void dcd_init(uint8_t rhport); + +// Deinitialize controller, unset device mode. +bool dcd_deinit(uint8_t rhport); + +// Interrupt Handler +void dcd_int_handler(uint8_t rhport); + +// Enable device interrupt +void dcd_int_enable (uint8_t rhport); + +// Disable device interrupt +void dcd_int_disable(uint8_t rhport); + +// Receive Set Address request, mcu port must also include status IN response +void dcd_set_address(uint8_t rhport, uint8_t dev_addr); + +// Wake up host +void dcd_remote_wakeup(uint8_t rhport); + +// Connect by enabling internal pull-up resistor on D+/D- +void dcd_connect(uint8_t rhport) TU_ATTR_WEAK; + +// Disconnect by disabling internal pull-up resistor on D+/D- +void dcd_disconnect(uint8_t rhport) TU_ATTR_WEAK; + +// Enable/Disable Start-of-frame interrupt. Default is disabled +void dcd_sof_enable(uint8_t rhport, bool en); + +//--------------------------------------------------------------------+ +// Endpoint API +//--------------------------------------------------------------------+ + +// Invoked when a control transfer's status stage is complete. +// May help DCD to prepare for next control transfer, this API is optional. +void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request); + +// Configure endpoint's registers according to descriptor +bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_ep); + +// Close all non-control endpoints, cancel all pending transfers if any. +// Invoked when switching from a non-zero Configuration by SET_CONFIGURE therefore +// required for multiple configuration support. +void dcd_edpt_close_all (uint8_t rhport); + +// Close an endpoint. +// Since it is weak, caller must TU_ASSERT this function's existence before calling it. +void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr) TU_ATTR_WEAK; + +// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack +bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes); + +// Submit an transfer using fifo, When complete dcd_event_xfer_complete() is invoked to notify the stack +// This API is optional, may be useful for register-based for transferring data. +bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) TU_ATTR_WEAK; + +// Stall endpoint, any queuing transfer should be removed from endpoint +void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr); + +// clear stall, data toggle is also reset to DATA0 +// This API never calls with control endpoints, since it is auto cleared when receiving setup packet +void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr); + +// Allocate packet buffer used by ISO endpoints +// Some MCU need manual packet buffer allocation, we allocate the largest size to avoid clustering +TU_ATTR_WEAK bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size); + +// Configure and enable an ISO endpoint according to descriptor +TU_ATTR_WEAK bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc); + +//--------------------------------------------------------------------+ +// Event API (implemented by stack) +//--------------------------------------------------------------------+ + +// Called by DCD to notify device stack +extern void dcd_event_handler(dcd_event_t const * event, bool in_isr); + +// helper to send bus signal event +TU_ATTR_ALWAYS_INLINE static inline void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr) { + dcd_event_t event = { .rhport = rhport, .event_id = eid }; + dcd_event_handler(&event, in_isr); +} + +// helper to send bus reset event +TU_ATTR_ALWAYS_INLINE static inline void dcd_event_bus_reset (uint8_t rhport, tusb_speed_t speed, bool in_isr) { + dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_BUS_RESET }; + event.bus_reset.speed = speed; + dcd_event_handler(&event, in_isr); +} + +// helper to send setup received +TU_ATTR_ALWAYS_INLINE static inline void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr) { + dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SETUP_RECEIVED }; + memcpy(&event.setup_received, setup, sizeof(tusb_control_request_t)); + + dcd_event_handler(&event, in_isr); +} + +// helper to send transfer complete event +TU_ATTR_ALWAYS_INLINE static inline void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr) { + dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_XFER_COMPLETE }; + + event.xfer_complete.ep_addr = ep_addr; + event.xfer_complete.len = xferred_bytes; + event.xfer_complete.result = result; + + dcd_event_handler(&event, in_isr); +} + +TU_ATTR_ALWAYS_INLINE static inline void dcd_event_sof(uint8_t rhport, uint32_t frame_count, bool in_isr) { + dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SOF }; + event.sof.frame_count = frame_count; + dcd_event_handler(&event, in_isr); +} + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_DCD_H_ */ diff --git a/test-devices/loopback-stm32/lib/tinyusb/device/usbd.c b/test-devices/loopback-stm32/lib/tinyusb/device/usbd.c new file mode 100644 index 00000000..e51aa0fc --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/device/usbd.c @@ -0,0 +1,1391 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if CFG_TUD_ENABLED + +#include "device/dcd.h" +#include "tusb.h" +#include "common/tusb_private.h" + +#include "device/usbd.h" +#include "device/usbd_pvt.h" + +//--------------------------------------------------------------------+ +// USBD Configuration +//--------------------------------------------------------------------+ +#ifndef CFG_TUD_TASK_QUEUE_SZ + #define CFG_TUD_TASK_QUEUE_SZ 16 +#endif + +//--------------------------------------------------------------------+ +// Weak stubs: invoked if no strong implementation is available +//--------------------------------------------------------------------+ +TU_ATTR_WEAK bool dcd_deinit(uint8_t rhport) { + (void) rhport; + return false; +} + +TU_ATTR_WEAK void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr) { + (void)rhport; + (void)eventid; + (void)in_isr; +} + +//--------------------------------------------------------------------+ +// Device Data +//--------------------------------------------------------------------+ + +// Invalid driver ID in itf2drv[] ep2drv[][] mapping +enum { DRVID_INVALID = 0xFFu }; + +typedef struct { + struct TU_ATTR_PACKED { + volatile uint8_t connected : 1; + volatile uint8_t addressed : 1; + volatile uint8_t suspended : 1; + + uint8_t remote_wakeup_en : 1; // enable/disable by host + uint8_t remote_wakeup_support : 1; // configuration descriptor's attribute + uint8_t self_powered : 1; // configuration descriptor's attribute + }; + volatile uint8_t cfg_num; // current active configuration (0x00 is not configured) + uint8_t speed; + volatile uint8_t setup_count; + + uint8_t itf2drv[CFG_TUD_INTERFACE_MAX]; // map interface number to driver (0xff is invalid) + uint8_t ep2drv[CFG_TUD_ENDPPOINT_MAX][2]; // map endpoint to driver ( 0xff is invalid ), can use only 4-bit each + + tu_edpt_state_t ep_status[CFG_TUD_ENDPPOINT_MAX][2]; + +}usbd_device_t; + +tu_static usbd_device_t _usbd_dev; + +//--------------------------------------------------------------------+ +// Class Driver +//--------------------------------------------------------------------+ +#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL + #define DRIVER_NAME(_name) .name = _name, +#else + #define DRIVER_NAME(_name) +#endif + +// Built-in class drivers +tu_static usbd_class_driver_t const _usbd_driver[] = { + #if CFG_TUD_CDC + { + DRIVER_NAME("CDC") + .init = cdcd_init, + .deinit = cdcd_deinit, + .reset = cdcd_reset, + .open = cdcd_open, + .control_xfer_cb = cdcd_control_xfer_cb, + .xfer_cb = cdcd_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_MSC + { + DRIVER_NAME("MSC") + .init = mscd_init, + .deinit = NULL, + .reset = mscd_reset, + .open = mscd_open, + .control_xfer_cb = mscd_control_xfer_cb, + .xfer_cb = mscd_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_HID + { + DRIVER_NAME("HID") + .init = hidd_init, + .deinit = hidd_deinit, + .reset = hidd_reset, + .open = hidd_open, + .control_xfer_cb = hidd_control_xfer_cb, + .xfer_cb = hidd_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_AUDIO + { + DRIVER_NAME("AUDIO") + .init = audiod_init, + .deinit = audiod_deinit, + .reset = audiod_reset, + .open = audiod_open, + .control_xfer_cb = audiod_control_xfer_cb, + .xfer_cb = audiod_xfer_cb, + .sof = audiod_sof_isr + }, + #endif + + #if CFG_TUD_VIDEO + { + DRIVER_NAME("VIDEO") + .init = videod_init, + .deinit = videod_deinit, + .reset = videod_reset, + .open = videod_open, + .control_xfer_cb = videod_control_xfer_cb, + .xfer_cb = videod_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_MIDI + { + DRIVER_NAME("MIDI") + .init = midid_init, + .deinit = midid_deinit, + .open = midid_open, + .reset = midid_reset, + .control_xfer_cb = midid_control_xfer_cb, + .xfer_cb = midid_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_VENDOR + { + DRIVER_NAME("VENDOR") + .init = vendord_init, + .deinit = vendord_deinit, + .reset = vendord_reset, + .open = vendord_open, + .control_xfer_cb = tud_vendor_control_xfer_cb, + .xfer_cb = vendord_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_USBTMC + { + DRIVER_NAME("TMC") + .init = usbtmcd_init_cb, + .deinit = usbtmcd_deinit, + .reset = usbtmcd_reset_cb, + .open = usbtmcd_open_cb, + .control_xfer_cb = usbtmcd_control_xfer_cb, + .xfer_cb = usbtmcd_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_DFU_RUNTIME + { + DRIVER_NAME("DFU-RUNTIME") + .init = dfu_rtd_init, + .deinit = dfu_rtd_deinit, + .reset = dfu_rtd_reset, + .open = dfu_rtd_open, + .control_xfer_cb = dfu_rtd_control_xfer_cb, + .xfer_cb = NULL, + .sof = NULL + }, + #endif + + #if CFG_TUD_DFU + { + DRIVER_NAME("DFU") + .init = dfu_moded_init, + .deinit = dfu_moded_deinit, + .reset = dfu_moded_reset, + .open = dfu_moded_open, + .control_xfer_cb = dfu_moded_control_xfer_cb, + .xfer_cb = NULL, + .sof = NULL + }, + #endif + + #if CFG_TUD_ECM_RNDIS || CFG_TUD_NCM + { + DRIVER_NAME("NET") + .init = netd_init, + .deinit = netd_deinit, + .reset = netd_reset, + .open = netd_open, + .control_xfer_cb = netd_control_xfer_cb, + .xfer_cb = netd_xfer_cb, + .sof = NULL, + }, + #endif + + #if CFG_TUD_BTH + { + DRIVER_NAME("BTH") + .init = btd_init, + .deinit = btd_deinit, + .reset = btd_reset, + .open = btd_open, + .control_xfer_cb = btd_control_xfer_cb, + .xfer_cb = btd_xfer_cb, + .sof = NULL + }, + #endif +}; + +enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(_usbd_driver) }; + +// Additional class drivers implemented by application +tu_static usbd_class_driver_t const * _app_driver = NULL; +tu_static uint8_t _app_driver_count = 0; + +#define TOTAL_DRIVER_COUNT (_app_driver_count + BUILTIN_DRIVER_COUNT) + +// virtually joins built-in and application drivers together. +// Application is positioned first to allow overwriting built-in ones. +TU_ATTR_ALWAYS_INLINE static inline usbd_class_driver_t const * get_driver(uint8_t drvid) { + usbd_class_driver_t const * driver = NULL; + if ( drvid < _app_driver_count ) { + // Application drivers + driver = &_app_driver[drvid]; + } else if ( drvid < TOTAL_DRIVER_COUNT && BUILTIN_DRIVER_COUNT > 0 ){ + driver = &_usbd_driver[drvid - _app_driver_count]; + } + return driver; +} + +//--------------------------------------------------------------------+ +// DCD Event +//--------------------------------------------------------------------+ + +enum { RHPORT_INVALID = 0xFFu }; +tu_static uint8_t _usbd_rhport = RHPORT_INVALID; + +// Event queue +// usbd_int_set() is used as mutex in OS NONE config +OSAL_QUEUE_DEF(usbd_int_set, _usbd_qdef, CFG_TUD_TASK_QUEUE_SZ, dcd_event_t); +tu_static osal_queue_t _usbd_q; + +// Mutex for claiming endpoint +#if OSAL_MUTEX_REQUIRED + tu_static osal_mutex_def_t _ubsd_mutexdef; + tu_static osal_mutex_t _usbd_mutex; +#else + #define _usbd_mutex NULL +#endif + +TU_ATTR_ALWAYS_INLINE static inline bool queue_event(dcd_event_t const * event, bool in_isr) { + TU_ASSERT(osal_queue_send(_usbd_q, event, in_isr)); + tud_event_hook_cb(event->rhport, event->event_id, in_isr); + return true; +} + +//--------------------------------------------------------------------+ +// Prototypes +//--------------------------------------------------------------------+ +static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request); +static bool process_set_config(uint8_t rhport, uint8_t cfg_num); +static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request); + +// from usbd_control.c +void usbd_control_reset(void); +void usbd_control_set_request(tusb_control_request_t const *request); +void usbd_control_set_complete_callback( usbd_control_xfer_cb_t fp ); +bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); + + +//--------------------------------------------------------------------+ +// Debug +//--------------------------------------------------------------------+ +#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL +tu_static char const* const _usbd_event_str[DCD_EVENT_COUNT] = { + "Invalid", + "Bus Reset", + "Unplugged", + "SOF", + "Suspend", + "Resume", + "Setup Received", + "Xfer Complete", + "Func Call" +}; + +// for usbd_control to print the name of control complete driver +void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback) { + for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) { + usbd_class_driver_t const* driver = get_driver(i); + if (driver && driver->control_xfer_cb == callback) { + TU_LOG_USBD("%s control complete\r\n", driver->name); + return; + } + } +} + +#endif + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ +tusb_speed_t tud_speed_get(void) { + return (tusb_speed_t) _usbd_dev.speed; +} + +bool tud_connected(void) { + return _usbd_dev.connected; +} + +bool tud_mounted(void) { + return _usbd_dev.cfg_num ? true : false; +} + +bool tud_suspended(void) { + return _usbd_dev.suspended; +} + +bool tud_remote_wakeup(void) { + // only wake up host if this feature is supported and enabled and we are suspended + TU_VERIFY (_usbd_dev.suspended && _usbd_dev.remote_wakeup_support && _usbd_dev.remote_wakeup_en); + dcd_remote_wakeup(_usbd_rhport); + return true; +} + +bool tud_disconnect(void) { + TU_VERIFY(dcd_disconnect); + dcd_disconnect(_usbd_rhport); + return true; +} + +bool tud_connect(void) { + TU_VERIFY(dcd_connect); + dcd_connect(_usbd_rhport); + return true; +} + +//--------------------------------------------------------------------+ +// USBD Task +//--------------------------------------------------------------------+ +bool tud_inited(void) { + return _usbd_rhport != RHPORT_INVALID; +} + +bool tud_init(uint8_t rhport) { + // skip if already initialized + if (tud_inited()) return true; + + TU_LOG_USBD("USBD init on controller %u\r\n", rhport); + TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(usbd_device_t)); + TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(dcd_event_t)); + TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(tu_fifo_t)); + TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(tu_edpt_stream_t)); + + tu_varclr(&_usbd_dev); + +#if OSAL_MUTEX_REQUIRED + // Init device mutex + _usbd_mutex = osal_mutex_create(&_ubsd_mutexdef); + TU_ASSERT(_usbd_mutex); +#endif + + // Init device queue & task + _usbd_q = osal_queue_create(&_usbd_qdef); + TU_ASSERT(_usbd_q); + + // Get application driver if available + if (usbd_app_driver_get_cb) { + _app_driver = usbd_app_driver_get_cb(&_app_driver_count); + } + + // Init class drivers + for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) { + usbd_class_driver_t const* driver = get_driver(i); + TU_ASSERT(driver && driver->init); + TU_LOG_USBD("%s init\r\n", driver->name); + driver->init(); + } + + _usbd_rhport = rhport; + + // Init device controller driver + dcd_init(rhport); + dcd_int_enable(rhport); + + return true; +} + +bool tud_deinit(uint8_t rhport) { + // skip if not initialized + if (!tud_inited()) return true; + + TU_LOG_USBD("USBD deinit on controller %u\r\n", rhport); + + // Deinit device controller driver + dcd_int_disable(rhport); + dcd_disconnect(rhport); + dcd_deinit(rhport); + + // Deinit class drivers + for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) { + usbd_class_driver_t const* driver = get_driver(i); + if(driver && driver->deinit) { + TU_LOG_USBD("%s deinit\r\n", driver->name); + driver->deinit(); + } + } + + // Deinit device queue & task + osal_queue_delete(_usbd_q); + _usbd_q = NULL; + +#if OSAL_MUTEX_REQUIRED + // TODO make sure there is no task waiting on this mutex + osal_mutex_delete(_usbd_mutex); + _usbd_mutex = NULL; +#endif + + _usbd_rhport = RHPORT_INVALID; + + return true; +} + +static void configuration_reset(uint8_t rhport) { + for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) { + usbd_class_driver_t const* driver = get_driver(i); + TU_ASSERT(driver,); + driver->reset(rhport); + } + + tu_varclr(&_usbd_dev); + memset(_usbd_dev.itf2drv, DRVID_INVALID, sizeof(_usbd_dev.itf2drv)); // invalid mapping + memset(_usbd_dev.ep2drv, DRVID_INVALID, sizeof(_usbd_dev.ep2drv)); // invalid mapping +} + +static void usbd_reset(uint8_t rhport) { + configuration_reset(rhport); + usbd_control_reset(); +} + +bool tud_task_event_ready(void) { + // Skip if stack is not initialized + if (!tud_inited()) return false; + return !osal_queue_empty(_usbd_q); +} + +/* USB Device Driver task + * This top level thread manages all device controller event and delegates events to class-specific drivers. + * This should be called periodically within the mainloop or rtos thread. + * + int main(void) { + application_init(); + tusb_init(); + + while(1) { // the mainloop + application_code(); + tud_task(); // tinyusb device task + } + } + */ +void tud_task_ext(uint32_t timeout_ms, bool in_isr) { + (void) in_isr; // not implemented yet + + // Skip if stack is not initialized + if (!tud_inited()) return; + + // Loop until there is no more events in the queue + while (1) { + dcd_event_t event; + if (!osal_queue_receive(_usbd_q, &event, timeout_ms)) return; + +#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL + if (event.event_id == DCD_EVENT_SETUP_RECEIVED) TU_LOG_USBD("\r\n"); // extra line for setup + TU_LOG_USBD("USBD %s ", event.event_id < DCD_EVENT_COUNT ? _usbd_event_str[event.event_id] : "CORRUPTED"); +#endif + + switch (event.event_id) { + case DCD_EVENT_BUS_RESET: + TU_LOG_USBD(": %s Speed\r\n", tu_str_speed[event.bus_reset.speed]); + usbd_reset(event.rhport); + _usbd_dev.speed = event.bus_reset.speed; + break; + + case DCD_EVENT_UNPLUGGED: + TU_LOG_USBD("\r\n"); + usbd_reset(event.rhport); + if (tud_umount_cb) tud_umount_cb(); + break; + + case DCD_EVENT_SETUP_RECEIVED: + _usbd_dev.setup_count--; + TU_LOG_BUF(CFG_TUD_LOG_LEVEL, &event.setup_received, 8); + if (_usbd_dev.setup_count) { + TU_LOG_USBD(" Skipped since there is other SETUP in queue\r\n"); + break; + } + + // Mark as connected after receiving 1st setup packet. + // But it is easier to set it every time instead of wasting time to check then set + _usbd_dev.connected = 1; + + // mark both in & out control as free + _usbd_dev.ep_status[0][TUSB_DIR_OUT].busy = 0; + _usbd_dev.ep_status[0][TUSB_DIR_OUT].claimed = 0; + _usbd_dev.ep_status[0][TUSB_DIR_IN].busy = 0; + _usbd_dev.ep_status[0][TUSB_DIR_IN].claimed = 0; + + // Process control request + if (!process_control_request(event.rhport, &event.setup_received)) { + TU_LOG_USBD(" Stall EP0\r\n"); + // Failed -> stall both control endpoint IN and OUT + dcd_edpt_stall(event.rhport, 0); + dcd_edpt_stall(event.rhport, 0 | TUSB_DIR_IN_MASK); + } + break; + + case DCD_EVENT_XFER_COMPLETE: { + // Invoke the class callback associated with the endpoint address + uint8_t const ep_addr = event.xfer_complete.ep_addr; + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const ep_dir = tu_edpt_dir(ep_addr); + + TU_LOG_USBD("on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len); + + _usbd_dev.ep_status[epnum][ep_dir].busy = 0; + _usbd_dev.ep_status[epnum][ep_dir].claimed = 0; + + if (0 == epnum) { + usbd_control_xfer_cb(event.rhport, ep_addr, (xfer_result_t) event.xfer_complete.result, + event.xfer_complete.len); + } else { + usbd_class_driver_t const* driver = get_driver(_usbd_dev.ep2drv[epnum][ep_dir]); + TU_ASSERT(driver,); + + TU_LOG_USBD(" %s xfer callback\r\n", driver->name); + driver->xfer_cb(event.rhport, ep_addr, (xfer_result_t) event.xfer_complete.result, event.xfer_complete.len); + } + break; + } + + case DCD_EVENT_SUSPEND: + // NOTE: When plugging/unplugging device, the D+/D- state are unstable and + // can accidentally meet the SUSPEND condition ( Bus Idle for 3ms ), which result in a series of event + // e.g suspend -> resume -> unplug/plug. Skip suspend/resume if not connected + if (_usbd_dev.connected) { + TU_LOG_USBD(": Remote Wakeup = %u\r\n", _usbd_dev.remote_wakeup_en); + if (tud_suspend_cb) tud_suspend_cb(_usbd_dev.remote_wakeup_en); + } else { + TU_LOG_USBD(" Skipped\r\n"); + } + break; + + case DCD_EVENT_RESUME: + if (_usbd_dev.connected) { + TU_LOG_USBD("\r\n"); + if (tud_resume_cb) tud_resume_cb(); + } else { + TU_LOG_USBD(" Skipped\r\n"); + } + break; + + case USBD_EVENT_FUNC_CALL: + TU_LOG_USBD("\r\n"); + if (event.func_call.func) event.func_call.func(event.func_call.param); + break; + + case DCD_EVENT_SOF: + default: + TU_BREAKPOINT(); + break; + } + +#if CFG_TUSB_OS != OPT_OS_NONE && CFG_TUSB_OS != OPT_OS_PICO + // return if there is no more events, for application to run other background + if (osal_queue_empty(_usbd_q)) return; +#endif + } +} + +//--------------------------------------------------------------------+ +// Control Request Parser & Handling +//--------------------------------------------------------------------+ + +// Helper to invoke class driver control request handler +static bool invoke_class_control(uint8_t rhport, usbd_class_driver_t const * driver, tusb_control_request_t const * request) { + usbd_control_set_complete_callback(driver->control_xfer_cb); + TU_LOG_USBD(" %s control request\r\n", driver->name); + return driver->control_xfer_cb(rhport, CONTROL_STAGE_SETUP, request); +} + +// This handles the actual request and its response. +// Returns false if unable to complete the request, causing caller to stall control endpoints. +static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request) { + usbd_control_set_complete_callback(NULL); + TU_ASSERT(p_request->bmRequestType_bit.type < TUSB_REQ_TYPE_INVALID); + + // Vendor request + if ( p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR ) { + TU_VERIFY(tud_vendor_control_xfer_cb); + + usbd_control_set_complete_callback(tud_vendor_control_xfer_cb); + return tud_vendor_control_xfer_cb(rhport, CONTROL_STAGE_SETUP, p_request); + } + +#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL + if (TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type && p_request->bRequest <= TUSB_REQ_SYNCH_FRAME) { + TU_LOG_USBD(" %s", tu_str_std_request[p_request->bRequest]); + if (TUSB_REQ_GET_DESCRIPTOR != p_request->bRequest) TU_LOG_USBD("\r\n"); + } +#endif + + switch ( p_request->bmRequestType_bit.recipient ) { + //------------- Device Requests e.g in enumeration -------------// + case TUSB_REQ_RCPT_DEVICE: + if ( TUSB_REQ_TYPE_CLASS == p_request->bmRequestType_bit.type ) { + uint8_t const itf = tu_u16_low(p_request->wIndex); + TU_VERIFY(itf < TU_ARRAY_SIZE(_usbd_dev.itf2drv)); + + usbd_class_driver_t const * driver = get_driver(_usbd_dev.itf2drv[itf]); + TU_VERIFY(driver); + + // forward to class driver: "non-STD request to Interface" + return invoke_class_control(rhport, driver, p_request); + } + + if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type ) { + // Non standard request is not supported + TU_BREAKPOINT(); + return false; + } + + switch ( p_request->bRequest ) { + case TUSB_REQ_SET_ADDRESS: + // Depending on mcu, status phase could be sent either before or after changing device address, + // or even require stack to not response with status at all + // Therefore DCD must take full responsibility to response and include zlp status packet if needed. + usbd_control_set_request(p_request); // set request since DCD has no access to tud_control_status() API + dcd_set_address(rhport, (uint8_t) p_request->wValue); + // skip tud_control_status() + _usbd_dev.addressed = 1; + break; + + case TUSB_REQ_GET_CONFIGURATION: { + uint8_t cfg_num = _usbd_dev.cfg_num; + tud_control_xfer(rhport, p_request, &cfg_num, 1); + } + break; + + case TUSB_REQ_SET_CONFIGURATION: { + uint8_t const cfg_num = (uint8_t) p_request->wValue; + + // Only process if new configure is different + if (_usbd_dev.cfg_num != cfg_num) { + if ( _usbd_dev.cfg_num ) { + // already configured: need to clear all endpoints and driver first + TU_LOG_USBD(" Clear current Configuration (%u) before switching\r\n", _usbd_dev.cfg_num); + + // close all non-control endpoints, cancel all pending transfers if any + dcd_edpt_close_all(rhport); + + // close all drivers and current configured state except bus speed + uint8_t const speed = _usbd_dev.speed; + configuration_reset(rhport); + + _usbd_dev.speed = speed; // restore speed + } + + // Handle the new configuration and execute the corresponding callback + if ( cfg_num ) { + // switch to new configuration if not zero + TU_ASSERT( process_set_config(rhport, cfg_num) ); + if ( tud_mount_cb ) tud_mount_cb(); + } else { + if ( tud_umount_cb ) tud_umount_cb(); + } + } + + _usbd_dev.cfg_num = cfg_num; + tud_control_status(rhport, p_request); + } + break; + + case TUSB_REQ_GET_DESCRIPTOR: + TU_VERIFY( process_get_descriptor(rhport, p_request) ); + break; + + case TUSB_REQ_SET_FEATURE: + // Only support remote wakeup for device feature + TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue); + + TU_LOG_USBD(" Enable Remote Wakeup\r\n"); + + // Host may enable remote wake up before suspending especially HID device + _usbd_dev.remote_wakeup_en = true; + tud_control_status(rhport, p_request); + break; + + case TUSB_REQ_CLEAR_FEATURE: + // Only support remote wakeup for device feature + TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue); + + TU_LOG_USBD(" Disable Remote Wakeup\r\n"); + + // Host may disable remote wake up after resuming + _usbd_dev.remote_wakeup_en = false; + tud_control_status(rhport, p_request); + break; + + case TUSB_REQ_GET_STATUS: { + // Device status bit mask + // - Bit 0: Self Powered + // - Bit 1: Remote Wakeup enabled + uint16_t status = (uint16_t) ((_usbd_dev.self_powered ? 1u : 0u) | (_usbd_dev.remote_wakeup_en ? 2u : 0u)); + tud_control_xfer(rhport, p_request, &status, 2); + break; + } + + // Unknown/Unsupported request + default: TU_BREAKPOINT(); return false; + } + break; + + //------------- Class/Interface Specific Request -------------// + case TUSB_REQ_RCPT_INTERFACE: { + uint8_t const itf = tu_u16_low(p_request->wIndex); + TU_VERIFY(itf < TU_ARRAY_SIZE(_usbd_dev.itf2drv)); + + usbd_class_driver_t const * driver = get_driver(_usbd_dev.itf2drv[itf]); + TU_VERIFY(driver); + + // all requests to Interface (STD or Class) is forwarded to class driver. + // notable requests are: GET HID REPORT DESCRIPTOR, SET_INTERFACE, GET_INTERFACE + if ( !invoke_class_control(rhport, driver, p_request) ) { + // For GET_INTERFACE and SET_INTERFACE, it is mandatory to respond even if the class + // driver doesn't use alternate settings or implement this + TU_VERIFY(TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type); + + switch(p_request->bRequest) { + case TUSB_REQ_GET_INTERFACE: + case TUSB_REQ_SET_INTERFACE: + // Clear complete callback if driver set since it can also stall the request. + usbd_control_set_complete_callback(NULL); + + if (TUSB_REQ_GET_INTERFACE == p_request->bRequest) { + uint8_t alternate = 0; + tud_control_xfer(rhport, p_request, &alternate, 1); + }else { + tud_control_status(rhport, p_request); + } + break; + + default: return false; + } + } + break; + } + + //------------- Endpoint Request -------------// + case TUSB_REQ_RCPT_ENDPOINT: { + uint8_t const ep_addr = tu_u16_low(p_request->wIndex); + uint8_t const ep_num = tu_edpt_number(ep_addr); + uint8_t const ep_dir = tu_edpt_dir(ep_addr); + + TU_ASSERT(ep_num < TU_ARRAY_SIZE(_usbd_dev.ep2drv) ); + usbd_class_driver_t const * driver = get_driver(_usbd_dev.ep2drv[ep_num][ep_dir]); + + if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type ) { + // Forward class request to its driver + TU_VERIFY(driver); + return invoke_class_control(rhport, driver, p_request); + } else { + // Handle STD request to endpoint + switch ( p_request->bRequest ) { + case TUSB_REQ_GET_STATUS: { + uint16_t status = usbd_edpt_stalled(rhport, ep_addr) ? 0x0001 : 0x0000; + tud_control_xfer(rhport, p_request, &status, 2); + } + break; + + case TUSB_REQ_CLEAR_FEATURE: + case TUSB_REQ_SET_FEATURE: { + if ( TUSB_REQ_FEATURE_EDPT_HALT == p_request->wValue ) { + if ( TUSB_REQ_CLEAR_FEATURE == p_request->bRequest ) { + usbd_edpt_clear_stall(rhport, ep_addr); + }else { + usbd_edpt_stall(rhport, ep_addr); + } + } + + if (driver) { + // Some classes such as USBTMC needs to clear/re-init its buffer when receiving CLEAR_FEATURE request + // We will also forward std request targeted endpoint to class drivers as well + + // STD request must always be ACKed regardless of driver returned value + // Also clear complete callback if driver set since it can also stall the request. + (void) invoke_class_control(rhport, driver, p_request); + usbd_control_set_complete_callback(NULL); + + // skip ZLP status if driver already did that + if ( !_usbd_dev.ep_status[0][TUSB_DIR_IN].busy ) tud_control_status(rhport, p_request); + } + } + break; + + // Unknown/Unsupported request + default: + TU_BREAKPOINT(); + return false; + } + } + } + break; + + // Unknown recipient + default: + TU_BREAKPOINT(); + return false; + } + + return true; +} + +// Process Set Configure Request +// This function parse configuration descriptor & open drivers accordingly +static bool process_set_config(uint8_t rhport, uint8_t cfg_num) +{ + // index is cfg_num-1 + tusb_desc_configuration_t const * desc_cfg = (tusb_desc_configuration_t const *) tud_descriptor_configuration_cb(cfg_num-1); + TU_ASSERT(desc_cfg != NULL && desc_cfg->bDescriptorType == TUSB_DESC_CONFIGURATION); + + // Parse configuration descriptor + _usbd_dev.remote_wakeup_support = (desc_cfg->bmAttributes & TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP) ? 1u : 0u; + _usbd_dev.self_powered = (desc_cfg->bmAttributes & TUSB_DESC_CONFIG_ATT_SELF_POWERED ) ? 1u : 0u; + + // Parse interface descriptor + uint8_t const * p_desc = ((uint8_t const*) desc_cfg) + sizeof(tusb_desc_configuration_t); + uint8_t const * desc_end = ((uint8_t const*) desc_cfg) + tu_le16toh(desc_cfg->wTotalLength); + + while( p_desc < desc_end ) + { + uint8_t assoc_itf_count = 1; + + // Class will always starts with Interface Association (if any) and then Interface descriptor + if ( TUSB_DESC_INTERFACE_ASSOCIATION == tu_desc_type(p_desc) ) + { + tusb_desc_interface_assoc_t const * desc_iad = (tusb_desc_interface_assoc_t const *) p_desc; + assoc_itf_count = desc_iad->bInterfaceCount; + + p_desc = tu_desc_next(p_desc); // next to Interface + + // IAD's first interface number and class should match with opened interface + //TU_ASSERT(desc_iad->bFirstInterface == desc_itf->bInterfaceNumber && + // desc_iad->bFunctionClass == desc_itf->bInterfaceClass); + } + + TU_ASSERT( TUSB_DESC_INTERFACE == tu_desc_type(p_desc) ); + tusb_desc_interface_t const * desc_itf = (tusb_desc_interface_t const*) p_desc; + + // Find driver for this interface + uint16_t const remaining_len = (uint16_t) (desc_end-p_desc); + uint8_t drv_id; + for (drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++) + { + usbd_class_driver_t const *driver = get_driver(drv_id); + TU_ASSERT(driver); + uint16_t const drv_len = driver->open(rhport, desc_itf, remaining_len); + + if ( (sizeof(tusb_desc_interface_t) <= drv_len) && (drv_len <= remaining_len) ) + { + // Open successfully + TU_LOG_USBD(" %s opened\r\n", driver->name); + + // Some drivers use 2 or more interfaces but may not have IAD e.g MIDI (always) or + // BTH (even CDC) with class in device descriptor (single interface) + if ( assoc_itf_count == 1) + { + #if CFG_TUD_CDC + if ( driver->open == cdcd_open ) assoc_itf_count = 2; + #endif + + #if CFG_TUD_MIDI + if ( driver->open == midid_open ) assoc_itf_count = 2; + #endif + + #if CFG_TUD_BTH && CFG_TUD_BTH_ISO_ALT_COUNT + if ( driver->open == btd_open ) assoc_itf_count = 2; + #endif + } + + // bind (associated) interfaces to found driver + for(uint8_t i=0; ibInterfaceNumber+i; + + // Interface number must not be used already + TU_ASSERT(DRVID_INVALID == _usbd_dev.itf2drv[itf_num]); + _usbd_dev.itf2drv[itf_num] = drv_id; + } + + // bind all endpoints to found driver + tu_edpt_bind_driver(_usbd_dev.ep2drv, desc_itf, drv_len, drv_id); + + // next Interface + p_desc += drv_len; + + break; // exit driver find loop + } + } + + // Failed if there is no supported drivers + TU_ASSERT(drv_id < TOTAL_DRIVER_COUNT); + } + + return true; +} + +// return descriptor's buffer and update desc_len +static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request) +{ + tusb_desc_type_t const desc_type = (tusb_desc_type_t) tu_u16_high(p_request->wValue); + uint8_t const desc_index = tu_u16_low( p_request->wValue ); + + switch(desc_type) + { + case TUSB_DESC_DEVICE: + { + TU_LOG_USBD(" Device\r\n"); + + void* desc_device = (void*) (uintptr_t) tud_descriptor_device_cb(); + + // Only response with exactly 1 Packet if: not addressed and host requested more data than device descriptor has. + // This only happens with the very first get device descriptor and EP0 size = 8 or 16. + if ((CFG_TUD_ENDPOINT0_SIZE < sizeof(tusb_desc_device_t)) && !_usbd_dev.addressed && + ((tusb_control_request_t const*) p_request)->wLength > sizeof(tusb_desc_device_t)) + { + // Hack here: we modify the request length to prevent usbd_control response with zlp + // since we are responding with 1 packet & less data than wLength. + tusb_control_request_t mod_request = *p_request; + mod_request.wLength = CFG_TUD_ENDPOINT0_SIZE; + + return tud_control_xfer(rhport, &mod_request, desc_device, CFG_TUD_ENDPOINT0_SIZE); + }else + { + return tud_control_xfer(rhport, p_request, desc_device, sizeof(tusb_desc_device_t)); + } + } + // break; // unreachable + + case TUSB_DESC_BOS: + { + TU_LOG_USBD(" BOS\r\n"); + + // requested by host if USB > 2.0 ( i.e 2.1 or 3.x ) + if (!tud_descriptor_bos_cb) return false; + + uintptr_t desc_bos = (uintptr_t) tud_descriptor_bos_cb(); + TU_ASSERT(desc_bos); + + // Use offsetof to avoid pointer to the odd/misaligned address + uint16_t const total_len = tu_le16toh( tu_unaligned_read16((const void*) (desc_bos + offsetof(tusb_desc_bos_t, wTotalLength))) ); + + return tud_control_xfer(rhport, p_request, (void*) desc_bos, total_len); + } + // break; // unreachable + + case TUSB_DESC_CONFIGURATION: + case TUSB_DESC_OTHER_SPEED_CONFIG: + { + uintptr_t desc_config; + + if ( desc_type == TUSB_DESC_CONFIGURATION ) + { + TU_LOG_USBD(" Configuration[%u]\r\n", desc_index); + desc_config = (uintptr_t) tud_descriptor_configuration_cb(desc_index); + }else + { + // Host only request this after getting Device Qualifier descriptor + TU_LOG_USBD(" Other Speed Configuration\r\n"); + TU_VERIFY( tud_descriptor_other_speed_configuration_cb ); + desc_config = (uintptr_t) tud_descriptor_other_speed_configuration_cb(desc_index); + } + + TU_ASSERT(desc_config); + + // Use offsetof to avoid pointer to the odd/misaligned address + uint16_t const total_len = tu_le16toh( tu_unaligned_read16((const void*) (desc_config + offsetof(tusb_desc_configuration_t, wTotalLength))) ); + + return tud_control_xfer(rhport, p_request, (void*) desc_config, total_len); + } + // break; // unreachable + + case TUSB_DESC_STRING: + { + TU_LOG_USBD(" String[%u]\r\n", desc_index); + + // String Descriptor always uses the desc set from user + uint8_t const* desc_str = (uint8_t const*) tud_descriptor_string_cb(desc_index, tu_le16toh(p_request->wIndex)); + TU_VERIFY(desc_str); + + // first byte of descriptor is its size + return tud_control_xfer(rhport, p_request, (void*) (uintptr_t) desc_str, tu_desc_len(desc_str)); + } + // break; // unreachable + + case TUSB_DESC_DEVICE_QUALIFIER: + { + TU_LOG_USBD(" Device Qualifier\r\n"); + + TU_VERIFY( tud_descriptor_device_qualifier_cb ); + + uint8_t const* desc_qualifier = tud_descriptor_device_qualifier_cb(); + TU_VERIFY(desc_qualifier); + + // first byte of descriptor is its size + return tud_control_xfer(rhport, p_request, (void*) (uintptr_t) desc_qualifier, tu_desc_len(desc_qualifier)); + } + // break; // unreachable + + default: return false; + } +} + +//--------------------------------------------------------------------+ +// DCD Event Handler +//--------------------------------------------------------------------+ +TU_ATTR_FAST_FUNC void dcd_event_handler(dcd_event_t const* event, bool in_isr) { + bool send = false; + switch (event->event_id) { + case DCD_EVENT_UNPLUGGED: + _usbd_dev.connected = 0; + _usbd_dev.addressed = 0; + _usbd_dev.cfg_num = 0; + _usbd_dev.suspended = 0; + send = true; + break; + + case DCD_EVENT_SUSPEND: + // NOTE: When plugging/unplugging device, the D+/D- state are unstable and + // can accidentally meet the SUSPEND condition ( Bus Idle for 3ms ). + // In addition, some MCUs such as SAMD or boards that haven no VBUS detection cannot distinguish + // suspended vs disconnected. We will skip handling SUSPEND/RESUME event if not currently connected + if (_usbd_dev.connected) { + _usbd_dev.suspended = 1; + send = true; + } + break; + + case DCD_EVENT_RESUME: + // skip event if not connected (especially required for SAMD) + if (_usbd_dev.connected) { + _usbd_dev.suspended = 0; + send = true; + } + break; + + case DCD_EVENT_SOF: + // Some MCUs after running dcd_remote_wakeup() does not have way to detect the end of remote wakeup + // which last 1-15 ms. DCD can use SOF as a clear indicator that bus is back to operational + if (_usbd_dev.suspended) { + _usbd_dev.suspended = 0; + + dcd_event_t const event_resume = {.rhport = event->rhport, .event_id = DCD_EVENT_RESUME}; + queue_event(&event_resume, in_isr); + } + + // SOF driver handler in ISR context + for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) { + usbd_class_driver_t const* driver = get_driver(i); + if (driver && driver->sof) { + driver->sof(event->rhport, event->sof.frame_count); + } + } + + // skip osal queue for SOF in usbd task + break; + + case DCD_EVENT_SETUP_RECEIVED: + _usbd_dev.setup_count++; + send = true; + break; + + default: + send = true; + break; + } + + if (send) { + queue_event(event, in_isr); + } +} + +//--------------------------------------------------------------------+ +// USBD API For Class Driver +//--------------------------------------------------------------------+ + +void usbd_int_set(bool enabled) +{ + if (enabled) + { + dcd_int_enable(_usbd_rhport); + }else + { + dcd_int_disable(_usbd_rhport); + } +} + +// Parse consecutive endpoint descriptors (IN & OUT) +bool usbd_open_edpt_pair(uint8_t rhport, uint8_t const* p_desc, uint8_t ep_count, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in) +{ + for(int i=0; ibDescriptorType && xfer_type == desc_ep->bmAttributes.xfer); + TU_ASSERT(usbd_edpt_open(rhport, desc_ep)); + + if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN ) + { + (*ep_in) = desc_ep->bEndpointAddress; + }else + { + (*ep_out) = desc_ep->bEndpointAddress; + } + + p_desc = tu_desc_next(p_desc); + } + + return true; +} + +// Helper to defer an isr function +void usbd_defer_func(osal_task_func_t func, void* param, bool in_isr) { + dcd_event_t event = { + .rhport = 0, + .event_id = USBD_EVENT_FUNC_CALL, + }; + event.func_call.func = func; + event.func_call.param = param; + + queue_event(&event, in_isr); +} + +//--------------------------------------------------------------------+ +// USBD Endpoint API +//--------------------------------------------------------------------+ + +bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep) { + rhport = _usbd_rhport; + + TU_ASSERT(tu_edpt_number(desc_ep->bEndpointAddress) < CFG_TUD_ENDPPOINT_MAX); + TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t) _usbd_dev.speed)); + + return dcd_edpt_open(rhport, desc_ep); +} + +bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr) { + (void) rhport; + + // TODO add this check later, also make sure we don't starve an out endpoint while suspending + // TU_VERIFY(tud_ready()); + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + tu_edpt_state_t* ep_state = &_usbd_dev.ep_status[epnum][dir]; + + return tu_edpt_claim(ep_state, _usbd_mutex); +} + +bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr) { + (void) rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + tu_edpt_state_t* ep_state = &_usbd_dev.ep_status[epnum][dir]; + + return tu_edpt_release(ep_state, _usbd_mutex); +} + +bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { + rhport = _usbd_rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + // TODO skip ready() check for now since enumeration also use this API + // TU_VERIFY(tud_ready()); + + TU_LOG_USBD(" Queue EP %02X with %u bytes ...\r\n", ep_addr, total_bytes); +#if CFG_TUD_LOG_LEVEL >= 3 + if(dir == TUSB_DIR_IN) { + TU_LOG_MEM(CFG_TUD_LOG_LEVEL, buffer, total_bytes, 2); + } +#endif + + // Attempt to transfer on a busy endpoint, sound like an race condition ! + TU_ASSERT(_usbd_dev.ep_status[epnum][dir].busy == 0); + + // Set busy first since the actual transfer can be complete before dcd_edpt_xfer() + // could return and USBD task can preempt and clear the busy + _usbd_dev.ep_status[epnum][dir].busy = 1; + + if (dcd_edpt_xfer(rhport, ep_addr, buffer, total_bytes)) { + return true; + } else { + // DCD error, mark endpoint as ready to allow next transfer + _usbd_dev.ep_status[epnum][dir].busy = 0; + _usbd_dev.ep_status[epnum][dir].claimed = 0; + TU_LOG_USBD("FAILED\r\n"); + TU_BREAKPOINT(); + return false; + } +} + +// The number of bytes has to be given explicitly to allow more flexible control of how many +// bytes should be written and second to keep the return value free to give back a boolean +// success message. If total_bytes is too big, the FIFO will copy only what is available +// into the USB buffer! +bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t total_bytes) { + rhport = _usbd_rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + TU_LOG_USBD(" Queue ISO EP %02X with %u bytes ... ", ep_addr, total_bytes); + + // Attempt to transfer on a busy endpoint, sound like an race condition ! + TU_ASSERT(_usbd_dev.ep_status[epnum][dir].busy == 0); + + // Set busy first since the actual transfer can be complete before dcd_edpt_xfer() could return + // and usbd task can preempt and clear the busy + _usbd_dev.ep_status[epnum][dir].busy = 1; + + if (dcd_edpt_xfer_fifo(rhport, ep_addr, ff, total_bytes)) { + TU_LOG_USBD("OK\r\n"); + return true; + } else { + // DCD error, mark endpoint as ready to allow next transfer + _usbd_dev.ep_status[epnum][dir].busy = 0; + _usbd_dev.ep_status[epnum][dir].claimed = 0; + TU_LOG_USBD("failed\r\n"); + TU_BREAKPOINT(); + return false; + } +} + +bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr) { + (void) rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + return _usbd_dev.ep_status[epnum][dir].busy; +} + +void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { + rhport = _usbd_rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + // only stalled if currently cleared + TU_LOG_USBD(" Stall EP %02X\r\n", ep_addr); + dcd_edpt_stall(rhport, ep_addr); + _usbd_dev.ep_status[epnum][dir].stalled = 1; + _usbd_dev.ep_status[epnum][dir].busy = 1; +} + +void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { + rhport = _usbd_rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + // only clear if currently stalled + TU_LOG_USBD(" Clear Stall EP %02X\r\n", ep_addr); + dcd_edpt_clear_stall(rhport, ep_addr); + _usbd_dev.ep_status[epnum][dir].stalled = 0; + _usbd_dev.ep_status[epnum][dir].busy = 0; +} + +bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr) { + (void) rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + return _usbd_dev.ep_status[epnum][dir].stalled; +} + +/** + * usbd_edpt_close will disable an endpoint. + * In progress transfers on this EP may be delivered after this call. + */ +void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr) { + rhport = _usbd_rhport; + + TU_ASSERT(dcd_edpt_close, /**/); + TU_LOG_USBD(" CLOSING Endpoint: 0x%02X\r\n", ep_addr); + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + dcd_edpt_close(rhport, ep_addr); + _usbd_dev.ep_status[epnum][dir].stalled = 0; + _usbd_dev.ep_status[epnum][dir].busy = 0; + _usbd_dev.ep_status[epnum][dir].claimed = 0; + + return; +} + +void usbd_sof_enable(uint8_t rhport, bool en) { + rhport = _usbd_rhport; + + // TODO: Check needed if all drivers including the user sof_cb does not need an active SOF ISR any more. + // Only if all drivers switched off SOF calls the SOF interrupt may be disabled + dcd_sof_enable(rhport, en); +} + +bool usbd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + rhport = _usbd_rhport; + + TU_ASSERT(dcd_edpt_iso_alloc); + TU_ASSERT(tu_edpt_number(ep_addr) < CFG_TUD_ENDPPOINT_MAX); + + return dcd_edpt_iso_alloc(rhport, ep_addr, largest_packet_size); +} + +bool usbd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep) { + rhport = _usbd_rhport; + + uint8_t const epnum = tu_edpt_number(desc_ep->bEndpointAddress); + uint8_t const dir = tu_edpt_dir(desc_ep->bEndpointAddress); + + TU_ASSERT(dcd_edpt_iso_activate); + TU_ASSERT(epnum < CFG_TUD_ENDPPOINT_MAX); + TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t) _usbd_dev.speed)); + + _usbd_dev.ep_status[epnum][dir].stalled = 0; + _usbd_dev.ep_status[epnum][dir].busy = 0; + _usbd_dev.ep_status[epnum][dir].claimed = 0; + return dcd_edpt_iso_activate(rhport, desc_ep); +} + +#endif diff --git a/test-devices/loopback-stm32/lib/tinyusb/device/usbd.h b/test-devices/loopback-stm32/lib/tinyusb/device/usbd.h new file mode 100644 index 00000000..f3673404 --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/device/usbd.h @@ -0,0 +1,872 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_USBD_H_ +#define _TUSB_USBD_H_ + +#include "common/tusb_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ + +// Init device stack on roothub port +bool tud_init (uint8_t rhport); + +// Deinit device stack on roothub port +bool tud_deinit(uint8_t rhport); + +// Check if device stack is already initialized +bool tud_inited(void); + +// Task function should be called in main/rtos loop, extended version of tud_task() +// - timeout_ms: millisecond to wait, zero = no wait, 0xFFFFFFFF = wait forever +// - in_isr: if function is called in ISR +void tud_task_ext(uint32_t timeout_ms, bool in_isr); + +// Task function should be called in main/rtos loop +TU_ATTR_ALWAYS_INLINE static inline +void tud_task (void) { + tud_task_ext(UINT32_MAX, false); +} + +// Check if there is pending events need processing by tud_task() +bool tud_task_event_ready(void); + +#ifndef _TUSB_DCD_H_ +extern void dcd_int_handler(uint8_t rhport); +#endif + +// Interrupt handler, name alias to DCD +#define tud_int_handler dcd_int_handler + +// Get current bus speed +tusb_speed_t tud_speed_get(void); + +// Check if device is connected (may not mounted/configured yet) +// True if just got out of Bus Reset and received the very first data from host +bool tud_connected(void); + +// Check if device is connected and configured +bool tud_mounted(void); + +// Check if device is suspended +bool tud_suspended(void); + +// Check if device is ready to transfer +TU_ATTR_ALWAYS_INLINE static inline +bool tud_ready(void) { + return tud_mounted() && !tud_suspended(); +} + +// Remote wake up host, only if suspended and enabled by host +bool tud_remote_wakeup(void); + +// Enable pull-up resistor on D+ D- +// Return false on unsupported MCUs +bool tud_disconnect(void); + +// Disable pull-up resistor on D+ D- +// Return false on unsupported MCUs +bool tud_connect(void); + +// Carry out Data and Status stage of control transfer +// - If len = 0, it is equivalent to sending status only +// - If len > wLength : it will be truncated +bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, void* buffer, uint16_t len); + +// Send STATUS (zero length) packet +bool tud_control_status(uint8_t rhport, tusb_control_request_t const * request); + +//--------------------------------------------------------------------+ +// Application Callbacks (WEAK is optional) +//--------------------------------------------------------------------+ + +// Invoked when received GET DEVICE DESCRIPTOR request +// Application return pointer to descriptor +uint8_t const * tud_descriptor_device_cb(void); + +// Invoked when received GET CONFIGURATION DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint8_t const * tud_descriptor_configuration_cb(uint8_t index); + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid); + +// Invoked when received GET BOS DESCRIPTOR request +// Application return pointer to descriptor +TU_ATTR_WEAK uint8_t const * tud_descriptor_bos_cb(void); + +// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete. +// device_qualifier descriptor describes information about a high-speed capable device that would +// change if the device were operating at the other speed. If not highspeed capable stall this request. +TU_ATTR_WEAK uint8_t const* tud_descriptor_device_qualifier_cb(void); + +// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa +TU_ATTR_WEAK uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index); + +// Invoked when device is mounted (configured) +TU_ATTR_WEAK void tud_mount_cb(void); + +// Invoked when device is unmounted +TU_ATTR_WEAK void tud_umount_cb(void); + +// Invoked when usb bus is suspended +// Within 7ms, device must draw an average of current less than 2.5 mA from bus +TU_ATTR_WEAK void tud_suspend_cb(bool remote_wakeup_en); + +// Invoked when usb bus is resumed +TU_ATTR_WEAK void tud_resume_cb(void); + +// Invoked when there is a new usb event, which need to be processed by tud_task()/tud_task_ext() +void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr); + +// Invoked when received control request with VENDOR TYPE +TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); + +//--------------------------------------------------------------------+ +// Binary Device Object Store (BOS) Descriptor Templates +//--------------------------------------------------------------------+ + +#define TUD_BOS_DESC_LEN 5 + +// total length, number of device caps +#define TUD_BOS_DESCRIPTOR(_total_len, _caps_num) \ + 5, TUSB_DESC_BOS, U16_TO_U8S_LE(_total_len), _caps_num + +// Device Capability Platform 128-bit UUID + Data +#define TUD_BOS_PLATFORM_DESCRIPTOR(...) \ + 4+TU_ARGS_NUM(__VA_ARGS__), TUSB_DESC_DEVICE_CAPABILITY, DEVICE_CAPABILITY_PLATFORM, 0x00, __VA_ARGS__ + +//------------- WebUSB BOS Platform -------------// + +// Descriptor Length +#define TUD_BOS_WEBUSB_DESC_LEN 24 + +// Vendor Code, iLandingPage +#define TUD_BOS_WEBUSB_DESCRIPTOR(_vendor_code, _ipage) \ + TUD_BOS_PLATFORM_DESCRIPTOR(TUD_BOS_WEBUSB_UUID, U16_TO_U8S_LE(0x0100), _vendor_code, _ipage) + +#define TUD_BOS_WEBUSB_UUID \ + 0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47, \ + 0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65 + +//------------- Microsoft OS 2.0 Platform -------------// +#define TUD_BOS_MICROSOFT_OS_DESC_LEN 28 + +// Total Length of descriptor set, vendor code +#define TUD_BOS_MS_OS_20_DESCRIPTOR(_desc_set_len, _vendor_code) \ + TUD_BOS_PLATFORM_DESCRIPTOR(TUD_BOS_MS_OS_20_UUID, U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(_desc_set_len), _vendor_code, 0) + +#define TUD_BOS_MS_OS_20_UUID \ + 0xDF, 0x60, 0xDD, 0xD8, 0x89, 0x45, 0xC7, 0x4C, \ + 0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F + +//--------------------------------------------------------------------+ +// Configuration Descriptor Templates +//--------------------------------------------------------------------+ + +#define TUD_CONFIG_DESC_LEN (9) + +// Config number, interface count, string index, total length, attribute, power in mA +#define TUD_CONFIG_DESCRIPTOR(config_num, _itfcount, _stridx, _total_len, _attribute, _power_ma) \ + 9, TUSB_DESC_CONFIGURATION, U16_TO_U8S_LE(_total_len), _itfcount, config_num, _stridx, TU_BIT(7) | _attribute, (_power_ma)/2 + +//--------------------------------------------------------------------+ +// CDC Descriptor Templates +//--------------------------------------------------------------------+ + +// Length of template descriptor: 66 bytes +#define TUD_CDC_DESC_LEN (8+9+5+5+4+5+7+9+7+7) + +// CDC Descriptor Template +// Interface number, string index, EP notification address and size, EP data address (out, in) and size. +#define TUD_CDC_DESCRIPTOR(_itfnum, _stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize) \ + /* Interface Associate */\ + 8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL, CDC_COMM_PROTOCOL_NONE, 0,\ + /* CDC Control Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL, CDC_COMM_PROTOCOL_NONE, _stridx,\ + /* CDC Header */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0120),\ + /* CDC Call */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_CALL_MANAGEMENT, 0, (uint8_t)((_itfnum) + 1),\ + /* CDC ACM: support line request + send break */\ + 4, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, 6,\ + /* CDC Union */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\ + /* Endpoint Notification */\ + 7, TUSB_DESC_ENDPOINT, _ep_notif, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_notif_size), 16,\ + /* CDC Data Interface */\ + 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 2, TUSB_CLASS_CDC_DATA, 0, 0, 0,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 + +//--------------------------------------------------------------------+ +// MSC Descriptor Templates +//--------------------------------------------------------------------+ + +// Length of template descriptor: 23 bytes +#define TUD_MSC_DESC_LEN (9 + 7 + 7) + +// Interface number, string index, EP Out & EP In address, EP size +#define TUD_MSC_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \ + /* Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_MSC, MSC_SUBCLASS_SCSI, MSC_PROTOCOL_BOT, _stridx,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 + + +//--------------------------------------------------------------------+ +// HID Descriptor Templates +//--------------------------------------------------------------------+ + +// Length of template descriptor: 25 bytes +#define TUD_HID_DESC_LEN (9 + 9 + 7) + +// HID Input only descriptor +// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval +#define TUD_HID_DESCRIPTOR(_itfnum, _stridx, _boot_protocol, _report_desc_len, _epin, _epsize, _ep_interval) \ + /* Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUSB_CLASS_HID, (uint8_t)((_boot_protocol) ? (uint8_t)HID_SUBCLASS_BOOT : 0), _boot_protocol, _stridx,\ + /* HID descriptor */\ + 9, HID_DESC_TYPE_HID, U16_TO_U8S_LE(0x0111), 0, 1, HID_DESC_TYPE_REPORT, U16_TO_U8S_LE(_report_desc_len),\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _ep_interval + +// Length of template descriptor: 32 bytes +#define TUD_HID_INOUT_DESC_LEN (9 + 9 + 7 + 7) + +// HID Input & Output descriptor +// Interface number, string index, protocol, report descriptor len, EP OUT & IN address, size & polling interval +#define TUD_HID_INOUT_DESCRIPTOR(_itfnum, _stridx, _boot_protocol, _report_desc_len, _epout, _epin, _epsize, _ep_interval) \ + /* Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_HID, (uint8_t)((_boot_protocol) ? (uint8_t)HID_SUBCLASS_BOOT : 0), _boot_protocol, _stridx,\ + /* HID descriptor */\ + 9, HID_DESC_TYPE_HID, U16_TO_U8S_LE(0x0111), 0, 1, HID_DESC_TYPE_REPORT, U16_TO_U8S_LE(_report_desc_len),\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _ep_interval, \ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _ep_interval + +//--------------------------------------------------------------------+ +// MIDI Descriptor Templates +// Note: MIDI v1.0 is based on Audio v1.0 +//--------------------------------------------------------------------+ + +#define TUD_MIDI_DESC_HEAD_LEN (9 + 9 + 9 + 7) +#define TUD_MIDI_DESC_HEAD(_itfnum, _stridx, _numcables) \ + /* Audio Control (AC) Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_CONTROL, AUDIO_FUNC_PROTOCOL_CODE_UNDEF, _stridx,\ + /* AC Header */\ + 9, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_HEADER, U16_TO_U8S_LE(0x0100), U16_TO_U8S_LE(0x0009), 1, (uint8_t)((_itfnum) + 1),\ + /* MIDI Streaming (MS) Interface */\ + 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum) + 1), 0, 2, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_MIDI_STREAMING, AUDIO_FUNC_PROTOCOL_CODE_UNDEF, 0,\ + /* MS Header */\ + 7, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_HEADER, U16_TO_U8S_LE(0x0100), U16_TO_U8S_LE(7 + (_numcables) * TUD_MIDI_DESC_JACK_LEN + 2 * TUD_MIDI_DESC_EP_LEN(_numcables)) + +#define TUD_MIDI_JACKID_IN_EMB(_cablenum) \ + (uint8_t)(((_cablenum) - 1) * 4 + 1) + +#define TUD_MIDI_JACKID_IN_EXT(_cablenum) \ + (uint8_t)(((_cablenum) - 1) * 4 + 2) + +#define TUD_MIDI_JACKID_OUT_EMB(_cablenum) \ + (uint8_t)(((_cablenum) - 1) * 4 + 3) + +#define TUD_MIDI_JACKID_OUT_EXT(_cablenum) \ + (uint8_t)(((_cablenum) - 1) * 4 + 4) + +#define TUD_MIDI_DESC_JACK_LEN (6 + 6 + 9 + 9) +#define TUD_MIDI_DESC_JACK_DESC(_cablenum, _stridx) \ + /* MS In Jack (Embedded) */\ + 6, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_IN_JACK, MIDI_JACK_EMBEDDED, TUD_MIDI_JACKID_IN_EMB(_cablenum), _stridx,\ + /* MS In Jack (External) */\ + 6, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_IN_JACK, MIDI_JACK_EXTERNAL, TUD_MIDI_JACKID_IN_EXT(_cablenum), _stridx,\ + /* MS Out Jack (Embedded), connected to In Jack External */\ + 9, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_OUT_JACK, MIDI_JACK_EMBEDDED, TUD_MIDI_JACKID_OUT_EMB(_cablenum), 1, TUD_MIDI_JACKID_IN_EXT(_cablenum), 1, _stridx,\ + /* MS Out Jack (External), connected to In Jack Embedded */\ + 9, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_OUT_JACK, MIDI_JACK_EXTERNAL, TUD_MIDI_JACKID_OUT_EXT(_cablenum), 1, TUD_MIDI_JACKID_IN_EMB(_cablenum), 1, _stridx + +#define TUD_MIDI_DESC_JACK(_cablenum) TUD_MIDI_DESC_JACK_DESC(_cablenum, 0) + +#define TUD_MIDI_DESC_EP_LEN(_numcables) (9 + 4 + (_numcables)) +#define TUD_MIDI_DESC_EP(_epout, _epsize, _numcables) \ + /* Endpoint: Note Audio v1.0's endpoint has 9 bytes instead of 7 */\ + 9, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0, 0, 0, \ + /* MS Endpoint (connected to embedded jack) */\ + (uint8_t)(4 + (_numcables)), TUSB_DESC_CS_ENDPOINT, MIDI_CS_ENDPOINT_GENERAL, _numcables + +// Length of template descriptor (88 bytes) +#define TUD_MIDI_DESC_LEN (TUD_MIDI_DESC_HEAD_LEN + TUD_MIDI_DESC_JACK_LEN + TUD_MIDI_DESC_EP_LEN(1) * 2) + +// MIDI simple descriptor +// - 1 Embedded Jack In connected to 1 External Jack Out +// - 1 Embedded Jack out connected to 1 External Jack In +#define TUD_MIDI_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \ + TUD_MIDI_DESC_HEAD(_itfnum, _stridx, 1),\ + TUD_MIDI_DESC_JACK_DESC(1, 0),\ + TUD_MIDI_DESC_EP(_epout, _epsize, 1),\ + TUD_MIDI_JACKID_IN_EMB(1),\ + TUD_MIDI_DESC_EP(_epin, _epsize, 1),\ + TUD_MIDI_JACKID_OUT_EMB(1) + +//--------------------------------------------------------------------+ +// Audio v2.0 Descriptor Templates +//--------------------------------------------------------------------+ + +/* Standard Interface Association Descriptor (IAD) */ +#define TUD_AUDIO_DESC_IAD_LEN 8 +#define TUD_AUDIO_DESC_IAD(_firstitf, _nitfs, _stridx) \ + TUD_AUDIO_DESC_IAD_LEN, TUSB_DESC_INTERFACE_ASSOCIATION, _firstitf, _nitfs, TUSB_CLASS_AUDIO, AUDIO_FUNCTION_SUBCLASS_UNDEFINED, AUDIO_FUNC_PROTOCOL_CODE_V2, _stridx + +/* Standard AC Interface Descriptor(4.7.1) */ +#define TUD_AUDIO_DESC_STD_AC_LEN 9 +#define TUD_AUDIO_DESC_STD_AC(_itfnum, _nEPs, _stridx) /* _nEPs is 0 or 1 */\ + TUD_AUDIO_DESC_STD_AC_LEN, TUSB_DESC_INTERFACE, _itfnum, /* fixed to zero */ 0x00, _nEPs, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_CONTROL, AUDIO_INT_PROTOCOL_CODE_V2, _stridx + +/* Class-Specific AC Interface Header Descriptor(4.7.2) */ +#define TUD_AUDIO_DESC_CS_AC_LEN 9 +#define TUD_AUDIO_DESC_CS_AC(_bcdADC, _category, _totallen, _ctrl) /* _bcdADC : Audio Device Class Specification Release Number in Binary-Coded Decimal, _category : see audio_function_t, _totallen : Total number of bytes returned for the class-specific AudioControl interface i.e. Clock Source, Unit and Terminal descriptors - Do not include TUD_AUDIO_DESC_CS_AC_LEN, we already do this here*/ \ + TUD_AUDIO_DESC_CS_AC_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_HEADER, U16_TO_U8S_LE(_bcdADC), _category, U16_TO_U8S_LE(_totallen + TUD_AUDIO_DESC_CS_AC_LEN), _ctrl + +/* Clock Source Descriptor(4.7.2.1) */ +#define TUD_AUDIO_DESC_CLK_SRC_LEN 8 +#define TUD_AUDIO_DESC_CLK_SRC(_clkid, _attr, _ctrl, _assocTerm, _stridx) \ + TUD_AUDIO_DESC_CLK_SRC_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_CLOCK_SOURCE, _clkid, _attr, _ctrl, _assocTerm, _stridx + +/* Input Terminal Descriptor(4.7.2.4) */ +#define TUD_AUDIO_DESC_INPUT_TERM_LEN 17 +#define TUD_AUDIO_DESC_INPUT_TERM(_termid, _termtype, _assocTerm, _clkid, _nchannelslogical, _channelcfg, _idxchannelnames, _ctrl, _stridx) \ + TUD_AUDIO_DESC_INPUT_TERM_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL, _termid, U16_TO_U8S_LE(_termtype), _assocTerm, _clkid, _nchannelslogical, U32_TO_U8S_LE(_channelcfg), _idxchannelnames, U16_TO_U8S_LE(_ctrl), _stridx + +/* Output Terminal Descriptor(4.7.2.5) */ +#define TUD_AUDIO_DESC_OUTPUT_TERM_LEN 12 +#define TUD_AUDIO_DESC_OUTPUT_TERM(_termid, _termtype, _assocTerm, _srcid, _clkid, _ctrl, _stridx) \ + TUD_AUDIO_DESC_OUTPUT_TERM_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL, _termid, U16_TO_U8S_LE(_termtype), _assocTerm, _srcid, _clkid, U16_TO_U8S_LE(_ctrl), _stridx + +/* Feature Unit Descriptor(4.7.2.8) */ +// 1 - Channel +#define TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN 6+(1+1)*4 +#define TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(_unitid, _srcid, _ctrlch0master, _ctrlch1, _stridx) \ + TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_FEATURE_UNIT, _unitid, _srcid, U32_TO_U8S_LE(_ctrlch0master), U32_TO_U8S_LE(_ctrlch1), _stridx + +// 2 - Channels +#define TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN (6+(2+1)*4) +#define TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(_unitid, _srcid, _ctrlch0master, _ctrlch1, _ctrlch2, _stridx) \ + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_FEATURE_UNIT, _unitid, _srcid, U32_TO_U8S_LE(_ctrlch0master), U32_TO_U8S_LE(_ctrlch1), U32_TO_U8S_LE(_ctrlch2), _stridx +// 4 - Channels +#define TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL_LEN (6+(4+1)*4) +#define TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL(_unitid, _srcid, _ctrlch0master, _ctrlch1, _ctrlch2, _ctrlch3, _ctrlch4, _stridx) \ + TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_FEATURE_UNIT, _unitid, _srcid, U32_TO_U8S_LE(_ctrlch0master), U32_TO_U8S_LE(_ctrlch1), U32_TO_U8S_LE(_ctrlch2), U32_TO_U8S_LE(_ctrlch3), U32_TO_U8S_LE(_ctrlch4), _stridx + +// For more channels, add definitions here + +/* Standard AC Interrupt Endpoint Descriptor(4.8.2.1) */ +#define TUD_AUDIO_DESC_STD_AC_INT_EP_LEN 7 +#define TUD_AUDIO_DESC_STD_AC_INT_EP(_ep, _interval) \ + TUD_AUDIO_DESC_STD_AC_INT_EP_LEN, TUSB_DESC_ENDPOINT, _ep, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(6), _interval + +/* Standard AS Interface Descriptor(4.9.1) */ +#define TUD_AUDIO_DESC_STD_AS_INT_LEN 9 +#define TUD_AUDIO_DESC_STD_AS_INT(_itfnum, _altset, _nEPs, _stridx) \ + TUD_AUDIO_DESC_STD_AS_INT_LEN, TUSB_DESC_INTERFACE, _itfnum, _altset, _nEPs, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_STREAMING, AUDIO_INT_PROTOCOL_CODE_V2, _stridx + +/* Class-Specific AS Interface Descriptor(4.9.2) */ +#define TUD_AUDIO_DESC_CS_AS_INT_LEN 16 +#define TUD_AUDIO_DESC_CS_AS_INT(_termid, _ctrl, _formattype, _formats, _nchannelsphysical, _channelcfg, _stridx) \ + TUD_AUDIO_DESC_CS_AS_INT_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AS_INTERFACE_AS_GENERAL, _termid, _ctrl, _formattype, U32_TO_U8S_LE(_formats), _nchannelsphysical, U32_TO_U8S_LE(_channelcfg), _stridx + +/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ +#define TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN 6 +#define TUD_AUDIO_DESC_TYPE_I_FORMAT(_subslotsize, _bitresolution) /* _subslotsize is number of bytes per sample (i.e. subslot) and can be 1,2,3, or 4 */\ + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AS_INTERFACE_FORMAT_TYPE, AUDIO_FORMAT_TYPE_I, _subslotsize, _bitresolution + +/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ +#define TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN 7 +#define TUD_AUDIO_DESC_STD_AS_ISO_EP(_ep, _attr, _maxEPsize, _interval) \ + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN, TUSB_DESC_ENDPOINT, _ep, _attr, U16_TO_U8S_LE(_maxEPsize), _interval + +/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ +#define TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN 8 +#define TUD_AUDIO_DESC_CS_AS_ISO_EP(_attr, _ctrl, _lockdelayunit, _lockdelay) \ + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN, TUSB_DESC_CS_ENDPOINT, AUDIO_CS_EP_SUBTYPE_GENERAL, _attr, _ctrl, _lockdelayunit, U16_TO_U8S_LE(_lockdelay) + +/* Standard AS Isochronous Feedback Endpoint Descriptor(4.10.2.1) */ +#define TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN 7 +#define TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(_ep, _interval) \ + TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN, TUSB_DESC_ENDPOINT, _ep, (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_NO_SYNC | (uint8_t)TUSB_ISO_EP_ATT_EXPLICIT_FB), U16_TO_U8S_LE(4), _interval + +// AUDIO simple descriptor (UAC2) for 1 microphone input +// - 1 Input Terminal, 1 Feature Unit (Mute and Volume Control), 1 Output Terminal, 1 Clock Source + +#define TUD_AUDIO_MIC_ONE_CH_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN\ + + TUD_AUDIO_DESC_STD_AC_LEN\ + + TUD_AUDIO_DESC_CS_AC_LEN\ + + TUD_AUDIO_DESC_CLK_SRC_LEN\ + + TUD_AUDIO_DESC_INPUT_TERM_LEN\ + + TUD_AUDIO_DESC_OUTPUT_TERM_LEN\ + + TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN\ + + TUD_AUDIO_DESC_STD_AS_INT_LEN\ + + TUD_AUDIO_DESC_STD_AS_INT_LEN\ + + TUD_AUDIO_DESC_CS_AS_INT_LEN\ + + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\ + + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\ + + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN) + +#define TUD_AUDIO_MIC_ONE_CH_DESC_N_AS_INT 1 // Number of AS interfaces + +#define TUD_AUDIO_MIC_ONE_CH_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epsize) \ + /* Standard Interface Association Descriptor (IAD) */\ + TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\ + /* Standard AC Interface Descriptor(4.7.1) */\ + TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\ + /* Class-Specific AC Interface Header Descriptor(4.7.2) */\ + TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_MICROPHONE, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN+TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS),\ + /* Clock Source Descriptor(4.7.2.1) */\ + TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x01, /*_stridx*/ 0x00),\ + /* Input Terminal Descriptor(4.7.2.4) */\ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x03, /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00),\ + /* Output Terminal Descriptor(4.7.2.5) */\ + TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ 0x03, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x01, /*_srcid*/ 0x02, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\ + /* Feature Unit Descriptor(4.7.2.8) */\ + TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00),\ + /* Standard AS Interface Descriptor(4.9.1) */\ + /* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */\ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\ + /* Standard AS Interface Descriptor(4.9.1) */\ + /* Interface 1, Alternate 1 - alternate interface for data streaming */\ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00),\ + /* Class-Specific AS Interface Descriptor(4.9.2) */\ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ 0x03, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ + /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\ + TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\ + /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\ + TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\ + /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\ + TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000) + +// AUDIO simple descriptor (UAC2) for 4 microphone input +// - 1 Input Terminal, 1 Feature Unit (Mute and Volume Control), 1 Output Terminal, 1 Clock Source + +#define TUD_AUDIO_MIC_FOUR_CH_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN\ + + TUD_AUDIO_DESC_STD_AC_LEN\ + + TUD_AUDIO_DESC_CS_AC_LEN\ + + TUD_AUDIO_DESC_CLK_SRC_LEN\ + + TUD_AUDIO_DESC_INPUT_TERM_LEN\ + + TUD_AUDIO_DESC_OUTPUT_TERM_LEN\ + + TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL_LEN\ + + TUD_AUDIO_DESC_STD_AS_INT_LEN\ + + TUD_AUDIO_DESC_STD_AS_INT_LEN\ + + TUD_AUDIO_DESC_CS_AS_INT_LEN\ + + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\ + + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\ + + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN) + +#define TUD_AUDIO_MIC_FOUR_CH_DESC_N_AS_INT 1 // Number of AS interfaces + +#define TUD_AUDIO_MIC_FOUR_CH_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epsize) \ + /* Standard Interface Association Descriptor (IAD) */\ + TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\ + /* Standard AC Interface Descriptor(4.7.1) */\ + TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\ + /* Class-Specific AC Interface Header Descriptor(4.7.2) */\ + TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_MICROPHONE, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN+TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS),\ + /* Clock Source Descriptor(4.7.2.1) */\ + TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x01, /*_stridx*/ 0x00),\ + /* Input Terminal Descriptor(4.7.2.4) */\ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x03, /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x04, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00),\ + /* Output Terminal Descriptor(4.7.2.5) */\ + TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ 0x03, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x01, /*_srcid*/ 0x02, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\ + /* Feature Unit Descriptor(4.7.2.8) */\ + TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch3*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch4*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00),\ + /* Standard AS Interface Descriptor(4.9.1) */\ + /* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */\ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\ + /* Standard AS Interface Descriptor(4.9.1) */\ + /* Interface 1, Alternate 1 - alternate interface for data streaming */\ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00),\ + /* Class-Specific AS Interface Descriptor(4.9.2) */\ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ 0x03, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x04, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ + /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\ + TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\ + /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\ + TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\ + /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\ + TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000) + +// AUDIO simple descriptor (UAC2) for mono speaker +// - 1 Input Terminal, 2 Feature Unit (Mute and Volume Control), 3 Output Terminal, 4 Clock Source + +#define TUD_AUDIO_SPEAKER_MONO_FB_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN\ + + TUD_AUDIO_DESC_STD_AC_LEN\ + + TUD_AUDIO_DESC_CS_AC_LEN\ + + TUD_AUDIO_DESC_CLK_SRC_LEN\ + + TUD_AUDIO_DESC_INPUT_TERM_LEN\ + + TUD_AUDIO_DESC_OUTPUT_TERM_LEN\ + + TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN\ + + TUD_AUDIO_DESC_STD_AS_INT_LEN\ + + TUD_AUDIO_DESC_STD_AS_INT_LEN\ + + TUD_AUDIO_DESC_CS_AS_INT_LEN\ + + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\ + + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\ + + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN\ + + TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN) + +#define TUD_AUDIO_SPEAKER_MONO_FB_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epsize, _epfb) \ + /* Standard Interface Association Descriptor (IAD) */\ + TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\ + /* Standard AC Interface Descriptor(4.7.1) */\ + TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\ + /* Class-Specific AC Interface Header Descriptor(4.7.2) */\ + TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_DESKTOP_SPEAKER, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN+TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS),\ + /* Clock Source Descriptor(4.7.2.1) */\ + TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x01, /*_stridx*/ 0x00),\ + /* Input Terminal Descriptor(4.7.2.4) */\ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00),\ + /* Output Terminal Descriptor(4.7.2.5) */\ + TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ 0x03, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, /*_assocTerm*/ 0x01, /*_srcid*/ 0x02, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\ + /* Feature Unit Descriptor(4.7.2.8) */\ + TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_ctrlch0master*/ 0 * (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_ctrlch1*/ 0 * (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_stridx*/ 0x00),\ + /* Standard AS Interface Descriptor(4.9.1) */\ + /* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */\ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\ + /* Standard AS Interface Descriptor(4.9.1) */\ + /* Interface 1, Alternate 1 - alternate interface for data streaming */\ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x02, /*_stridx*/ 0x00),\ + /* Class-Specific AS Interface Descriptor(4.9.2) */\ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ 0x01, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ + /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\ + TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\ + /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\ + TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\ + /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\ + TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000),\ + /* Standard AS Isochronous Feedback Endpoint Descriptor(4.10.2.1) */\ + TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(/*_ep*/ _epfb, /*_interval*/ 1)\ + +// Calculate wMaxPacketSize of Endpoints +#define TUD_AUDIO_EP_SIZE(_maxFrequency, _nBytesPerSample, _nChannels) \ + ((((_maxFrequency + (TUD_OPT_HIGH_SPEED ? 7999 : 999)) / (TUD_OPT_HIGH_SPEED ? 8000 : 1000)) + 1) * _nBytesPerSample * _nChannels) + + +//--------------------------------------------------------------------+ +// USBTMC/USB488 Descriptor Templates +//--------------------------------------------------------------------+ + +#define TUD_USBTMC_APP_CLASS (TUSB_CLASS_APPLICATION_SPECIFIC) +#define TUD_USBTMC_APP_SUBCLASS 0x03u + +#define TUD_USBTMC_PROTOCOL_STD 0x00u +#define TUD_USBTMC_PROTOCOL_USB488 0x01u + +// Interface number, number of endpoints, EP string index, USB_TMC_PROTOCOL*, bulk-out endpoint ID, +// bulk-in endpoint ID +#define TUD_USBTMC_IF_DESCRIPTOR(_itfnum, _bNumEndpoints, _stridx, _itfProtocol) \ + /* Interface */ \ + 0x09, TUSB_DESC_INTERFACE, _itfnum, 0x00, _bNumEndpoints, TUD_USBTMC_APP_CLASS, TUD_USBTMC_APP_SUBCLASS, _itfProtocol, _stridx + +#define TUD_USBTMC_IF_DESCRIPTOR_LEN 9u + +#define TUD_USBTMC_BULK_DESCRIPTORS(_epout, _epin, _bulk_epsize) \ + /* Endpoint Out */ \ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_bulk_epsize), 0u, \ + /* Endpoint In */ \ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_bulk_epsize), 0u + +#define TUD_USBTMC_BULK_DESCRIPTORS_LEN (7u+7u) + +/* optional interrupt endpoint */ \ +// _int_pollingInterval : for LS/FS, expressed in frames (1ms each). 16 may be a good number? +#define TUD_USBTMC_INT_DESCRIPTOR(_ep_interrupt, _ep_interrupt_size, _int_pollingInterval ) \ + 7, TUSB_DESC_ENDPOINT, _ep_interrupt, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_interrupt_size), _int_pollingInterval + +#define TUD_USBTMC_INT_DESCRIPTOR_LEN (7u) + +//--------------------------------------------------------------------+ +// Vendor Descriptor Templates +//--------------------------------------------------------------------+ + +#define TUD_VENDOR_DESC_LEN (9+7+7) + +// Interface number, string index, EP Out & IN address, EP size +#define TUD_VENDOR_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \ + /* Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_VENDOR_SPECIFIC, 0x00, 0x00, _stridx,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 + +//--------------------------------------------------------------------+ +// DFU Runtime Descriptor Templates +//--------------------------------------------------------------------+ + +#define TUD_DFU_APP_CLASS (TUSB_CLASS_APPLICATION_SPECIFIC) +#define TUD_DFU_APP_SUBCLASS (APP_SUBCLASS_DFU_RUNTIME) + +// Length of template descriptr: 18 bytes +#define TUD_DFU_RT_DESC_LEN (9 + 9) + +// DFU runtime descriptor +// Interface number, string index, attributes, detach timeout, transfer size +#define TUD_DFU_RT_DESCRIPTOR(_itfnum, _stridx, _attr, _timeout, _xfer_size) \ + /* Interface */ \ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUD_DFU_APP_CLASS, TUD_DFU_APP_SUBCLASS, DFU_PROTOCOL_RT, _stridx, \ + /* Function */ \ + 9, DFU_DESC_FUNCTIONAL, _attr, U16_TO_U8S_LE(_timeout), U16_TO_U8S_LE(_xfer_size), U16_TO_U8S_LE(0x0101) + +//--------------------------------------------------------------------+ +// DFU Descriptor Templates +//--------------------------------------------------------------------+ + +// Length of template descriptor: 9 bytes + number of alternatives * 9 +#define TUD_DFU_DESC_LEN(_alt_count) (9 + (_alt_count) * 9) + +// Interface number, Alternate count, starting string index, attributes, detach timeout, transfer size +// Note: Alternate count must be numeric or macro, string index is increased by one for each Alt interface +#define TUD_DFU_DESCRIPTOR(_itfnum, _alt_count, _stridx, _attr, _timeout, _xfer_size) \ + TU_XSTRCAT(_TUD_DFU_ALT_,_alt_count)(_itfnum, 0, _stridx), \ + /* Function */ \ + 9, DFU_DESC_FUNCTIONAL, _attr, U16_TO_U8S_LE(_timeout), U16_TO_U8S_LE(_xfer_size), U16_TO_U8S_LE(0x0101) + +#define _TUD_DFU_ALT(_itfnum, _alt, _stridx) \ + /* Interface */ \ + 9, TUSB_DESC_INTERFACE, _itfnum, _alt, 0, TUD_DFU_APP_CLASS, TUD_DFU_APP_SUBCLASS, DFU_PROTOCOL_DFU, _stridx + +#define _TUD_DFU_ALT_1(_itfnum, _alt_count, _stridx) \ + _TUD_DFU_ALT(_itfnum, _alt_count, _stridx) + +#define _TUD_DFU_ALT_2(_itfnum, _alt_count, _stridx) \ + _TUD_DFU_ALT(_itfnum, _alt_count, _stridx), \ + _TUD_DFU_ALT_1(_itfnum, _alt_count+1, _stridx+1) + +#define _TUD_DFU_ALT_3(_itfnum, _alt_count, _stridx) \ + _TUD_DFU_ALT(_itfnum, _alt_count, _stridx), \ + _TUD_DFU_ALT_2(_itfnum, _alt_count+1, _stridx+1) + +#define _TUD_DFU_ALT_4(_itfnum, _alt_count, _stridx) \ + _TUD_DFU_ALT(_itfnum, _alt_count, _stridx), \ + _TUD_DFU_ALT_3(_itfnum, _alt_count+1, _stridx+1) + +#define _TUD_DFU_ALT_5(_itfnum, _alt_count, _stridx) \ + _TUD_DFU_ALT(_itfnum, _alt_count, _stridx), \ + _TUD_DFU_ALT_4(_itfnum, _alt_count+1, _stridx+1) + +#define _TUD_DFU_ALT_6(_itfnum, _alt_count, _stridx) \ + _TUD_DFU_ALT(_itfnum, _alt_count, _stridx), \ + _TUD_DFU_ALT_5(_itfnum, _alt_count+1, _stridx+1) + +#define _TUD_DFU_ALT_7(_itfnum, _alt_count, _stridx) \ + _TUD_DFU_ALT(_itfnum, _alt_count, _stridx), \ + _TUD_DFU_ALT_6(_itfnum, _alt_count+1, _stridx+1) + +#define _TUD_DFU_ALT_8(_itfnum, _alt_count, _stridx) \ + _TUD_DFU_ALT(_itfnum, _alt_count, _stridx), \ + _TUD_DFU_ALT_7(_itfnum, _alt_count+1, _stridx+1) + +//--------------------------------------------------------------------+ +// CDC-ECM Descriptor Templates +//--------------------------------------------------------------------+ + +// Length of template descriptor: 71 bytes +#define TUD_CDC_ECM_DESC_LEN (8+9+5+5+13+7+9+9+7+7) + +// CDC-ECM Descriptor Template +// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size. +#define TUD_CDC_ECM_DESCRIPTOR(_itfnum, _desc_stridx, _mac_stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize, _maxsegmentsize) \ + /* Interface Association */\ + 8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_ETHERNET_CONTROL_MODEL, 0, 0,\ + /* CDC Control Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_ETHERNET_CONTROL_MODEL, 0, _desc_stridx,\ + /* CDC-ECM Header */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0120),\ + /* CDC-ECM Union */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\ + /* CDC-ECM Functional Descriptor */\ + 13, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ETHERNET_NETWORKING, _mac_stridx, 0, 0, 0, 0, U16_TO_U8S_LE(_maxsegmentsize), U16_TO_U8S_LE(0), 0,\ + /* Endpoint Notification */\ + 7, TUSB_DESC_ENDPOINT, _ep_notif, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_notif_size), 1,\ + /* CDC Data Interface (default inactive) */\ + 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 0, TUSB_CLASS_CDC_DATA, 0, 0, 0,\ + /* CDC Data Interface (alternative active) */\ + 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 1, 2, TUSB_CLASS_CDC_DATA, 0, 0, 0,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 + +//--------------------------------------------------------------------+ +// RNDIS Descriptor Templates +//--------------------------------------------------------------------+ + +#if 0 +/* Windows XP */ +#define TUD_RNDIS_ITF_CLASS TUSB_CLASS_CDC +#define TUD_RNDIS_ITF_SUBCLASS CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL +#define TUD_RNDIS_ITF_PROTOCOL 0xFF /* CDC_COMM_PROTOCOL_MICROSOFT_RNDIS */ +#else +/* Windows 7+ */ +#define TUD_RNDIS_ITF_CLASS TUSB_CLASS_WIRELESS_CONTROLLER +#define TUD_RNDIS_ITF_SUBCLASS 0x01 +#define TUD_RNDIS_ITF_PROTOCOL 0x03 +#endif + +// Length of template descriptor: 66 bytes +#define TUD_RNDIS_DESC_LEN (8+9+5+5+4+5+7+9+7+7) + +// RNDIS Descriptor Template +// Interface number, string index, EP notification address and size, EP data address (out, in) and size. +#define TUD_RNDIS_DESCRIPTOR(_itfnum, _stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize) \ + /* Interface Association */\ + 8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUD_RNDIS_ITF_CLASS, TUD_RNDIS_ITF_SUBCLASS, TUD_RNDIS_ITF_PROTOCOL, 0,\ + /* CDC Control Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUD_RNDIS_ITF_CLASS, TUD_RNDIS_ITF_SUBCLASS, TUD_RNDIS_ITF_PROTOCOL, _stridx,\ + /* CDC-ACM Header */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0110),\ + /* CDC Call Management */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_CALL_MANAGEMENT, 0, (uint8_t)((_itfnum) + 1),\ + /* ACM */\ + 4, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, 0,\ + /* CDC Union */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\ + /* Endpoint Notification */\ + 7, TUSB_DESC_ENDPOINT, _ep_notif, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_notif_size), 1,\ + /* CDC Data Interface */\ + 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 2, TUSB_CLASS_CDC_DATA, 0, 0, 0,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 + +//--------------------------------------------------------------------+ +// Bluetooth Radio Descriptor Templates +//--------------------------------------------------------------------+ + +#define TUD_BT_APP_CLASS (TUSB_CLASS_WIRELESS_CONTROLLER) +#define TUD_BT_APP_SUBCLASS 0x01 +#define TUD_BT_PROTOCOL_PRIMARY_CONTROLLER 0x01 +#define TUD_BT_PROTOCOL_AMP_CONTROLLER 0x02 + +// Length of template descriptor: 38 bytes + number of ISO alternatives * 23 +#define TUD_BTH_DESC_LEN (8 + 9 + 7 + 7 + 7 + (CFG_TUD_BTH_ISO_ALT_COUNT) * (9 + 7 + 7)) + +/* Primary Interface */ +#define TUD_BTH_PRI_ITF(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_interval, _ep_in, _ep_out, _ep_size) \ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 3, TUD_BT_APP_CLASS, TUD_BT_APP_SUBCLASS, TUD_BT_PROTOCOL_PRIMARY_CONTROLLER, _stridx, \ + /* Endpoint In for events */ \ + 7, TUSB_DESC_ENDPOINT, _ep_evt, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_evt_size), _ep_evt_interval, \ + /* Endpoint In for ACL data */ \ + 7, TUSB_DESC_ENDPOINT, _ep_in, TUSB_XFER_BULK, U16_TO_U8S_LE(_ep_size), 1, \ + /* Endpoint Out for ACL data */ \ + 7, TUSB_DESC_ENDPOINT, _ep_out, TUSB_XFER_BULK, U16_TO_U8S_LE(_ep_size), 1 + +#define TUD_BTH_ISO_ITF(_itfnum, _alt, _ep_in, _ep_out, _n) ,\ + /* Interface with 2 endpoints */ \ + 9, TUSB_DESC_INTERFACE, _itfnum, _alt, 2, TUD_BT_APP_CLASS, TUD_BT_APP_SUBCLASS, TUD_BT_PROTOCOL_PRIMARY_CONTROLLER, 0, \ + /* Isochronous endpoints */ \ + 7, TUSB_DESC_ENDPOINT, _ep_in, TUSB_XFER_ISOCHRONOUS, U16_TO_U8S_LE(_n), 1, \ + 7, TUSB_DESC_ENDPOINT, _ep_out, TUSB_XFER_ISOCHRONOUS, U16_TO_U8S_LE(_n), 1 + +#define _FIRST(a, ...) a +#define _REST(a, ...) __VA_ARGS__ + +#define TUD_BTH_ISO_ITF_0(_itfnum, ...) +#define TUD_BTH_ISO_ITF_1(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 1, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) +#define TUD_BTH_ISO_ITF_2(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 2, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \ + TUD_BTH_ISO_ITF_1(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__)) +#define TUD_BTH_ISO_ITF_3(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 3, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \ + TUD_BTH_ISO_ITF_2(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__)) +#define TUD_BTH_ISO_ITF_4(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 4, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \ + TUD_BTH_ISO_ITF_3(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__)) +#define TUD_BTH_ISO_ITF_5(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 5, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \ + TUD_BTH_ISO_ITF_4(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__)) +#define TUD_BTH_ISO_ITF_6(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 6, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \ + TUD_BTH_ISO_ITF_5(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__)) + +#define TUD_BTH_ISO_ITFS(_itfnum, _ep_in, _ep_out, ...) \ + TU_XSTRCAT(TUD_BTH_ISO_ITF_, CFG_TUD_BTH_ISO_ALT_COUNT)(_itfnum, _ep_in, _ep_out, __VA_ARGS__) + +// BT Primary controller descriptor +// Interface number, string index, attributes, event endpoint, event endpoint size, interval, data in, data out, data endpoint size, iso endpoint sizes +// TODO BTH should also use IAD like CDC for composite device +#define TUD_BTH_DESCRIPTOR(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_interval, _ep_in, _ep_out, _ep_size,...) \ + /* Interface Associate */\ + 8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUD_BT_APP_CLASS, TUD_BT_APP_SUBCLASS, TUD_BT_PROTOCOL_PRIMARY_CONTROLLER, 0,\ + TUD_BTH_PRI_ITF(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_interval, _ep_in, _ep_out, _ep_size) \ + TUD_BTH_ISO_ITFS(_itfnum + 1, _ep_in + 1, _ep_out + 1, __VA_ARGS__) + +//--------------------------------------------------------------------+ +// CDC-NCM Descriptor Templates +//--------------------------------------------------------------------+ + +// Length of template descriptor +#define TUD_CDC_NCM_DESC_LEN (8+9+5+5+13+6+7+9+9+7+7) + +// CDC-ECM Descriptor Template +// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size. +#define TUD_CDC_NCM_DESCRIPTOR(_itfnum, _desc_stridx, _mac_stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize, _maxsegmentsize) \ + /* Interface Association */\ + 8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_NETWORK_CONTROL_MODEL, 0, 0,\ + /* CDC Control Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_NETWORK_CONTROL_MODEL, 0, _desc_stridx,\ + /* CDC-NCM Header */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0110),\ + /* CDC-NCM Union */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\ + /* CDC-NCM Functional Descriptor */\ + 13, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ETHERNET_NETWORKING, _mac_stridx, 0, 0, 0, 0, U16_TO_U8S_LE(_maxsegmentsize), U16_TO_U8S_LE(0), 0, \ + /* CDC-NCM Functional Descriptor */\ + 6, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_NCM, U16_TO_U8S_LE(0x0100), 0, \ + /* Endpoint Notification */\ + 7, TUSB_DESC_ENDPOINT, _ep_notif, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_notif_size), 50,\ + /* CDC Data Interface (default inactive) */\ + 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 0, TUSB_CLASS_CDC_DATA, 0, NCM_DATA_PROTOCOL_NETWORK_TRANSFER_BLOCK, 0,\ + /* CDC Data Interface (alternative active) */\ + 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 1, 2, TUSB_CLASS_CDC_DATA, 0, NCM_DATA_PROTOCOL_NETWORK_TRANSFER_BLOCK, 0,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 + +#ifdef __cplusplus +} +#endif + +#endif /* _TUSB_USBD_H_ */ + +/** @} */ diff --git a/test-devices/loopback-stm32/lib/tinyusb/device/usbd_control.c b/test-devices/loopback-stm32/lib/tinyusb/device/usbd_control.c new file mode 100644 index 00000000..35cce1f7 --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/device/usbd_control.c @@ -0,0 +1,222 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if CFG_TUD_ENABLED + +#include "dcd.h" +#include "tusb.h" +#include "device/usbd_pvt.h" + +//--------------------------------------------------------------------+ +// Callback weak stubs (called if application does not provide) +//--------------------------------------------------------------------+ +TU_ATTR_WEAK void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* request) { + (void) rhport; + (void) request; +} + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL +extern void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback); +#endif + +enum { + EDPT_CTRL_OUT = 0x00, + EDPT_CTRL_IN = 0x80 +}; + +typedef struct { + tusb_control_request_t request; + uint8_t* buffer; + uint16_t data_len; + uint16_t total_xferred; + usbd_control_xfer_cb_t complete_cb; +} usbd_control_xfer_t; + +tu_static usbd_control_xfer_t _ctrl_xfer; + +CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN +tu_static uint8_t _usbd_ctrl_buf[CFG_TUD_ENDPOINT0_SIZE]; + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ + +// Queue ZLP status transaction +static inline bool _status_stage_xact(uint8_t rhport, tusb_control_request_t const* request) { + // Opposite to endpoint in Data Phase + uint8_t const ep_addr = request->bmRequestType_bit.direction ? EDPT_CTRL_OUT : EDPT_CTRL_IN; + return usbd_edpt_xfer(rhport, ep_addr, NULL, 0); +} + +// Status phase +bool tud_control_status(uint8_t rhport, tusb_control_request_t const* request) { + _ctrl_xfer.request = (*request); + _ctrl_xfer.buffer = NULL; + _ctrl_xfer.total_xferred = 0; + _ctrl_xfer.data_len = 0; + + return _status_stage_xact(rhport, request); +} + +// Queue a transaction in Data Stage +// Each transaction has up to Endpoint0's max packet size. +// This function can also transfer an zero-length packet +static bool _data_stage_xact(uint8_t rhport) { + uint16_t const xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred, + CFG_TUD_ENDPOINT0_SIZE); + + uint8_t ep_addr = EDPT_CTRL_OUT; + + if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN) { + ep_addr = EDPT_CTRL_IN; + if (xact_len) { + TU_VERIFY(0 == tu_memcpy_s(_usbd_ctrl_buf, CFG_TUD_ENDPOINT0_SIZE, _ctrl_xfer.buffer, xact_len)); + } + } + + return usbd_edpt_xfer(rhport, ep_addr, xact_len ? _usbd_ctrl_buf : NULL, xact_len); +} + +// Transmit data to/from the control endpoint. +// If the request's wLength is zero, a status packet is sent instead. +bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const* request, void* buffer, uint16_t len) { + _ctrl_xfer.request = (*request); + _ctrl_xfer.buffer = (uint8_t*) buffer; + _ctrl_xfer.total_xferred = 0U; + _ctrl_xfer.data_len = tu_min16(len, request->wLength); + + if (request->wLength > 0U) { + if (_ctrl_xfer.data_len > 0U) { + TU_ASSERT(buffer); + } + +// TU_LOG2(" Control total data length is %u bytes\r\n", _ctrl_xfer.data_len); + + // Data stage + TU_ASSERT(_data_stage_xact(rhport)); + } else { + // Status stage + TU_ASSERT(_status_stage_xact(rhport, request)); + } + + return true; +} + +//--------------------------------------------------------------------+ +// USBD API +//--------------------------------------------------------------------+ +void usbd_control_reset(void); +void usbd_control_set_request(tusb_control_request_t const* request); +void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp); +bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); + +void usbd_control_reset(void) { + tu_varclr(&_ctrl_xfer); +} + +// Set complete callback +void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp) { + _ctrl_xfer.complete_cb = fp; +} + +// for dcd_set_address where DCD is responsible for status response +void usbd_control_set_request(tusb_control_request_t const* request) { + _ctrl_xfer.request = (*request); + _ctrl_xfer.buffer = NULL; + _ctrl_xfer.total_xferred = 0; + _ctrl_xfer.data_len = 0; +} + +// callback when a transaction complete on +// - DATA stage of control endpoint or +// - Status stage +bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { + (void) result; + + // Endpoint Address is opposite to direction bit, this is Status Stage complete event + if (tu_edpt_dir(ep_addr) != _ctrl_xfer.request.bmRequestType_bit.direction) { + TU_ASSERT(0 == xferred_bytes); + + // invoke optional dcd hook if available + dcd_edpt0_status_complete(rhport, &_ctrl_xfer.request); + + if (_ctrl_xfer.complete_cb) { + // TODO refactor with usbd_driver_print_control_complete_name + _ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_ACK, &_ctrl_xfer.request); + } + + return true; + } + + if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_OUT) { + TU_VERIFY(_ctrl_xfer.buffer); + memcpy(_ctrl_xfer.buffer, _usbd_ctrl_buf, xferred_bytes); + TU_LOG_MEM(CFG_TUD_LOG_LEVEL, _usbd_ctrl_buf, xferred_bytes, 2); + } + + _ctrl_xfer.total_xferred += (uint16_t) xferred_bytes; + _ctrl_xfer.buffer += xferred_bytes; + + // Data Stage is complete when all request's length are transferred or + // a short packet is sent including zero-length packet. + if ((_ctrl_xfer.request.wLength == _ctrl_xfer.total_xferred) || + (xferred_bytes < CFG_TUD_ENDPOINT0_SIZE)) { + // DATA stage is complete + bool is_ok = true; + + // invoke complete callback if set + // callback can still stall control in status phase e.g out data does not make sense + if (_ctrl_xfer.complete_cb) { + #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL + usbd_driver_print_control_complete_name(_ctrl_xfer.complete_cb); + #endif + + is_ok = _ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_DATA, &_ctrl_xfer.request); + } + + if (is_ok) { + // Send status + TU_ASSERT(_status_stage_xact(rhport, &_ctrl_xfer.request)); + } else { + // Stall both IN and OUT control endpoint + dcd_edpt_stall(rhport, EDPT_CTRL_OUT); + dcd_edpt_stall(rhport, EDPT_CTRL_IN); + } + } else { + // More data to transfer + TU_ASSERT(_data_stage_xact(rhport)); + } + + return true; +} + +#endif diff --git a/test-devices/loopback-stm32/lib/tinyusb/device/usbd_pvt.h b/test-devices/loopback-stm32/lib/tinyusb/device/usbd_pvt.h new file mode 100644 index 00000000..47752f32 --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/device/usbd_pvt.h @@ -0,0 +1,127 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ +#ifndef _TUSB_USBD_PVT_H_ +#define _TUSB_USBD_PVT_H_ + +#include "osal/osal.h" +#include "common/tusb_fifo.h" + +#ifdef __cplusplus + extern "C" { +#endif + +#define TU_LOG_USBD(...) TU_LOG(CFG_TUD_LOG_LEVEL, __VA_ARGS__) + +//--------------------------------------------------------------------+ +// Class Driver API +//--------------------------------------------------------------------+ + +typedef struct { + #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL + char const* name; + #endif + + void (* init ) (void); + bool (* deinit ) (void); + void (* reset ) (uint8_t rhport); + uint16_t (* open ) (uint8_t rhport, tusb_desc_interface_t const * desc_intf, uint16_t max_len); + bool (* control_xfer_cb ) (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); + bool (* xfer_cb ) (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); + void (* sof ) (uint8_t rhport, uint32_t frame_count); // optional +} usbd_class_driver_t; + +// Invoked when initializing device stack to get additional class drivers. +// Can be implemented by application to extend/overwrite class driver support. +// Note: The drivers array must be accessible at all time when stack is active +usbd_class_driver_t const* usbd_app_driver_get_cb(uint8_t* driver_count) TU_ATTR_WEAK; + +typedef bool (*usbd_control_xfer_cb_t)(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); + +void usbd_int_set(bool enabled); + +//--------------------------------------------------------------------+ +// USBD Endpoint API +// Note: rhport should be 0 since device stack only support 1 rhport for now +//--------------------------------------------------------------------+ + +// Open an endpoint +bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep); + +// Close an endpoint +void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr); + +// Submit a usb transfer +bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes); + +// Submit a usb ISO transfer by use of a FIFO (ring buffer) - all bytes in FIFO get transmitted +bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes); + +// Claim an endpoint before submitting a transfer. +// If caller does not make any transfer, it must release endpoint for others. +bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr); + +// Release claimed endpoint without submitting a transfer +bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr); + +// Check if endpoint is busy transferring +bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr); + +// Stall endpoint +void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr); + +// Clear stalled endpoint +void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr); + +// Check if endpoint is stalled +bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr); + +// Allocate packet buffer used by ISO endpoints +bool usbd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size); + +// Configure and enable an ISO endpoint according to descriptor +bool usbd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc); + +// Check if endpoint is ready (not busy and not stalled) +TU_ATTR_ALWAYS_INLINE static inline +bool usbd_edpt_ready(uint8_t rhport, uint8_t ep_addr) { + return !usbd_edpt_busy(rhport, ep_addr) && !usbd_edpt_stalled(rhport, ep_addr); +} + +// Enable SOF interrupt +void usbd_sof_enable(uint8_t rhport, bool en); + +/*------------------------------------------------------------------*/ +/* Helper + *------------------------------------------------------------------*/ + +bool usbd_open_edpt_pair(uint8_t rhport, uint8_t const* p_desc, uint8_t ep_count, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in); +void usbd_defer_func(osal_task_func_t func, void *param, bool in_isr); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/test-devices/loopback-stm32/lib/tinyusb/osal/osal.h b/test-devices/loopback-stm32/lib/tinyusb/osal/osal.h new file mode 100644 index 00000000..8f45ea5c --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/osal/osal.h @@ -0,0 +1,99 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_OSAL_H_ +#define _TUSB_OSAL_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include "common/tusb_common.h" + +typedef void (*osal_task_func_t)( void * ); + +// Timeout +#define OSAL_TIMEOUT_NOTIMEOUT (0) // Return immediately +#define OSAL_TIMEOUT_NORMAL (10) // Default timeout +#define OSAL_TIMEOUT_WAIT_FOREVER (UINT32_MAX) // Wait forever +#define OSAL_TIMEOUT_CONTROL_XFER OSAL_TIMEOUT_WAIT_FOREVER + +// Mutex is required when using a preempted RTOS or MCU has multiple cores +#if (CFG_TUSB_OS == OPT_OS_NONE) && !TUP_MCU_MULTIPLE_CORE + #define OSAL_MUTEX_REQUIRED 0 + #define OSAL_MUTEX_DEF(_name) uint8_t :0 +#else + #define OSAL_MUTEX_REQUIRED 1 + #define OSAL_MUTEX_DEF(_name) osal_mutex_def_t _name +#endif + +// OS thin implementation +#if CFG_TUSB_OS == OPT_OS_NONE + #include "osal_none.h" +#elif CFG_TUSB_OS == OPT_OS_FREERTOS + #include "osal_freertos.h" +#elif CFG_TUSB_OS == OPT_OS_MYNEWT + #include "osal_mynewt.h" +#elif CFG_TUSB_OS == OPT_OS_PICO + #include "osal_pico.h" +#elif CFG_TUSB_OS == OPT_OS_RTTHREAD + #include "osal_rtthread.h" +#elif CFG_TUSB_OS == OPT_OS_RTX4 + #include "osal_rtx4.h" +#elif CFG_TUSB_OS == OPT_OS_CUSTOM + #include "tusb_os_custom.h" // implemented by application +#else + #error OS is not supported yet +#endif + +//--------------------------------------------------------------------+ +// OSAL Porting API +// Should be implemented as static inline function in osal_port.h header +/* + osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef); + bool osal_semaphore_delete(osal_semaphore_t semd_hdl); + bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr); + bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec); + void osal_semaphore_reset(osal_semaphore_t sem_hdl); // TODO removed + + osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef); + bool osal_mutex_delete(osal_mutex_t mutex_hdl) + bool osal_mutex_lock (osal_mutex_t sem_hdl, uint32_t msec); + bool osal_mutex_unlock(osal_mutex_t mutex_hdl); + + osal_queue_t osal_queue_create(osal_queue_def_t* qdef); + bool osal_queue_delete(osal_queue_t qhdl); + bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec); + bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr); + bool osal_queue_empty(osal_queue_t qhdl); +*/ +//--------------------------------------------------------------------+ + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_OSAL_H_ */ diff --git a/test-devices/loopback-stm32/lib/tinyusb/osal/osal_none.h b/test-devices/loopback-stm32/lib/tinyusb/osal/osal_none.h new file mode 100644 index 00000000..c93f7a86 --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/osal/osal_none.h @@ -0,0 +1,196 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef TUSB_OSAL_NONE_H_ +#define TUSB_OSAL_NONE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +//--------------------------------------------------------------------+ +// TASK API +//--------------------------------------------------------------------+ + +#if CFG_TUH_ENABLED +// currently only needed/available in host mode +TU_ATTR_WEAK void osal_task_delay(uint32_t msec); +#endif + +//--------------------------------------------------------------------+ +// Binary Semaphore API +//--------------------------------------------------------------------+ +typedef struct { + volatile uint16_t count; +} osal_semaphore_def_t; + +typedef osal_semaphore_def_t* osal_semaphore_t; + +TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef) { + semdef->count = 0; + return semdef; +} + +TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_delete(osal_semaphore_t semd_hdl) { + (void) semd_hdl; + return true; // nothing to do +} + + +TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) { + (void) in_isr; + sem_hdl->count++; + return true; +} + +// TODO blocking for now +TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec) { + (void) msec; + + while (sem_hdl->count == 0) {} + sem_hdl->count--; + + return true; +} + +TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl) { + sem_hdl->count = 0; +} + +//--------------------------------------------------------------------+ +// MUTEX API +// Within tinyusb, mutex is never used in ISR context +//--------------------------------------------------------------------+ +typedef osal_semaphore_def_t osal_mutex_def_t; +typedef osal_semaphore_t osal_mutex_t; + +#if OSAL_MUTEX_REQUIRED +// Note: multiple cores MCUs usually do provide IPC API for mutex +// or we can use std atomic function + +TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef) { + mdef->count = 1; + return mdef; +} + +TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_delete(osal_mutex_t mutex_hdl) { + (void) mutex_hdl; + return true; // nothing to do +} + +TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock (osal_mutex_t mutex_hdl, uint32_t msec) { + return osal_semaphore_wait(mutex_hdl, msec); +} + +TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl) { + return osal_semaphore_post(mutex_hdl, false); +} + +#else + +#define osal_mutex_create(_mdef) (NULL) +#define osal_mutex_lock(_mutex_hdl, _ms) (true) +#define osal_mutex_unlock(_mutex_hdl) (true) + +#endif + +//--------------------------------------------------------------------+ +// QUEUE API +//--------------------------------------------------------------------+ +#include "common/tusb_fifo.h" + +typedef struct { + void (* interrupt_set)(bool); + tu_fifo_t ff; +} osal_queue_def_t; + +typedef osal_queue_def_t* osal_queue_t; + +// _int_set is used as mutex in OS NONE (disable/enable USB ISR) +#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \ + uint8_t _name##_buf[_depth*sizeof(_type)]; \ + osal_queue_def_t _name = { \ + .interrupt_set = _int_set, \ + .ff = TU_FIFO_INIT(_name##_buf, _depth, _type, false) \ + } + +// lock queue by disable USB interrupt +TU_ATTR_ALWAYS_INLINE static inline void _osal_q_lock(osal_queue_t qhdl) { + // disable dcd/hcd interrupt + qhdl->interrupt_set(false); +} + +// unlock queue +TU_ATTR_ALWAYS_INLINE static inline void _osal_q_unlock(osal_queue_t qhdl) { + // enable dcd/hcd interrupt + qhdl->interrupt_set(true); +} + +TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) { + tu_fifo_clear(&qdef->ff); + return (osal_queue_t) qdef; +} + +TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_delete(osal_queue_t qhdl) { + (void) qhdl; + return true; // nothing to do +} + +TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec) { + (void) msec; // not used, always behave as msec = 0 + + _osal_q_lock(qhdl); + bool success = tu_fifo_read(&qhdl->ff, data); + _osal_q_unlock(qhdl); + + return success; +} + +TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const* data, bool in_isr) { + if (!in_isr) { + _osal_q_lock(qhdl); + } + + bool success = tu_fifo_write(&qhdl->ff, data); + + if (!in_isr) { + _osal_q_unlock(qhdl); + } + + return success; +} + +TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl) { + // Skip queue lock/unlock since this function is primarily called + // with interrupt disabled before going into low power mode + return tu_fifo_empty(&qhdl->ff); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test-devices/loopback-stm32/lib/tinyusb/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/test-devices/loopback-stm32/lib/tinyusb/portable/st/stm32_fsdev/dcd_stm32_fsdev.c new file mode 100644 index 00000000..a26c6689 --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/portable/st/stm32_fsdev/dcd_stm32_fsdev.c @@ -0,0 +1,1383 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Nathan Conrad + * + * Portions: + * Copyright (c) 2016 STMicroelectronics + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * Copyright (c) 2022 Simon Küppers (skuep) + * Copyright (c) 2022 HiFiPhile + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +/********************************************** + * This driver has been tested with the following MCUs: + * - F070, F072, L053, F042F6 + * + * It also should work with minimal changes for any ST MCU with an "USB A"/"PCD"/"HCD" peripheral. This + * covers: + * + * F04x, F072, F078, 070x6/B 1024 byte buffer + * F102, F103 512 byte buffer; no internal D+ pull-up (maybe many more changes?) + * F302xB/C, F303xB/C, F373 512 byte buffer; no internal D+ pull-up + * F302x6/8, F302xD/E2, F303xD/E 1024 byte buffer; no internal D+ pull-up + * L0x2, L0x3 1024 byte buffer + * L1 512 byte buffer + * L4x2, L4x3 1024 byte buffer + * G0 2048 byte buffer + * + * To use this driver, you must: + * - If you are using a device with crystal-less USB, set up the clock recovery system (CRS) + * - Remap pins to be D+/D- on devices that they are shared (for example: F042Fx) + * - This is different to the normal "alternate function" GPIO interface, needs to go through SYSCFG->CFGRx register + * - Enable USB clock; Perhaps use __HAL_RCC_USB_CLK_ENABLE(); + * - (Optionally configure GPIO HAL to tell it the USB driver is using the USB pins) + * - call tusb_init(); + * - periodically call tusb_task(); + * + * Assumptions of the driver: + * - You are not using CAN (it must share the packet buffer) + * - APB clock is >= 10 MHz + * - On some boards, series resistors are required, but not on others. + * - On some boards, D+ pull up resistor (1.5kohm) is required, but not on others. + * - You don't have long-running interrupts; some USB packets must be quickly responded to. + * - You have the ST CMSIS library linked into the project. HAL is not used. + * + * Current driver limitations (i.e., a list of features for you to add): + * - STALL handled, but not tested. + * - Does it work? No clue. + * - All EP BTABLE buffers are created based on max packet size of first EP opened with that address. + * - Packet buffer memory is copied in the interrupt. + * - This is better for performance, but means interrupts are disabled for longer + * - DMA may be the best choice, but it could also be pushed to the USBD task. + * - No double-buffering + * - No DMA + * - Minimal error handling + * - Perhaps error interrupts should be reported to the stack, or cause a device reset? + * - Assumes a single USB peripheral; I think that no hardware has multiple so this is fine. + * - Add a callback for enabling/disabling the D+ PU on devices without an internal PU. + * - F3 models use three separate interrupts. I think we could only use the LP interrupt for + * everything? However, the interrupts are configurable so the DisableInt and EnableInt + * below functions could be adjusting the wrong interrupts (if they had been reconfigured) + * - LPM is not used correctly, or at all? + * + * USB documentation and Reference implementations + * - STM32 Reference manuals + * - STM32 USB Hardware Guidelines AN4879 + * + * - STM32 HAL (much of this driver is based on this) + * - libopencm3/lib/stm32/common/st_usbfs_core.c + * - Keil USB Device http://www.keil.com/pack/doc/mw/USB/html/group__usbd.html + * + * - YouTube OpenTechLab 011; https://www.youtube.com/watch?v=4FOkJLp_PUw + * + * Advantages over HAL driver: + * - Tiny (saves RAM, assumes a single USB peripheral) + * + * Notes: + * - The buffer table is allocated as endpoints are opened. The allocation is only + * cleared when the device is reset. This may be bad if the USB device needs + * to be reconfigured. + */ + +#include "tusb_option.h" + +#if CFG_TUD_ENABLED && defined(TUP_USBIP_FSDEV) + +#include "device/dcd.h" + +#ifdef TUP_USBIP_FSDEV_STM32 +// Undefine to reduce the dependence on HAL +#undef USE_HAL_DRIVER +#include "portable/st/stm32_fsdev/dcd_stm32_fsdev.h" +#endif + +/***************************************************** + * Configuration + *****************************************************/ + +// HW supports max of 8 bidirectional endpoints, but this can be reduced to save RAM +// (8u here would mean 8 IN and 8 OUT) +#ifndef MAX_EP_COUNT +#define MAX_EP_COUNT 8U +#endif + +// If sharing with CAN, one can set this to be non-zero to give CAN space where it wants it +// Both of these MUST be a multiple of 2, and are in byte units. +#ifndef DCD_STM32_BTABLE_BASE +#define DCD_STM32_BTABLE_BASE 0U +#endif + +#ifndef DCD_STM32_BTABLE_SIZE +#define DCD_STM32_BTABLE_SIZE (FSDEV_PMA_SIZE - DCD_STM32_BTABLE_BASE) +#endif + +/*************************************************** + * Checks, structs, defines, function definitions, etc. + */ + +TU_VERIFY_STATIC((MAX_EP_COUNT) <= STFSDEV_EP_COUNT, "Only 8 endpoints supported on the hardware"); +TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) + (DCD_STM32_BTABLE_SIZE)) <= (FSDEV_PMA_SIZE), "BTABLE does not fit in PMA RAM"); +TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) % 8) == 0, "BTABLE base must be aligned to 8 bytes"); + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +// One of these for every EP IN & OUT, uses a bit of RAM.... +typedef struct { + uint8_t *buffer; + tu_fifo_t *ff; + uint16_t total_len; + uint16_t queued_len; + uint16_t max_packet_size; + uint8_t ep_idx; // index for USB_EPnR register + bool iso_in_sending; // Workaround for ISO IN EP doesn't have interrupt mask +} xfer_ctl_t; + +// EP allocator +typedef struct { + uint8_t ep_num; + uint8_t ep_type; + bool allocated[2]; +} ep_alloc_t; + +static xfer_ctl_t xfer_status[MAX_EP_COUNT][2]; + +static ep_alloc_t ep_alloc_status[STFSDEV_EP_COUNT]; + +static TU_ATTR_ALIGNED(4) uint32_t _setup_packet[6]; + +static uint8_t remoteWakeCountdown; // When wake is requested + +//--------------------------------------------------------------------+ +// Prototypes +//--------------------------------------------------------------------+ + +// into the stack. +static void dcd_handle_bus_reset(void); +static void dcd_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix); +static bool edpt_xfer(uint8_t rhport, uint8_t ep_addr); +static void dcd_ep_ctr_handler(void); + +// PMA allocation/access +static uint16_t ep_buf_ptr; ///< Points to first free memory location +static uint32_t dcd_pma_alloc(uint16_t length, bool dbuf); +static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type); +static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes); +static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes); + +static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNBytes); +static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBytes); + +//--------------------------------------------------------------------+ +// Inline helper +//--------------------------------------------------------------------+ + +TU_ATTR_ALWAYS_INLINE static inline xfer_ctl_t *xfer_ctl_ptr(uint32_t ep_addr) +{ + uint8_t epnum = tu_edpt_number(ep_addr); + uint8_t dir = tu_edpt_dir(ep_addr); + // Fix -Werror=null-dereference + TU_ASSERT(epnum < MAX_EP_COUNT, &xfer_status[0][0]); + + return &xfer_status[epnum][dir]; +} + +//--------------------------------------------------------------------+ +// Controller API +//--------------------------------------------------------------------+ + +void dcd_init(uint8_t rhport) +{ + /* Clocks should already be enabled */ + /* Use __HAL_RCC_USB_CLK_ENABLE(); to enable the clocks before calling this function */ + + /* The RM mentions to use a special ordering of PDWN and FRES, but this isn't done in HAL. + * Here, the RM is followed. */ + + for (uint32_t i = 0; i < 200; i++) { // should be a few us + asm("NOP"); + } + // Perform USB peripheral reset + USB->CNTR = USB_CNTR_FRES | USB_CNTR_PDWN; + for (uint32_t i = 0; i < 200; i++) { // should be a few us + asm("NOP"); + } + + USB->CNTR &= ~USB_CNTR_PDWN; + + // Wait startup time, for F042 and F070, this is <= 1 us. + for (uint32_t i = 0; i < 200; i++) { // should be a few us + asm("NOP"); + } + USB->CNTR = 0; // Enable USB + +#if !defined(STM32G0) && !defined(STM32H5) // BTABLE register does not exist any more on STM32G0, it is fixed to USB SRAM base address + USB->BTABLE = DCD_STM32_BTABLE_BASE; +#endif + USB->ISTR = 0; // Clear pending interrupts + + // Reset endpoints to disabled + for (uint32_t i = 0; i < STFSDEV_EP_COUNT; i++) { + // This doesn't clear all bits since some bits are "toggle", but does set the type to DISABLED. + pcd_set_endpoint(USB, i, 0u); + } + + USB->CNTR |= USB_CNTR_RESETM | USB_CNTR_ESOFM | USB_CNTR_CTRM | USB_CNTR_SUSPM | USB_CNTR_WKUPM; + dcd_handle_bus_reset(); + + // Enable pull-up if supported + if (dcd_connect) { + dcd_connect(rhport); + } +} + +// Define only on MCU with internal pull-up. BSP can define on MCU without internal PU. +#if defined(USB_BCDR_DPPU) + +// Disable internal D+ PU +void dcd_disconnect(uint8_t rhport) +{ + (void)rhport; + USB->BCDR &= ~(USB_BCDR_DPPU); +} + +// Enable internal D+ PU +void dcd_connect(uint8_t rhport) +{ + (void)rhport; + USB->BCDR |= USB_BCDR_DPPU; +} + +#elif defined(SYSCFG_PMC_USB_PU) // works e.g. on STM32L151 +// Disable internal D+ PU +void dcd_disconnect(uint8_t rhport) +{ + (void)rhport; + SYSCFG->PMC &= ~(SYSCFG_PMC_USB_PU); +} + +// Enable internal D+ PU +void dcd_connect(uint8_t rhport) +{ + (void)rhport; + SYSCFG->PMC |= SYSCFG_PMC_USB_PU; +} +#endif + +void dcd_sof_enable(uint8_t rhport, bool en) +{ + (void)rhport; + (void)en; + + if (en) { + USB->CNTR |= USB_CNTR_SOFM; + } else { + USB->CNTR &= ~USB_CNTR_SOFM; + } +} + +// Enable device interrupt +void dcd_int_enable(uint8_t rhport) +{ + (void)rhport; + // Member here forces write to RAM before allowing ISR to execute + __DSB(); + __ISB(); +#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0 || CFG_TUSB_MCU == OPT_MCU_STM32L4 + NVIC_EnableIRQ(USB_IRQn); + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L1 + NVIC_EnableIRQ(USB_LP_IRQn); + +#elif CFG_TUSB_MCU == OPT_MCU_STM32F3 +// Some STM32F302/F303 devices allow to remap the USB interrupt vectors from +// shared USB/CAN IRQs to separate CAN and USB IRQs. +// This dynamically checks if this remap is active to enable the right IRQs. +#ifdef SYSCFG_CFGR1_USB_IT_RMP + if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) { + NVIC_EnableIRQ(USB_HP_IRQn); + NVIC_EnableIRQ(USB_LP_IRQn); + NVIC_EnableIRQ(USBWakeUp_RMP_IRQn); + } else +#endif + { + NVIC_EnableIRQ(USB_HP_CAN_TX_IRQn); + NVIC_EnableIRQ(USB_LP_CAN_RX0_IRQn); + NVIC_EnableIRQ(USBWakeUp_IRQn); + } +#elif CFG_TUSB_MCU == OPT_MCU_STM32F1 + NVIC_EnableIRQ(USB_HP_CAN1_TX_IRQn); + NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn); + NVIC_EnableIRQ(USBWakeUp_IRQn); + +#elif CFG_TUSB_MCU == OPT_MCU_STM32G4 + NVIC_EnableIRQ(USB_HP_IRQn); + NVIC_EnableIRQ(USB_LP_IRQn); + NVIC_EnableIRQ(USBWakeUp_IRQn); + +#elif CFG_TUSB_MCU == OPT_MCU_STM32G0 +#ifdef STM32G0B0xx + NVIC_EnableIRQ(USB_IRQn); +#else + NVIC_EnableIRQ(USB_UCPD1_2_IRQn); +#endif + +#elif CFG_TUSB_MCU == OPT_MCU_STM32H5 + NVIC_EnableIRQ(USB_DRD_FS_IRQn); + +#elif CFG_TUSB_MCU == OPT_MCU_STM32WB + NVIC_EnableIRQ(USB_HP_IRQn); + NVIC_EnableIRQ(USB_LP_IRQn); + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L5 + NVIC_EnableIRQ(USB_FS_IRQn); + +#else +#error Unknown arch in USB driver +#endif +} + +// Disable device interrupt +void dcd_int_disable(uint8_t rhport) +{ + (void)rhport; + +#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0 || CFG_TUSB_MCU == OPT_MCU_STM32L4 + NVIC_DisableIRQ(USB_IRQn); +#elif CFG_TUSB_MCU == OPT_MCU_STM32L1 + NVIC_DisableIRQ(USB_LP_IRQn); +#elif CFG_TUSB_MCU == OPT_MCU_STM32F3 +// Some STM32F302/F303 devices allow to remap the USB interrupt vectors from +// shared USB/CAN IRQs to separate CAN and USB IRQs. +// This dynamically checks if this remap is active to disable the right IRQs. +#ifdef SYSCFG_CFGR1_USB_IT_RMP + if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) { + NVIC_DisableIRQ(USB_HP_IRQn); + NVIC_DisableIRQ(USB_LP_IRQn); + NVIC_DisableIRQ(USBWakeUp_RMP_IRQn); + } else +#endif + { + NVIC_DisableIRQ(USB_HP_CAN_TX_IRQn); + NVIC_DisableIRQ(USB_LP_CAN_RX0_IRQn); + NVIC_DisableIRQ(USBWakeUp_IRQn); + } +#elif CFG_TUSB_MCU == OPT_MCU_STM32F1 + NVIC_DisableIRQ(USB_HP_CAN1_TX_IRQn); + NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn); + NVIC_DisableIRQ(USBWakeUp_IRQn); + +#elif CFG_TUSB_MCU == OPT_MCU_STM32G4 + NVIC_DisableIRQ(USB_HP_IRQn); + NVIC_DisableIRQ(USB_LP_IRQn); + NVIC_DisableIRQ(USBWakeUp_IRQn); + +#elif CFG_TUSB_MCU == OPT_MCU_STM32G0 +#ifdef STM32G0B0xx + NVIC_DisableIRQ(USB_IRQn); +#else + NVIC_DisableIRQ(USB_UCPD1_2_IRQn); +#endif + +#elif CFG_TUSB_MCU == OPT_MCU_STM32H5 + NVIC_DisableIRQ(USB_DRD_FS_IRQn); + +#elif CFG_TUSB_MCU == OPT_MCU_STM32WB + NVIC_DisableIRQ(USB_HP_IRQn); + NVIC_DisableIRQ(USB_LP_IRQn); + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L5 + NVIC_DisableIRQ(USB_FS_IRQn); + +#else +#error Unknown arch in USB driver +#endif + + // CMSIS has a membar after disabling interrupts +} + +// Receive Set Address request, mcu port must also include status IN response +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) +{ + (void)rhport; + (void)dev_addr; + + // Respond with status + dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK | 0x00, NULL, 0); + + // DCD can only set address after status for this request is complete. + // do it at dcd_edpt0_status_complete() +} + +void dcd_remote_wakeup(uint8_t rhport) +{ + (void)rhport; + + USB->CNTR |= USB_CNTR_RESUME; + remoteWakeCountdown = 4u; // required to be 1 to 15 ms, ESOF should trigger every 1ms. +} + +static const tusb_desc_endpoint_t ep0OUT_desc = { + .bLength = sizeof(tusb_desc_endpoint_t), + .bDescriptorType = TUSB_DESC_ENDPOINT, + .bEndpointAddress = 0x00, + .bmAttributes = {.xfer = TUSB_XFER_CONTROL}, + .wMaxPacketSize = CFG_TUD_ENDPOINT0_SIZE, + .bInterval = 0 +}; + +static const tusb_desc_endpoint_t ep0IN_desc = { + .bLength = sizeof(tusb_desc_endpoint_t), + .bDescriptorType = TUSB_DESC_ENDPOINT, + .bEndpointAddress = 0x80, + .bmAttributes = {.xfer = TUSB_XFER_CONTROL}, + .wMaxPacketSize = CFG_TUD_ENDPOINT0_SIZE, + .bInterval = 0 +}; + +static void dcd_handle_bus_reset(void) +{ + USB->DADDR = 0u; // disable USB peripheral by clearing the EF flag + + for (uint32_t i = 0; i < STFSDEV_EP_COUNT; i++) { + // Clear EP allocation status + ep_alloc_status[i].ep_num = 0xFF; + ep_alloc_status[i].ep_type = 0xFF; + ep_alloc_status[i].allocated[0] = false; + ep_alloc_status[i].allocated[1] = false; + } + + // Reset PMA allocation + ep_buf_ptr = DCD_STM32_BTABLE_BASE + 8 * MAX_EP_COUNT; + + dcd_edpt_open(0, &ep0OUT_desc); + dcd_edpt_open(0, &ep0IN_desc); + + USB->DADDR = USB_DADDR_EF; // Set enable flag, and leaving the device address as zero. +} + +// Handle CTR interrupt for the TX/IN direction +// +// Upon call, (wIstr & USB_ISTR_DIR) == 0U +static void dcd_ep_ctr_tx_handler(uint32_t wIstr) +{ + uint32_t EPindex = wIstr & USB_ISTR_EP_ID; + uint32_t wEPRegVal = pcd_get_endpoint(USB, EPindex); + uint8_t ep_addr = (wEPRegVal & USB_EPADDR_FIELD) | TUSB_DIR_IN_MASK; + + // Verify the CTR_TX bit is set. This was in the ST Micro code, + // but I'm not sure it's actually necessary? + if ((wEPRegVal & USB_EP_CTR_TX) == 0U) { + return; + } + + /* clear int flag */ + pcd_clear_tx_ep_ctr(USB, EPindex); + + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + + if ((wEPRegVal & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS) { + // Ignore spurious interrupts that we don't schedule + // host can send IN token while there is no data to send, since ISO does not have NAK + // this will result to zero length packet --> trigger interrupt (which cannot be masked) + if (!xfer->iso_in_sending) { + return; + } + xfer->iso_in_sending = false; + + if (wEPRegVal & USB_EP_DTOG_TX) { + pcd_set_ep_tx_dbuf0_cnt(USB, EPindex, 0); + } else { + pcd_set_ep_tx_dbuf1_cnt(USB, EPindex, 0); + } + } + + if ((xfer->total_len != xfer->queued_len)) { + dcd_transmit_packet(xfer, EPindex); + } else { + dcd_event_xfer_complete(0, ep_addr, xfer->total_len, XFER_RESULT_SUCCESS, true); + } +} + +// Handle CTR interrupt for the RX/OUT direction +// Upon call, (wIstr & USB_ISTR_DIR) == 0U +static void dcd_ep_ctr_rx_handler(uint32_t wIstr) +{ +#ifdef FSDEV_BUS_32BIT + /* https://www.st.com/resource/en/errata_sheet/es0561-stm32h503cbebkbrb-device-errata-stmicroelectronics.pdf + * From STM32H503 errata 2.15.1: Buffer description table update completes after CTR interrupt triggers + * Description: + * - During OUT transfers, the correct transfer interrupt (CTR) is triggered a little before the last USB SRAM accesses + * have completed. If the software responds quickly to the interrupt, the full buffer contents may not be correct. + * Workaround: + * - Software should ensure that a small delay is included before accessing the SRAM contents. This delay + * should be 800 ns in Full Speed mode and 6.4 μs in Low Speed mode + * - Since H5 can run up to 250Mhz -> 1 cycle = 4ns. Per errata, we need to wait 200 cycles. Though executing code + * also takes time, so we'll wait 60 cycles (count = 20). + * - Since Low Speed mode is not supported/popular, we will ignore it for now. + * + * Note: this errata also seems to apply to G0, U5, H5 etc. + */ + volatile uint32_t cycle_count = 20; // defined as PCD_RX_PMA_CNT in stm32 hal_driver + while (cycle_count > 0U) { + cycle_count--; // each count take 3 cycles (1 for sub, jump, and compare) + } +#endif + + uint32_t EPindex = wIstr & USB_ISTR_EP_ID; + uint32_t wEPRegVal = pcd_get_endpoint(USB, EPindex); + uint8_t ep_addr = wEPRegVal & USB_EPADDR_FIELD; + + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + + // Verify the CTR_RX bit is set. This was in the ST Micro code, + // but I'm not sure it's actually necessary? + if ((wEPRegVal & USB_EP_CTR_RX) == 0U) { + return; + } + + if ((ep_addr == 0U) && ((wEPRegVal & USB_EP_SETUP) != 0U)) { + /* Setup packet */ + uint32_t count = pcd_get_ep_rx_cnt(USB, EPindex); + // Setup packet should always be 8 bytes. If not, ignore it, and try again. + if (count == 8) { + // Must reset EP to NAK (in case it had been stalling) (though, maybe too late here) + pcd_set_ep_rx_status(USB, 0u, USB_EP_RX_NAK); + pcd_set_ep_tx_status(USB, 0u, USB_EP_TX_NAK); +#ifdef FSDEV_BUS_32BIT + dcd_event_setup_received(0, (uint8_t *)(USB_PMAADDR + pcd_get_ep_rx_address(USB, EPindex)), true); +#else + // The setup_received function uses memcpy, so this must first copy the setup data into + // user memory, to allow for the 32-bit access that memcpy performs. + uint8_t userMemBuf[8]; + dcd_read_packet_memory(userMemBuf, pcd_get_ep_rx_address(USB, EPindex), 8); + dcd_event_setup_received(0, (uint8_t *)userMemBuf, true); +#endif + } + } else { + // Clear RX CTR interrupt flag + if (ep_addr != 0u) { + pcd_clear_rx_ep_ctr(USB, EPindex); + } + + uint32_t count; + uint16_t addr; + /* Read from correct register when ISOCHRONOUS (double buffered) */ + if ((wEPRegVal & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS) { + if (wEPRegVal & USB_EP_DTOG_RX) { + count = pcd_get_ep_dbuf0_cnt(USB, EPindex); + addr = pcd_get_ep_dbuf0_address(USB, EPindex); + } else { + count = pcd_get_ep_dbuf1_cnt(USB, EPindex); + addr = pcd_get_ep_dbuf1_address(USB, EPindex); + } + } else { + count = pcd_get_ep_rx_cnt(USB, EPindex); + addr = pcd_get_ep_rx_address(USB, EPindex); + } + + TU_ASSERT(count <= xfer->max_packet_size, /**/); + + if (count != 0U) { + if (xfer->ff) { + dcd_read_packet_memory_ff(xfer->ff, addr, count); + } else { + dcd_read_packet_memory(&(xfer->buffer[xfer->queued_len]), addr, count); + } + + xfer->queued_len = (uint16_t)(xfer->queued_len + count); + } + + if ((count < xfer->max_packet_size) || (xfer->queued_len == xfer->total_len)) { + // all bytes received or short packet + dcd_event_xfer_complete(0, ep_addr, xfer->queued_len, XFER_RESULT_SUCCESS, true); + } else { + /* Set endpoint active again for receiving more data. + * Note that isochronous endpoints stay active always */ + if ((wEPRegVal & USB_EP_TYPE_MASK) != USB_EP_ISOCHRONOUS) { + uint16_t remaining = xfer->total_len - xfer->queued_len; + uint16_t cnt = tu_min16(remaining, xfer->max_packet_size); + pcd_set_ep_rx_cnt(USB, EPindex, cnt); + } + pcd_set_ep_rx_status(USB, EPindex, USB_EP_RX_VALID); + } + } + + // For EP0, prepare to receive another SETUP packet. + // Clear CTR last so that a new packet does not overwrite the packing being read. + // (Based on the docs, it seems SETUP will always be accepted after CTR is cleared) + if (ep_addr == 0u) { + // Always be prepared for a status packet... + pcd_set_ep_rx_cnt(USB, EPindex, CFG_TUD_ENDPOINT0_SIZE); + pcd_clear_rx_ep_ctr(USB, EPindex); + } +} + +static void dcd_ep_ctr_handler(void) +{ + uint32_t wIstr; + + /* stay in loop while pending interrupts */ + while (((wIstr = USB->ISTR) & USB_ISTR_CTR) != 0U) { + if ((wIstr & USB_ISTR_DIR) == 0U) { + /* TX/IN */ + dcd_ep_ctr_tx_handler(wIstr); + } else { + /* RX/OUT*/ + dcd_ep_ctr_rx_handler(wIstr); + } + } +} + +void dcd_int_handler(uint8_t rhport) +{ + + (void)rhport; + + uint32_t int_status = USB->ISTR; + // const uint32_t handled_ints = USB_ISTR_CTR | USB_ISTR_RESET | USB_ISTR_WKUP + // | USB_ISTR_SUSP | USB_ISTR_SOF | USB_ISTR_ESOF; + // unused IRQs: (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_L1REQ ) + + // The ST driver loops here on the CTR bit, but that loop has been moved into the + // dcd_ep_ctr_handler(), so less need to loop here. The other interrupts shouldn't + // be triggered repeatedly. + + /* Put SOF flag at the beginning of ISR in case to get least amount of jitter if it is used for timing purposes */ + if (int_status & USB_ISTR_SOF) { + USB->ISTR = (fsdev_bus_t)~USB_ISTR_SOF; + dcd_event_sof(0, USB->FNR & USB_FNR_FN, true); + } + + if (int_status & USB_ISTR_RESET) { + // USBRST is start of reset. + USB->ISTR = (fsdev_bus_t)~USB_ISTR_RESET; + dcd_handle_bus_reset(); + dcd_event_bus_reset(0, TUSB_SPEED_FULL, true); + return; // Don't do the rest of the things here; perhaps they've been cleared? + } + + if (int_status & USB_ISTR_CTR) { + /* servicing of the endpoint correct transfer interrupt */ + /* clear of the CTR flag into the sub */ + dcd_ep_ctr_handler(); + } + + if (int_status & USB_ISTR_WKUP) { + USB->CNTR &= ~USB_CNTR_LPMODE; + USB->CNTR &= ~USB_CNTR_FSUSP; + + USB->ISTR = (fsdev_bus_t)~USB_ISTR_WKUP; + dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); + } + + if (int_status & USB_ISTR_SUSP) { + /* Suspend is asserted for both suspend and unplug events. without Vbus monitoring, + * these events cannot be differentiated, so we only trigger suspend. */ + + /* Force low-power mode in the macrocell */ + USB->CNTR |= USB_CNTR_FSUSP; + USB->CNTR |= USB_CNTR_LPMODE; + + /* clear of the ISTR bit must be done after setting of CNTR_FSUSP */ + USB->ISTR = (fsdev_bus_t)~USB_ISTR_SUSP; + dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true); + } + + if (int_status & USB_ISTR_ESOF) { + if (remoteWakeCountdown == 1u) { + USB->CNTR &= ~USB_CNTR_RESUME; + } + if (remoteWakeCountdown > 0u) { + remoteWakeCountdown--; + } + USB->ISTR = (fsdev_bus_t)~USB_ISTR_ESOF; + } +} + +//--------------------------------------------------------------------+ +// Endpoint API +//--------------------------------------------------------------------+ + +// Invoked when a control transfer's status stage is complete. +// May help DCD to prepare for next control transfer, this API is optional. +void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const *request) +{ + (void)rhport; + + if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && + request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && + request->bRequest == TUSB_REQ_SET_ADDRESS) { + uint8_t const dev_addr = (uint8_t)request->wValue; + + // Setting new address after the whole request is complete + USB->DADDR &= ~USB_DADDR_ADD; + USB->DADDR |= dev_addr; // leave the enable bit set + } +} + +/*** + * Allocate a section of PMA + * In case of double buffering, high 16bit is the address of 2nd buffer + * During failure, TU_ASSERT is used. If this happens, rework/reallocate memory manually. + */ +static uint32_t dcd_pma_alloc(uint16_t length, bool dbuf) +{ + // Ensure allocated buffer is aligned +#ifdef FSDEV_BUS_32BIT + length = (length + 3) & ~0x03; +#else + length = (length + 1) & ~0x01; +#endif + + uint32_t addr = ep_buf_ptr; + ep_buf_ptr = (uint16_t)(ep_buf_ptr + length); // increment buffer pointer + + if (dbuf) { + addr |= ((uint32_t)ep_buf_ptr) << 16; + ep_buf_ptr = (uint16_t)(ep_buf_ptr + length); // increment buffer pointer + } + + // Verify packet buffer is not overflowed + TU_ASSERT(ep_buf_ptr <= FSDEV_PMA_SIZE, 0xFFFF); + + return addr; +} + +/*** + * Allocate hardware endpoint + */ +static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type) +{ + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + for (uint8_t i = 0; i < STFSDEV_EP_COUNT; i++) { + // Check if already allocated + if (ep_alloc_status[i].allocated[dir] && + ep_alloc_status[i].ep_type == ep_type && + ep_alloc_status[i].ep_num == epnum) { + return i; + } + + // If EP of current direction is not allocated + // Except for ISO endpoint, both direction should be free + if (!ep_alloc_status[i].allocated[dir] && + (ep_type != TUSB_XFER_ISOCHRONOUS || !ep_alloc_status[i].allocated[dir ^ 1])) { + // Check if EP number is the same + if (ep_alloc_status[i].ep_num == 0xFF || ep_alloc_status[i].ep_num == epnum) { + // One EP pair has to be the same type + if (ep_alloc_status[i].ep_type == 0xFF || ep_alloc_status[i].ep_type == ep_type) { + ep_alloc_status[i].ep_num = epnum; + ep_alloc_status[i].ep_type = ep_type; + ep_alloc_status[i].allocated[dir] = true; + + return i; + } + } + } + } + + // Allocation failed + TU_ASSERT(0); +} + +// The STM32F0 doesn't seem to like |= or &= to manipulate the EP#R registers, +// so I'm using the #define from HAL here, instead. + +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *p_endpoint_desc) +{ + (void)rhport; + uint8_t const ep_addr = p_endpoint_desc->bEndpointAddress; + uint8_t const ep_idx = dcd_ep_alloc(ep_addr, p_endpoint_desc->bmAttributes.xfer); + uint8_t const dir = tu_edpt_dir(ep_addr); + const uint16_t packet_size = tu_edpt_packet_size(p_endpoint_desc); + const uint16_t buffer_size = pcd_aligned_buffer_size(packet_size); + uint16_t pma_addr; + uint32_t wType; + + TU_ASSERT(ep_idx < STFSDEV_EP_COUNT); + TU_ASSERT(buffer_size <= 64); + + // Set type + switch (p_endpoint_desc->bmAttributes.xfer) { + case TUSB_XFER_CONTROL: + wType = USB_EP_CONTROL; + break; + case TUSB_XFER_BULK: + wType = USB_EP_CONTROL; + break; + + case TUSB_XFER_INTERRUPT: + wType = USB_EP_INTERRUPT; + break; + + default: + // Note: ISO endpoint should use alloc / active functions + TU_ASSERT(false); + } + + pcd_set_eptype(USB, ep_idx, wType); + pcd_set_ep_address(USB, ep_idx, tu_edpt_number(ep_addr)); + + /* Create a packet memory buffer area. */ + pma_addr = dcd_pma_alloc(buffer_size, false); + + if (dir == TUSB_DIR_IN) { + pcd_set_ep_tx_address(USB, ep_idx, pma_addr); + pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_NAK); + pcd_clear_tx_dtog(USB, ep_idx); + } else { + pcd_set_ep_rx_address(USB, ep_idx, pma_addr); + pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_NAK); + pcd_clear_rx_dtog(USB, ep_idx); + } + + xfer_ctl_ptr(ep_addr)->max_packet_size = packet_size; + xfer_ctl_ptr(ep_addr)->ep_idx = ep_idx; + + return true; +} + +void dcd_edpt_close_all(uint8_t rhport) +{ + (void)rhport; + + for (uint32_t i = 1; i < STFSDEV_EP_COUNT; i++) { + // Reset endpoint + pcd_set_endpoint(USB, i, 0); + // Clear EP allocation status + ep_alloc_status[i].ep_num = 0xFF; + ep_alloc_status[i].ep_type = 0xFF; + ep_alloc_status[i].allocated[0] = false; + ep_alloc_status[i].allocated[1] = false; + } + + // Reset PMA allocation + ep_buf_ptr = DCD_STM32_BTABLE_BASE + 8 * MAX_EP_COUNT + 2 * CFG_TUD_ENDPOINT0_SIZE; +} + +/** + * Close an endpoint. + * + * This function may be called with interrupts enabled or disabled. + * + * This also clears transfers in progress, should there be any. + */ +void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) +{ + (void)rhport; + + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + uint8_t const ep_idx = xfer->ep_idx; + uint8_t const dir = tu_edpt_dir(ep_addr); + + if (dir == TUSB_DIR_IN) { + pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_DIS); + } else { + pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_DIS); + } +} + +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) +{ + (void)rhport; + + uint8_t const ep_idx = dcd_ep_alloc(ep_addr, TUSB_XFER_ISOCHRONOUS); + const uint16_t buffer_size = pcd_aligned_buffer_size(largest_packet_size); + + /* Create a packet memory buffer area. Enable double buffering for devices with 2048 bytes PMA, + for smaller devices double buffering occupy too much space. */ +#if FSDEV_PMA_SIZE > 1024u + uint32_t pma_addr = dcd_pma_alloc(buffer_size, true); + uint16_t pma_addr2 = pma_addr >> 16; +#else + uint32_t pma_addr = dcd_pma_alloc(buffer_size, true); + uint16_t pma_addr2 = pma_addr; +#endif + pcd_set_ep_tx_address(USB, ep_idx, pma_addr); + pcd_set_ep_rx_address(USB, ep_idx, pma_addr2); + + pcd_set_eptype(USB, ep_idx, USB_EP_ISOCHRONOUS); + + xfer_ctl_ptr(ep_addr)->ep_idx = ep_idx; + + return true; +} + +bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *p_endpoint_desc) +{ + (void)rhport; + uint8_t const ep_addr = p_endpoint_desc->bEndpointAddress; + uint8_t const ep_idx = xfer_ctl_ptr(ep_addr)->ep_idx; + uint8_t const dir = tu_edpt_dir(ep_addr); + const uint16_t packet_size = tu_edpt_packet_size(p_endpoint_desc); + + pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_DIS); + pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_DIS); + + pcd_set_ep_address(USB, ep_idx, tu_edpt_number(ep_addr)); + + pcd_clear_tx_dtog(USB, ep_idx); + pcd_clear_rx_dtog(USB, ep_idx); + + if (dir == TUSB_DIR_IN) { + pcd_rx_dtog(USB, ep_idx); + } else { + pcd_tx_dtog(USB, ep_idx); + } + + xfer_ctl_ptr(ep_addr)->max_packet_size = packet_size; + + return true; +} + +// Currently, single-buffered, and only 64 bytes at a time (max) + +static void dcd_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix) +{ + uint16_t len = (uint16_t)(xfer->total_len - xfer->queued_len); + if (len > xfer->max_packet_size) { + len = xfer->max_packet_size; + } + + uint16_t ep_reg = pcd_get_endpoint(USB, ep_ix); + bool const is_iso = (ep_reg & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS; + uint16_t addr_ptr; + + if (is_iso) { + if (ep_reg & USB_EP_DTOG_TX) { + addr_ptr = pcd_get_ep_dbuf1_address(USB, ep_ix); + pcd_set_ep_tx_dbuf1_cnt(USB, ep_ix, len); + } else { + addr_ptr = pcd_get_ep_dbuf0_address(USB, ep_ix); + pcd_set_ep_tx_dbuf0_cnt(USB, ep_ix, len); + } + } else { + addr_ptr = pcd_get_ep_tx_address(USB, ep_ix); + pcd_set_ep_tx_cnt(USB, ep_ix, len); + } + + if (xfer->ff) { + dcd_write_packet_memory_ff(xfer->ff, addr_ptr, len); + } else { + dcd_write_packet_memory(addr_ptr, &(xfer->buffer[xfer->queued_len]), len); + } + xfer->queued_len = (uint16_t)(xfer->queued_len + len); + + dcd_int_disable(0); + pcd_set_ep_tx_status(USB, ep_ix, USB_EP_TX_VALID); + if (is_iso) { + xfer->iso_in_sending = true; + } + dcd_int_enable(0); +} + +static bool edpt_xfer(uint8_t rhport, uint8_t ep_addr) +{ + (void)rhport; + + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + uint8_t const ep_idx = xfer->ep_idx; + uint8_t const dir = tu_edpt_dir(ep_addr); + + if (dir == TUSB_DIR_IN) { + dcd_transmit_packet(xfer, ep_idx); + } else { + // A setup token can occur immediately after an OUT STATUS packet so make sure we have a valid + // buffer for the control endpoint. + if (ep_idx == 0 && xfer->buffer == NULL) { + xfer->buffer = (uint8_t *)_setup_packet; + } + + uint32_t cnt = (uint32_t ) tu_min16(xfer->total_len, xfer->max_packet_size); + uint16_t ep_reg = pcd_get_endpoint(USB, ep_idx); + + if ((ep_reg & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS) { + pcd_set_ep_rx_dbuf0_cnt(USB, ep_idx, cnt); + pcd_set_ep_rx_dbuf1_cnt(USB, ep_idx, cnt); + } else { + pcd_set_ep_rx_cnt(USB, ep_idx, cnt); + } + + pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_VALID); + } + + return true; +} + +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) +{ + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + + xfer->buffer = buffer; + xfer->ff = NULL; + xfer->total_len = total_bytes; + xfer->queued_len = 0; + + return edpt_xfer(rhport, ep_addr); +} + +bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t total_bytes) +{ + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + xfer->buffer = NULL; + xfer->ff = ff; + xfer->total_len = total_bytes; + xfer->queued_len = 0; + + return edpt_xfer(rhport, ep_addr); +} + +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) +{ + (void)rhport; + + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + uint8_t const ep_idx = xfer->ep_idx; + uint8_t const dir = tu_edpt_dir(ep_addr); + + if (dir == TUSB_DIR_IN) { + pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_STALL); + } else { + pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_STALL); + } +} + +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) +{ + (void)rhport; + + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + uint8_t const ep_idx = xfer->ep_idx; + uint8_t const dir = tu_edpt_dir(ep_addr); + + if (dir == TUSB_DIR_IN) { // IN + if (pcd_get_eptype(USB, ep_idx) != USB_EP_ISOCHRONOUS) { + pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_NAK); + } + + /* Reset to DATA0 if clearing stall condition. */ + pcd_clear_tx_dtog(USB, ep_idx); + } else { // OUT + if (pcd_get_eptype(USB, ep_idx) != USB_EP_ISOCHRONOUS) { + pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_NAK); + } + /* Reset to DATA0 if clearing stall condition. */ + pcd_clear_rx_dtog(USB, ep_idx); + } +} + +#ifdef FSDEV_BUS_32BIT +static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes) +{ + const uint8_t *srcVal = src; + volatile uint32_t *dst32 = (volatile uint32_t *)(USB_PMAADDR + dst); + + for (uint32_t n = wNBytes / 4; n > 0; --n) { + *dst32++ = tu_unaligned_read32(srcVal); + srcVal += 4; + } + + wNBytes = wNBytes & 0x03; + if (wNBytes) { + uint32_t wrVal = *srcVal; + wNBytes--; + + if (wNBytes) { + wrVal |= *++srcVal << 8; + wNBytes--; + + if (wNBytes) { + wrVal |= *++srcVal << 16; + } + } + + *dst32 = wrVal; + } + + return true; +} +#else +// Packet buffer access can only be 8- or 16-bit. +/** + * @brief Copy a buffer from user memory area to packet memory area (PMA). + * This uses byte-access for user memory (so support non-aligned buffers) + * and 16-bit access for packet memory. + * @param dst, byte address in PMA; must be 16-bit aligned + * @param src pointer to user memory area. + * @param wPMABufAddr address into PMA. + * @param wNBytes no. of bytes to be copied. + * @retval None + */ +static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes) +{ + uint32_t n = (uint32_t)wNBytes >> 1U; + uint16_t temp1, temp2; + const uint8_t *srcVal; + + // The GCC optimizer will combine access to 32-bit sizes if we let it. Force + // it volatile so that it won't do that. + __IO uint16_t *pdwVal; + + srcVal = src; + pdwVal = &pma[FSDEV_PMA_STRIDE * (dst >> 1)]; + + while (n--) { + temp1 = (uint16_t)*srcVal; + srcVal++; + temp2 = temp1 | ((uint16_t)(((uint16_t)(*srcVal)) << 8U)); + *pdwVal = temp2; + pdwVal += FSDEV_PMA_STRIDE; + srcVal++; + } + + if (wNBytes) { + temp1 = *srcVal; + *pdwVal = temp1; + } + + return true; +} +#endif + +/** + * @brief Copy from FIFO to packet memory area (PMA). + * Uses byte-access of system memory and 16-bit access of packet memory + * @param wNBytes no. of bytes to be copied. + * @retval None + */ +static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNBytes) +{ + // Since we copy from a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies + tu_fifo_buffer_info_t info; + tu_fifo_get_read_info(ff, &info); + + uint16_t cnt_lin = TU_MIN(wNBytes, info.len_lin); + uint16_t cnt_wrap = TU_MIN(wNBytes - cnt_lin, info.len_wrap); + + // We want to read from the FIFO and write it into the PMA, if LIN part is ODD and has WRAPPED part, + // last lin byte will be combined with wrapped part + // To ensure PMA is always access aligned (dst aligned to 16 or 32 bit) +#ifdef FSDEV_BUS_32BIT + if ((cnt_lin & 0x03) && cnt_wrap) { + // Copy first linear part + dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin & ~0x03); + dst += cnt_lin & ~0x03; + + // Copy last linear bytes & first wrapped bytes to buffer + uint32_t i; + uint8_t tmp[4]; + for (i = 0; i < (cnt_lin & 0x03); i++) { + tmp[i] = ((uint8_t *)info.ptr_lin)[(cnt_lin & ~0x03) + i]; + } + uint32_t wCnt = cnt_wrap; + for (; i < 4 && wCnt > 0; i++, wCnt--) { + tmp[i] = *(uint8_t *)info.ptr_wrap; + info.ptr_wrap = (uint8_t *)info.ptr_wrap + 1; + } + + // Write unaligned buffer + dcd_write_packet_memory(dst, &tmp, 4); + dst += 4; + + // Copy rest of wrapped byte + if (wCnt) + dcd_write_packet_memory(dst, info.ptr_wrap, wCnt); + } +#else + if ((cnt_lin & 0x01) && cnt_wrap) { + // Copy first linear part + dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin & ~0x01); + dst += cnt_lin & ~0x01; + + // Copy last linear byte & first wrapped byte + uint16_t tmp = ((uint8_t *)info.ptr_lin)[cnt_lin - 1] | ((uint16_t)(((uint8_t *)info.ptr_wrap)[0]) << 8U); + dcd_write_packet_memory(dst, &tmp, 2); + dst += 2; + + // Copy rest of wrapped byte + dcd_write_packet_memory(dst, ((uint8_t *)info.ptr_wrap) + 1, cnt_wrap - 1); + } +#endif + else { + // Copy linear part + dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin); + dst += info.len_lin; + + if (info.len_wrap) { + // Copy wrapped byte + dcd_write_packet_memory(dst, info.ptr_wrap, cnt_wrap); + } + } + + tu_fifo_advance_read_pointer(ff, cnt_lin + cnt_wrap); + + return true; +} + +#ifdef FSDEV_BUS_32BIT +static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes) +{ + uint8_t *dstVal = dst; + volatile uint32_t *src32 = (volatile uint32_t *)(USB_PMAADDR + src); + + for (uint32_t n = wNBytes / 4; n > 0; --n) { + tu_unaligned_write32(dstVal, *src32++); + dstVal += 4; + } + + wNBytes = wNBytes & 0x03; + if (wNBytes) { + uint32_t rdVal = *src32; + + *dstVal = tu_u32_byte0(rdVal); + wNBytes--; + + if (wNBytes) { + *++dstVal = tu_u32_byte1(rdVal); + wNBytes--; + + if (wNBytes) { + *++dstVal = tu_u32_byte2(rdVal); + } + } + } + + return true; +} +#else +/** + * @brief Copy a buffer from packet memory area (PMA) to user memory area. + * Uses byte-access of system memory and 16-bit access of packet memory + * @param wNBytes no. of bytes to be copied. + * @retval None + */ +static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes) +{ + uint32_t n = (uint32_t)wNBytes >> 1U; + // The GCC optimizer will combine access to 32-bit sizes if we let it. Force + // it volatile so that it won't do that. + __IO const uint16_t *pdwVal; + uint32_t temp; + + pdwVal = &pma[FSDEV_PMA_STRIDE * (src >> 1)]; + uint8_t *dstVal = (uint8_t *)dst; + + while (n--) { + temp = *pdwVal; + pdwVal += FSDEV_PMA_STRIDE; + *dstVal++ = ((temp >> 0) & 0xFF); + *dstVal++ = ((temp >> 8) & 0xFF); + } + + if (wNBytes & 0x01) { + temp = *pdwVal; + pdwVal += FSDEV_PMA_STRIDE; + *dstVal++ = ((temp >> 0) & 0xFF); + } + return true; +} +#endif + +/** + * @brief Copy a buffer from user packet memory area (PMA) to FIFO. + * Uses byte-access of system memory and 16-bit access of packet memory + * @param wNBytes no. of bytes to be copied. + * @retval None + */ +static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBytes) +{ + // Since we copy into a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies + // Check for first linear part + tu_fifo_buffer_info_t info; + tu_fifo_get_write_info(ff, &info); // We want to read from the FIFO + + uint16_t cnt_lin = TU_MIN(wNBytes, info.len_lin); + uint16_t cnt_wrap = TU_MIN(wNBytes - cnt_lin, info.len_wrap); + + // We want to read from PMA and write it into the FIFO, if LIN part is ODD and has WRAPPED part, + // last lin byte will be combined with wrapped part + // To ensure PMA is always access aligned (src aligned to 16 or 32 bit) +#ifdef FSDEV_BUS_32BIT + if ((cnt_lin & 0x03) && cnt_wrap) { + // Copy first linear part + dcd_read_packet_memory(info.ptr_lin, src, cnt_lin & ~0x03); + src += cnt_lin & ~0x03; + + // Copy last linear bytes & first wrapped bytes + uint8_t tmp[4]; + dcd_read_packet_memory(tmp, src, 4); + src += 4; + + uint32_t i; + for (i = 0; i < (cnt_lin & 0x03); i++) { + ((uint8_t *)info.ptr_lin)[(cnt_lin & ~0x03) + i] = tmp[i]; + } + uint32_t wCnt = cnt_wrap; + for (; i < 4 && wCnt > 0; i++, wCnt--) { + *(uint8_t *)info.ptr_wrap = tmp[i]; + info.ptr_wrap = (uint8_t *)info.ptr_wrap + 1; + } + + // Copy rest of wrapped byte + if (wCnt) + dcd_read_packet_memory(info.ptr_wrap, src, wCnt); + } +#else + if ((cnt_lin & 0x01) && cnt_wrap) { + // Copy first linear part + dcd_read_packet_memory(info.ptr_lin, src, cnt_lin & ~0x01); + src += cnt_lin & ~0x01; + + // Copy last linear byte & first wrapped byte + uint8_t tmp[2]; + dcd_read_packet_memory(tmp, src, 2); + src += 2; + + ((uint8_t *)info.ptr_lin)[cnt_lin - 1] = tmp[0]; + ((uint8_t *)info.ptr_wrap)[0] = tmp[1]; + + // Copy rest of wrapped byte + dcd_read_packet_memory(((uint8_t *)info.ptr_wrap) + 1, src, cnt_wrap - 1); + } +#endif + else { + // Copy linear part + dcd_read_packet_memory(info.ptr_lin, src, cnt_lin); + src += cnt_lin; + + if (info.len_wrap) { + // Copy wrapped byte + dcd_read_packet_memory(info.ptr_wrap, src, cnt_wrap); + } + } + + tu_fifo_advance_write_pointer(ff, cnt_lin + cnt_wrap); + + return true; +} + +#endif diff --git a/test-devices/loopback-stm32/lib/tinyusb/portable/st/stm32_fsdev/dcd_stm32_fsdev.h b/test-devices/loopback-stm32/lib/tinyusb/portable/st/stm32_fsdev/dcd_stm32_fsdev.h new file mode 100644 index 00000000..7992f34a --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/portable/st/stm32_fsdev/dcd_stm32_fsdev.h @@ -0,0 +1,551 @@ +/* + * Copyright(c) 2016 STMicroelectronics + * Copyright(c) N Conrad + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the TinyUSB stack. + */ + +// This file contains source copied from ST's HAL, and thus should have their copyright statement. + +// FSDEV_PMA_SIZE is PMA buffer size in bytes. +// On 512-byte devices, access with a stride of two words (use every other 16-bit address) +// On 1024-byte devices, access with a stride of one word (use every 16-bit address) + +#ifndef PORTABLE_ST_STM32F0_DCD_STM32F0_FSDEV_PVT_ST_H_ +#define PORTABLE_ST_STM32F0_DCD_STM32F0_FSDEV_PVT_ST_H_ + +#if CFG_TUSB_MCU == OPT_MCU_STM32F0 + #include "stm32f0xx.h" + #define FSDEV_PMA_SIZE (1024u) + // F0x2 models are crystal-less + // All have internal D+ pull-up + // 070RB: 2 x 16 bits/word memory LPM Support, BCD Support + // PMA dedicated to USB (no sharing with CAN) + +#elif CFG_TUSB_MCU == OPT_MCU_STM32F1 + #include "stm32f1xx.h" + #define FSDEV_PMA_SIZE (512u) + // NO internal Pull-ups + // *B, and *C: 2 x 16 bits/word + + // F1 names this differently from the rest + #define USB_CNTR_LPMODE USB_CNTR_LP_MODE + +#elif defined(STM32F302xB) || defined(STM32F302xC) || \ + defined(STM32F303xB) || defined(STM32F303xC) || \ + defined(STM32F373xC) + #include "stm32f3xx.h" + #define FSDEV_PMA_SIZE (512u) + // NO internal Pull-ups + // *B, and *C: 1 x 16 bits/word + // PMA dedicated to USB (no sharing with CAN) + +#elif defined(STM32F302x6) || defined(STM32F302x8) || \ + defined(STM32F302xD) || defined(STM32F302xE) || \ + defined(STM32F303xD) || defined(STM32F303xE) + #include "stm32f3xx.h" + #define FSDEV_PMA_SIZE (1024u) + // NO internal Pull-ups + // *6, *8, *D, and *E: 2 x 16 bits/word LPM Support + // When CAN clock is enabled, USB can use first 768 bytes ONLY. + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L0 + #include "stm32l0xx.h" + #define FSDEV_PMA_SIZE (1024u) + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L1 + #include "stm32l1xx.h" + #define FSDEV_PMA_SIZE (512u) + +#elif CFG_TUSB_MCU == OPT_MCU_STM32G4 + #include "stm32g4xx.h" + #define FSDEV_PMA_SIZE (1024u) + +#elif CFG_TUSB_MCU == OPT_MCU_STM32G0 + #include "stm32g0xx.h" + #define FSDEV_BUS_32BIT + #define FSDEV_PMA_SIZE (2048u) + #undef USB_PMAADDR + #define USB_PMAADDR USB_DRD_PMAADDR + #define USB_TypeDef USB_DRD_TypeDef + #define EP0R CHEP0R + #define USB_EP_CTR_RX USB_EP_VTRX + #define USB_EP_CTR_TX USB_EP_VTTX + #define USB_EP_T_FIELD USB_CHEP_UTYPE + #define USB_EPREG_MASK USB_CHEP_REG_MASK + #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK + #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK + #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1 + #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2 + #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1 + #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2 + #define USB_EPRX_STAT USB_CH_RX_VALID + #define USB_EPKIND_MASK USB_EP_KIND_MASK + #define USB USB_DRD_FS + #define USB_CNTR_FRES USB_CNTR_USBRST + #define USB_CNTR_RESUME USB_CNTR_L2RES + #define USB_ISTR_EP_ID USB_ISTR_IDN + #define USB_EPADDR_FIELD USB_CHEP_ADDR + #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY + #define USB_CNTR_FSUSP USB_CNTR_SUSPEN + +#elif CFG_TUSB_MCU == OPT_MCU_STM32H5 + #include "stm32h5xx.h" + #define FSDEV_BUS_32BIT + + #if !defined(USB_DRD_BASE) && defined(USB_DRD_FS_BASE) + #define USB_DRD_BASE USB_DRD_FS_BASE + #endif + + #define FSDEV_PMA_SIZE (2048u) + #undef USB_PMAADDR + #define USB_PMAADDR USB_DRD_PMAADDR + #define USB_TypeDef USB_DRD_TypeDef + #define EP0R CHEP0R + #define USB_EP_CTR_RX USB_EP_VTRX + #define USB_EP_CTR_TX USB_EP_VTTX + #define USB_EP_T_FIELD USB_CHEP_UTYPE + #define USB_EPREG_MASK USB_CHEP_REG_MASK + #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK + #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK + #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1 + #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2 + #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1 + #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2 + #define USB_EPRX_STAT USB_CH_RX_VALID + #define USB_EPKIND_MASK USB_EP_KIND_MASK + #define USB USB_DRD_FS + #define USB_CNTR_FRES USB_CNTR_USBRST + #define USB_CNTR_RESUME USB_CNTR_L2RES + #define USB_ISTR_EP_ID USB_ISTR_IDN + #define USB_EPADDR_FIELD USB_CHEP_ADDR + #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY + #define USB_CNTR_FSUSP USB_CNTR_SUSPEN + +#elif CFG_TUSB_MCU == OPT_MCU_STM32WB + #include "stm32wbxx.h" + #define FSDEV_PMA_SIZE (1024u) + /* ST provided header has incorrect value */ + #undef USB_PMAADDR + #define USB_PMAADDR USB1_PMAADDR + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L4 + #include "stm32l4xx.h" + #define FSDEV_PMA_SIZE (1024u) + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L5 + #include "stm32l5xx.h" + #define FSDEV_PMA_SIZE (1024u) + + #ifndef USB_PMAADDR + #define USB_PMAADDR (USB_BASE + (USB_PMAADDR_NS - USB_BASE_NS)) + #endif + +#else + #error You are using an untested or unimplemented STM32 variant. Please update the driver. + // This includes L1x0, L1x1, L1x2, L4x2 and L4x3, G1x1, G1x3, and G1x4 +#endif + +// For purposes of accessing the packet +#if ((FSDEV_PMA_SIZE) == 512u) + #define FSDEV_PMA_STRIDE (2u) +#elif ((FSDEV_PMA_SIZE) == 1024u) + #define FSDEV_PMA_STRIDE (1u) +#endif + +// The fsdev_bus_t type can be used for both register and PMA access necessities +// For type-safety create a new macro for the volatile address of PMAADDR +// The compiler should warn us if we cast it to a non-volatile type? +#ifdef FSDEV_BUS_32BIT +typedef uint32_t fsdev_bus_t; +static __IO uint32_t * const pma32 = (__IO uint32_t*)USB_PMAADDR; + +#else +typedef uint16_t fsdev_bus_t; +// Volatile is also needed to prevent the optimizer from changing access to 32-bit (as 32-bit access is forbidden) +static __IO uint16_t * const pma = (__IO uint16_t*)USB_PMAADDR; + +TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t * pcd_btable_word_ptr(USB_TypeDef * USBx, size_t x) { + size_t total_word_offset = (((USBx)->BTABLE)>>1) + x; + total_word_offset *= FSDEV_PMA_STRIDE; + return &(pma[total_word_offset]); +} + +TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t* pcd_ep_tx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx) { + return pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 1u); +} + +TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t* pcd_ep_rx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx) { + return pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 3u); +} +#endif + +/* Aligned buffer size according to hardware */ +TU_ATTR_ALWAYS_INLINE static inline uint16_t pcd_aligned_buffer_size(uint16_t size) { + /* The STM32 full speed USB peripheral supports only a limited set of + * buffer sizes given by the RX buffer entry format in the USB_BTABLE. */ + uint16_t blocksize = (size > 62) ? 32 : 2; + + // Round up while dividing requested size by blocksize + uint16_t numblocks = (size + blocksize - 1) / blocksize ; + + return numblocks * blocksize; +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wRegValue) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + __O uint32_t *reg = (__O uint32_t *)(USB_DRD_BASE + bEpIdx*4); + *reg = wRegValue; +#else + __O uint16_t *reg = (__O uint16_t *)((&USBx->EP0R) + bEpIdx*2u); + *reg = (uint16_t)wRegValue; +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + __I uint32_t *reg = (__I uint32_t *)(USB_DRD_BASE + bEpIdx*4); +#else + __I uint16_t *reg = (__I uint16_t *)((&USBx->EP0R) + bEpIdx*2u); +#endif + return *reg; +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_eptype(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wType) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= (uint32_t)USB_EP_T_MASK; + regVal |= wType; + regVal |= USB_EP_CTR_RX | USB_EP_CTR_TX; // These clear on write0, so must set high + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_eptype(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EP_T_FIELD; + return regVal; +} + +/** + * @brief Clears bit CTR_RX / CTR_TX in the endpoint register. + * @param USBx USB peripheral instance register address. + * @param bEpIdx Endpoint Number. + * @retval None + */ +TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_rx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPREG_MASK; + regVal &= ~USB_EP_CTR_RX; + regVal |= USB_EP_CTR_TX; // preserve CTR_TX (clears on writing 0) + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPREG_MASK; + regVal &= ~USB_EP_CTR_TX; + regVal |= USB_EP_CTR_RX; // preserve CTR_RX (clears on writing 0) + pcd_set_endpoint(USBx, bEpIdx,regVal); +} + +/** + * @brief gets counter of the tx buffer. + * @param USBx USB peripheral instance register address. + * @param bEpIdx Endpoint Number. + * @retval Counter value + */ +TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + return (pma32[2*bEpIdx] & 0x03FF0000) >> 16; +#else + __I uint16_t *regPtr = pcd_ep_tx_cnt_ptr(USBx, bEpIdx); + return *regPtr & 0x3ffU; +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + return (pma32[2*bEpIdx + 1] & 0x03FF0000) >> 16; +#else + __I uint16_t *regPtr = pcd_ep_rx_cnt_ptr(USBx, bEpIdx); + return *regPtr & 0x3ffU; +#endif +} + +#define pcd_get_ep_dbuf0_cnt pcd_get_ep_tx_cnt +#define pcd_get_ep_dbuf1_cnt pcd_get_ep_rx_cnt + +/** + * @brief Sets address in an endpoint register. + * @param USBx USB peripheral instance register address. + * @param bEpIdx Endpoint Number. + * @param bAddr Address. + * @retval None + */ +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t bAddr) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPREG_MASK; + regVal |= bAddr; + regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; + pcd_set_endpoint(USBx, bEpIdx,regVal); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + return pma32[2*bEpIdx] & 0x0000FFFFu ; +#else + return *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 0u); +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + return pma32[2*bEpIdx + 1] & 0x0000FFFFu; +#else + return *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 2u); +#endif +} + +#define pcd_get_ep_dbuf0_address pcd_get_ep_tx_address +#define pcd_get_ep_dbuf1_address pcd_get_ep_rx_address + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + pma32[2*bEpIdx] = (pma32[2*bEpIdx] & 0xFFFF0000u) | (addr & 0x0000FFFCu); +#else + *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 0u) = addr; +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & 0xFFFF0000u) | (addr & 0x0000FFFCu); +#else + *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 2u) = addr; +#endif +} + +#define pcd_set_ep_dbuf0_address pcd_set_ep_tx_address +#define pcd_set_ep_dbuf1_address pcd_set_ep_rx_address + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + pma32[2*bEpIdx] = (pma32[2*bEpIdx] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16); +#else + __IO uint16_t * reg = pcd_ep_tx_cnt_ptr(USBx, bEpIdx); + *reg = (uint16_t) (*reg & (uint16_t) ~0x3FFU) | (wCount & 0x3FFU); +#endif +} + +#define pcd_set_ep_tx_dbuf0_cnt pcd_set_ep_tx_cnt + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_dbuf1_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16); +#else + __IO uint16_t * reg = pcd_ep_rx_cnt_ptr(USBx, bEpIdx); + *reg = (uint16_t) (*reg & (uint16_t) ~0x3FFU) | (wCount & 0x3FFU); +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_blsize_num_blocks(USB_TypeDef * USBx, uint32_t rxtx_idx, + uint32_t blocksize, uint32_t numblocks) { + /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */ +#ifdef FSDEV_BUS_32BIT + (void) USBx; + pma32[rxtx_idx] = (pma32[rxtx_idx] & 0x0000FFFFu) | (blocksize << 31) | ((numblocks - blocksize) << 26); +#else + __IO uint16_t *pdwReg = pcd_btable_word_ptr(USBx, rxtx_idx*2u + 1u); + *pdwReg = (blocksize << 15) | ((numblocks - blocksize) << 10); +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_bufsize(USB_TypeDef * USBx, uint32_t rxtx_idx, uint32_t wCount) { + wCount = pcd_aligned_buffer_size(wCount); + + /* We assume that the buffer size is already aligned to hardware requirements. */ + uint16_t blocksize = (wCount > 62) ? 1 : 0; + uint16_t numblocks = wCount / (blocksize ? 32 : 2); + + /* There should be no remainder in the above calculation */ + TU_ASSERT((wCount - (numblocks * (blocksize ? 32 : 2))) == 0, /**/); + + /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */ + pcd_set_ep_blsize_num_blocks(USBx, rxtx_idx, blocksize, numblocks); +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_dbuf0_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) { + pcd_set_ep_bufsize(USBx, 2*bEpIdx, wCount); +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) { + pcd_set_ep_bufsize(USBx, 2*bEpIdx + 1, wCount); +} + +#define pcd_set_ep_rx_dbuf1_cnt pcd_set_ep_rx_cnt + +/** + * @brief sets the status for tx transfer (bits STAT_TX[1:0]). + * @param USBx USB peripheral instance register address. + * @param bEpIdx Endpoint Number. + * @param wState new state + * @retval None + */ +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_status(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wState) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPTX_DTOGMASK; + + /* toggle first bit ? */ + if((USB_EPTX_DTOG1 & (wState))!= 0U) + { + regVal ^= USB_EPTX_DTOG1; + } + /* toggle second bit ? */ + if((USB_EPTX_DTOG2 & ((uint32_t)(wState)))!= 0U) + { + regVal ^= USB_EPTX_DTOG2; + } + + regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +/** + * @brief sets the status for rx transfer (bits STAT_TX[1:0]) + * @param USBx USB peripheral instance register address. + * @param bEpIdx Endpoint Number. + * @param wState new state + * @retval None + */ + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_status(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wState) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPRX_DTOGMASK; + + /* toggle first bit ? */ + if((USB_EPRX_DTOG1 & wState)!= 0U) { + regVal ^= USB_EPRX_DTOG1; + } + /* toggle second bit ? */ + if((USB_EPRX_DTOG2 & wState)!= 0U) { + regVal ^= USB_EPRX_DTOG2; + } + + regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_status(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + return (regVal & USB_EPRX_STAT) >> (12u); +} + + +/** + * @brief Toggles DTOG_RX / DTOG_TX bit in the endpoint register. + * @param USBx USB peripheral instance register address. + * @param bEpIdx Endpoint Number. + * @retval None + */ +TU_ATTR_ALWAYS_INLINE static inline void pcd_rx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPREG_MASK; + regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX|USB_EP_DTOG_RX; + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_tx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPREG_MASK; + regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX|USB_EP_DTOG_TX; + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +/** + * @brief Clears DTOG_RX / DTOG_TX bit in the endpoint register. + * @param USBx USB peripheral instance register address. + * @param bEpIdx Endpoint Number. + * @retval None + */ +TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_rx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + if((regVal & USB_EP_DTOG_RX) != 0) { + pcd_rx_dtog(USBx,bEpIdx); + } +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + if((regVal & USB_EP_DTOG_TX) != 0) { + pcd_tx_dtog(USBx,bEpIdx); + } +} + +/** + * @brief set & clear EP_KIND bit. + * @param USBx USB peripheral instance register address. + * @param bEpIdx Endpoint Number. + * @retval None + */ +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_kind(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal |= USB_EP_KIND; + regVal &= USB_EPREG_MASK; + regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_ep_kind(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPKIND_MASK; + regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +// This checks if the device has "LPM" +#if defined(USB_ISTR_L1REQ) +#define USB_ISTR_L1REQ_FORCED (USB_ISTR_L1REQ) +#else +#define USB_ISTR_L1REQ_FORCED ((uint16_t)0x0000U) +#endif + +#define USB_ISTR_ALL_EVENTS (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_WKUP | USB_ISTR_SUSP | \ + USB_ISTR_RESET | USB_ISTR_SOF | USB_ISTR_ESOF | USB_ISTR_L1REQ_FORCED ) + +// Number of endpoints in hardware +// TODO should use TUP_DCD_ENDPOINT_MAX +#define STFSDEV_EP_COUNT (8u) + +#endif /* PORTABLE_ST_STM32F0_DCD_STM32F0_FSDEV_PVT_ST_H_ */ diff --git a/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dcd_dwc2.c b/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dcd_dwc2.c new file mode 100644 index 00000000..692096fc --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dcd_dwc2.c @@ -0,0 +1,1198 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 William D. Jones + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * Copyright (c) 2020 Jan Duempelmann + * Copyright (c) 2020 Reinhard Panhuber + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if CFG_TUD_ENABLED && defined(TUP_USBIP_DWC2) + +#include "device/dcd.h" +#include "dwc2_type.h" + +// Following symbols must be defined by port header +// - _dwc2_controller[]: array of controllers +// - DWC2_EP_MAX: largest EP counts of all controllers +// - dwc2_phy_init/dwc2_phy_update: phy init called before and after core reset +// - dwc2_dcd_int_enable/dwc2_dcd_int_disable +// - dwc2_remote_wakeup_delay + +#if defined(TUP_USBIP_DWC2_STM32) + #include "dwc2_stm32.h" +#elif TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) + #include "dwc2_esp32.h" +#elif TU_CHECK_MCU(OPT_MCU_GD32VF103) + #include "dwc2_gd32.h" +#elif TU_CHECK_MCU(OPT_MCU_BCM2711, OPT_MCU_BCM2835, OPT_MCU_BCM2837) + #include "dwc2_bcm.h" +#elif TU_CHECK_MCU(OPT_MCU_EFM32GG) + #include "dwc2_efm32.h" +#elif TU_CHECK_MCU(OPT_MCU_XMC4000) + #include "dwc2_xmc.h" +#else + #error "Unsupported MCUs" +#endif + +//--------------------------------------------------------------------+ +// MACRO TYPEDEF CONSTANT ENUM +//--------------------------------------------------------------------+ + +// DWC2 registers +#define DWC2_REG(_port) ((dwc2_regs_t*) _dwc2_controller[_port].reg_base) + +// Debug level for DWC2 +#define DWC2_DEBUG 2 + +#ifndef dcache_clean +#define dcache_clean(_addr, _size) +#endif + +#ifndef dcache_invalidate +#define dcache_invalidate(_addr, _size) +#endif + +#ifndef dcache_clean_invalidate +#define dcache_clean_invalidate(_addr, _size) +#endif + +static TU_ATTR_ALIGNED(4) uint32_t _setup_packet[2]; + +typedef struct { + uint8_t* buffer; + tu_fifo_t* ff; + uint16_t total_len; + uint16_t max_size; + uint8_t interval; +} xfer_ctl_t; + +static xfer_ctl_t xfer_status[DWC2_EP_MAX][2]; +#define XFER_CTL_BASE(_ep, _dir) (&xfer_status[_ep][_dir]) + +// EP0 transfers are limited to 1 packet - larger sizes has to be split +static uint16_t ep0_pending[2]; // Index determines direction as tusb_dir_t type + +// TX FIFO RAM allocation so far in words - RX FIFO size is readily available from dwc2->grxfsiz +static uint16_t _allocated_fifo_words_tx; // TX FIFO size in words (IN EPs) + +// SOF enabling flag - required for SOF to not get disabled in ISR when SOF was enabled by +static bool _sof_en; + +// Calculate the RX FIFO size according to minimum recommendations from reference manual +// RxFIFO = (5 * number of control endpoints + 8) + +// ((largest USB packet used / 4) + 1 for status information) + +// (2 * number of OUT endpoints) + 1 for Global NAK +// with number of control endpoints = 1 we have +// RxFIFO = 15 + (largest USB packet used / 4) + 2 * number of OUT endpoints +// we double the largest USB packet size to be able to hold up to 2 packets +static inline uint16_t calc_grxfsiz(uint16_t max_ep_size, uint8_t ep_count) { + return 15 + 2 * (max_ep_size / 4) + 2 * ep_count; +} + +TU_ATTR_ALWAYS_INLINE static inline void fifo_flush_tx(dwc2_regs_t* dwc2, uint8_t epnum) { + // flush TX fifo and wait for it cleared + dwc2->grstctl = GRSTCTL_TXFFLSH | (epnum << GRSTCTL_TXFNUM_Pos); + while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {} +} +TU_ATTR_ALWAYS_INLINE static inline void fifo_flush_rx(dwc2_regs_t* dwc2) { + // flush RX fifo and wait for it cleared + dwc2->grstctl = GRSTCTL_RXFFLSH; + while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {} +} + +static bool fifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + uint8_t const ep_count = _dwc2_controller[rhport].ep_count; + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + TU_ASSERT(epnum < ep_count); + + uint16_t fifo_size = tu_div_ceil(packet_size, 4); + + // "USB Data FIFOs" section in reference manual + // Peripheral FIFO architecture + // + // --------------- 320 or 1024 ( 1280 or 4096 bytes ) + // | IN FIFO 0 | + // --------------- (320 or 1024) - 16 + // | IN FIFO 1 | + // --------------- (320 or 1024) - 16 - x + // | . . . . | + // --------------- (320 or 1024) - 16 - x - y - ... - z + // | IN FIFO MAX | + // --------------- + // | FREE | + // --------------- GRXFSIZ + // | OUT FIFO | + // | ( Shared ) | + // --------------- 0 + // + // In FIFO is allocated by following rules: + // - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n". + if (dir == TUSB_DIR_OUT) { + // Calculate required size of RX FIFO + uint16_t const sz = calc_grxfsiz(4 * fifo_size, ep_count); + + // If size_rx needs to be extended check if possible and if so enlarge it + if (dwc2->grxfsiz < sz) { + TU_ASSERT(sz + _allocated_fifo_words_tx <= _dwc2_controller[rhport].ep_fifo_size / 4); + + // Enlarge RX FIFO + dwc2->grxfsiz = sz; + } + } else { + // Note if The TXFELVL is configured as half empty. In order + // to be able to write a packet at that point, the fifo must be twice the max_size. + if ((dwc2->gahbcfg & GAHBCFG_TXFELVL) == 0) { + fifo_size *= 2; + } + + // Check if free space is available + TU_ASSERT(_allocated_fifo_words_tx + fifo_size + dwc2->grxfsiz <= _dwc2_controller[rhport].ep_fifo_size / 4); + _allocated_fifo_words_tx += fifo_size; + TU_LOG(DWC2_DEBUG, " Allocated %u bytes at offset %" PRIu32, fifo_size * 4, + _dwc2_controller[rhport].ep_fifo_size - _allocated_fifo_words_tx * 4); + + // DIEPTXF starts at FIFO #1. + // Both TXFD and TXSA are in unit of 32-bit words. + dwc2->dieptxf[epnum - 1] = (fifo_size << DIEPTXF_INEPTXFD_Pos) | + (_dwc2_controller[rhport].ep_fifo_size / 4 - _allocated_fifo_words_tx); + } + + return true; +} + +static void edpt_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress); + uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); + + xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir); + xfer->max_size = tu_edpt_packet_size(p_endpoint_desc); + xfer->interval = p_endpoint_desc->bInterval; + + // USBAEP, EPTYP, SD0PID_SEVNFRM, MPSIZ are the same for IN and OUT endpoints. + uint32_t const dxepctl = (1 << DOEPCTL_USBAEP_Pos) | + (p_endpoint_desc->bmAttributes.xfer << DOEPCTL_EPTYP_Pos) | + (p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? DOEPCTL_SD0PID_SEVNFRM : 0) | + (xfer->max_size << DOEPCTL_MPSIZ_Pos); + + if (dir == TUSB_DIR_OUT) { + dwc2->epout[epnum].doepctl = dxepctl; + dwc2->daintmsk |= TU_BIT(DAINTMSK_OEPM_Pos + epnum); + } else { + dwc2->epin[epnum].diepctl = dxepctl | (epnum << DIEPCTL_TXFNUM_Pos); + dwc2->daintmsk |= (1 << (DAINTMSK_IEPM_Pos + epnum)); + } +} + +static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) { + (void) rhport; + + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + if (dir == TUSB_DIR_IN) { + dwc2_epin_t* epin = dwc2->epin; + + // Only disable currently enabled non-control endpoint + if ((epnum == 0) || !(epin[epnum].diepctl & DIEPCTL_EPENA)) { + epin[epnum].diepctl |= DIEPCTL_SNAK | (stall ? DIEPCTL_STALL : 0); + } else { + // Stop transmitting packets and NAK IN xfers. + epin[epnum].diepctl |= DIEPCTL_SNAK; + while ((epin[epnum].diepint & DIEPINT_INEPNE) == 0) {} + + // Disable the endpoint. + epin[epnum].diepctl |= DIEPCTL_EPDIS | (stall ? DIEPCTL_STALL : 0); + while ((epin[epnum].diepint & DIEPINT_EPDISD_Msk) == 0) {} + + epin[epnum].diepint = DIEPINT_EPDISD; + } + + // Flush the FIFO, and wait until we have confirmed it cleared. + fifo_flush_tx(dwc2, epnum); + } else { + dwc2_epout_t* epout = dwc2->epout; + + // Only disable currently enabled non-control endpoint + if ((epnum == 0) || !(epout[epnum].doepctl & DOEPCTL_EPENA)) { + epout[epnum].doepctl |= stall ? DOEPCTL_STALL : 0; + } else { + // Asserting GONAK is required to STALL an OUT endpoint. + // Simpler to use polling here, we don't use the "B"OUTNAKEFF interrupt + // anyway, and it can't be cleared by user code. If this while loop never + // finishes, we have bigger problems than just the stack. + dwc2->dctl |= DCTL_SGONAK; + while ((dwc2->gintsts & GINTSTS_BOUTNAKEFF_Msk) == 0) {} + + // Ditto here- disable the endpoint. + epout[epnum].doepctl |= DOEPCTL_EPDIS | (stall ? DOEPCTL_STALL : 0); + while ((epout[epnum].doepint & DOEPINT_EPDISD_Msk) == 0) {} + + epout[epnum].doepint = DOEPINT_EPDISD; + + // Allow other OUT endpoints to keep receiving. + dwc2->dctl |= DCTL_CGONAK; + } + } +} + +// Start of Bus Reset +static void bus_reset(uint8_t rhport) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + uint8_t const ep_count = _dwc2_controller[rhport].ep_count; + + tu_memclr(xfer_status, sizeof(xfer_status)); + + _sof_en = false; + + // clear device address + dwc2->dcfg &= ~DCFG_DAD_Msk; + + // 1. NAK for all OUT endpoints + for (uint8_t n = 0; n < ep_count; n++) { + dwc2->epout[n].doepctl |= DOEPCTL_SNAK; + } + + // 2. Disable all IN endpoints + for (uint8_t n = 0; n < ep_count; n++) { + if (dwc2->epin[n].diepctl & DIEPCTL_EPENA) { + dwc2->epin[n].diepctl |= DIEPCTL_SNAK | DIEPCTL_EPDIS; + } + } + + fifo_flush_tx(dwc2, 0x10); // all tx fifo + fifo_flush_rx(dwc2); + + // 3. Set up interrupt mask + dwc2->daintmsk = TU_BIT(DAINTMSK_OEPM_Pos) | TU_BIT(DAINTMSK_IEPM_Pos); + dwc2->doepmsk = DOEPMSK_STUPM | DOEPMSK_XFRCM; + dwc2->diepmsk = DIEPMSK_TOM | DIEPMSK_XFRCM; + + // "USB Data FIFOs" section in reference manual + // Peripheral FIFO architecture + // + // The FIFO is split up in a lower part where the RX FIFO is located and an upper part where the TX FIFOs start. + // We do this to allow the RX FIFO to grow dynamically which is possible since the free space is located + // between the RX and TX FIFOs. This is required by ISO OUT EPs which need a bigger FIFO than the standard + // configuration done below. + // + // Dynamically FIFO sizes are of interest only for ISO EPs since all others are usually not opened and closed. + // All EPs other than ISO are opened as soon as the driver starts up i.e. when the host sends a + // configure interface command. Hence, all IN EPs other the ISO will be located at the top. IN ISO EPs are usually + // opened when the host sends an additional command: setInterface. At this point in time + // the ISO EP will be located next to the free space and can change its size. In case more IN EPs change its size + // an additional memory + // + // --------------- 320 or 1024 ( 1280 or 4096 bytes ) + // | IN FIFO 0 | + // --------------- (320 or 1024) - 16 + // | IN FIFO 1 | + // --------------- (320 or 1024) - 16 - x + // | . . . . | + // --------------- (320 or 1024) - 16 - x - y - ... - z + // | IN FIFO MAX | + // --------------- + // | FREE | + // --------------- GRXFSIZ + // | OUT FIFO | + // | ( Shared ) | + // --------------- 0 + // + // According to "FIFO RAM allocation" section in RM, FIFO RAM are allocated as follows (each word 32-bits): + // - Each EP IN needs at least max packet size, 16 words is sufficient for EP0 IN + // + // - All EP OUT shared a unique OUT FIFO which uses + // - 13 for setup packets + control words (up to 3 setup packets). + // - 1 for global NAK (not required/used here). + // - Largest-EPsize / 4 + 1. ( FS: 64 bytes, HS: 512 bytes). Recommended is "2 x (Largest-EPsize/4) + 1" + // - 2 for each used OUT endpoint + // + // Therefore GRXFSIZ = 13 + 1 + 1 + 2 x (Largest-EPsize/4) + 2 x EPOUTnum + // - FullSpeed (64 Bytes ): GRXFSIZ = 15 + 2 x 16 + 2 x ep_count = 47 + 2 x ep_count + // - Highspeed (512 bytes): GRXFSIZ = 15 + 2 x 128 + 2 x ep_count = 271 + 2 x ep_count + // + // NOTE: Largest-EPsize & EPOUTnum is actual used endpoints in configuration. Since DCD has no knowledge + // of the overall picture yet. We will use the worst scenario: largest possible + ep_count + // + // For Isochronous, largest EP size can be 1023/1024 for FS/HS respectively. In addition if multiple ISO + // are enabled at least "2 x (Largest-EPsize/4) + 1" are recommended. Maybe provide a macro for application to + // overwrite this. + + // EP0 out max is 64 + dwc2->grxfsiz = calc_grxfsiz(64, ep_count); + + // Setup the control endpoint 0 + _allocated_fifo_words_tx = 16; + + // Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word ) + dwc2->dieptxf0 = (16 << DIEPTXF0_TX0FD_Pos) | (_dwc2_controller[rhport].ep_fifo_size / 4 - _allocated_fifo_words_tx); + + // Fixed control EP0 size to 64 bytes + dwc2->epin[0].diepctl &= ~(0x03 << DIEPCTL_MPSIZ_Pos); + xfer_status[0][TUSB_DIR_OUT].max_size = 64; + xfer_status[0][TUSB_DIR_IN].max_size = 64; + + dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos); + + dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT; +} + +static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t const dir, uint16_t const num_packets, + uint16_t total_bytes) { + (void) rhport; + + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + + // EP0 is limited to one packet each xfer + // We use multiple transaction of xfer->max_size length to get a whole transfer done + if (epnum == 0) { + xfer_ctl_t* const xfer = XFER_CTL_BASE(epnum, dir); + total_bytes = tu_min16(ep0_pending[dir], xfer->max_size); + ep0_pending[dir] -= total_bytes; + } + + // IN and OUT endpoint xfers are interrupt-driven, we just schedule them here. + if (dir == TUSB_DIR_IN) { + dwc2_epin_t* epin = dwc2->epin; + + // A full IN transfer (multiple packets, possibly) triggers XFRC. + epin[epnum].dieptsiz = (num_packets << DIEPTSIZ_PKTCNT_Pos) | + ((total_bytes << DIEPTSIZ_XFRSIZ_Pos) & DIEPTSIZ_XFRSIZ_Msk); + + epin[epnum].diepctl |= DIEPCTL_EPENA | DIEPCTL_CNAK; + + // For ISO endpoint set correct odd/even bit for next frame. + if ((epin[epnum].diepctl & DIEPCTL_EPTYP) == DIEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1) { + // Take odd/even bit from frame counter. + uint32_t const odd_frame_now = (dwc2->dsts & (1u << DSTS_FNSOF_Pos)); + epin[epnum].diepctl |= (odd_frame_now ? DIEPCTL_SD0PID_SEVNFRM_Msk : DIEPCTL_SODDFRM_Msk); + } + // Enable fifo empty interrupt only if there are something to put in the fifo. + if (total_bytes != 0) { + dwc2->diepempmsk |= (1 << epnum); + } + } else { + dwc2_epout_t* epout = dwc2->epout; + + // A full OUT transfer (multiple packets, possibly) triggers XFRC. + epout[epnum].doeptsiz &= ~(DOEPTSIZ_PKTCNT_Msk | DOEPTSIZ_XFRSIZ); + epout[epnum].doeptsiz |= (num_packets << DOEPTSIZ_PKTCNT_Pos) | + ((total_bytes << DOEPTSIZ_XFRSIZ_Pos) & DOEPTSIZ_XFRSIZ_Msk); + + epout[epnum].doepctl |= DOEPCTL_EPENA | DOEPCTL_CNAK; + if ((epout[epnum].doepctl & DOEPCTL_EPTYP) == DOEPCTL_EPTYP_0 && + XFER_CTL_BASE(epnum, dir)->interval == 1) { + // Take odd/even bit from frame counter. + uint32_t const odd_frame_now = (dwc2->dsts & (1u << DSTS_FNSOF_Pos)); + epout[epnum].doepctl |= (odd_frame_now ? DOEPCTL_SD0PID_SEVNFRM_Msk : DOEPCTL_SODDFRM_Msk); + } + } +} + +/*------------------------------------------------------------------*/ +/* Controller API + *------------------------------------------------------------------*/ +#if CFG_TUSB_DEBUG >= DWC2_DEBUG +void print_dwc2_info(dwc2_regs_t* dwc2) { + // print guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4 + // use dwc2_info.py/md for bit-field value and comparison with other ports + volatile uint32_t const* p = (volatile uint32_t const*) &dwc2->guid; + TU_LOG(DWC2_DEBUG, "guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4\r\n"); + for (size_t i = 0; i < 5; i++) { + TU_LOG(DWC2_DEBUG, "0x%08" PRIX32 ", ", p[i]); + } + TU_LOG(DWC2_DEBUG, "0x%08" PRIX32 "\r\n", p[5]); +} +#endif + +static void reset_core(dwc2_regs_t* dwc2) { + // reset core + dwc2->grstctl |= GRSTCTL_CSRST; + + // wait for reset bit is cleared + // TODO version 4.20a should wait for RESET DONE mask + while (dwc2->grstctl & GRSTCTL_CSRST) {} + + // wait for AHB master IDLE + while (!(dwc2->grstctl & GRSTCTL_AHBIDL)) {} + + // wait for device mode ? +} + +static bool phy_hs_supported(dwc2_regs_t* dwc2) { + (void) dwc2; + +#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) + // note: esp32 incorrect report its hs_phy_type as utmi + return false; +#elif !TUD_OPT_HIGH_SPEED + return false; +#else + return dwc2->ghwcfg2_bm.hs_phy_type != HS_PHY_TYPE_NONE; +#endif +} + +static void phy_fs_init(dwc2_regs_t* dwc2) { + TU_LOG(DWC2_DEBUG, "Fullspeed PHY init\r\n"); + + // Select FS PHY + dwc2->gusbcfg |= GUSBCFG_PHYSEL; + + // MCU specific PHY init before reset + dwc2_phy_init(dwc2, HS_PHY_TYPE_NONE); + + // Reset core after selecting PHY + reset_core(dwc2); + + // USB turnaround time is critical for certification where long cables and 5-Hubs are used. + // So if you need the AHB to run at less than 30 MHz, and if USB turnaround time is not critical, + // these bits can be programmed to a larger value. Default is 5 + dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_TRDT_Msk) | (5u << GUSBCFG_TRDT_Pos); + + // MCU specific PHY update post reset + dwc2_phy_update(dwc2, HS_PHY_TYPE_NONE); + + // set max speed + dwc2->dcfg = (dwc2->dcfg & ~DCFG_DSPD_Msk) | (DCFG_DSPD_FS << DCFG_DSPD_Pos); +} + +static void phy_hs_init(dwc2_regs_t* dwc2) { + uint32_t gusbcfg = dwc2->gusbcfg; + + // De-select FS PHY + gusbcfg &= ~GUSBCFG_PHYSEL; + + if (dwc2->ghwcfg2_bm.hs_phy_type == HS_PHY_TYPE_ULPI) { + TU_LOG(DWC2_DEBUG, "Highspeed ULPI PHY init\r\n"); + + // Select ULPI + gusbcfg |= GUSBCFG_ULPI_UTMI_SEL; + + // ULPI 8-bit interface, single data rate + gusbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL); + + // default internal VBUS Indicator and Drive + gusbcfg &= ~(GUSBCFG_ULPIEVBUSD | GUSBCFG_ULPIEVBUSI); + + // Disable FS/LS ULPI + gusbcfg &= ~(GUSBCFG_ULPIFSLS | GUSBCFG_ULPICSM); + } else { + TU_LOG(DWC2_DEBUG, "Highspeed UTMI+ PHY init\r\n"); + + // Select UTMI+ with 8-bit interface + gusbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16); + + // Set 16-bit interface if supported + if (dwc2->ghwcfg4_bm.utmi_phy_data_width) gusbcfg |= GUSBCFG_PHYIF16; + } + + // Apply config + dwc2->gusbcfg = gusbcfg; + + // mcu specific phy init + dwc2_phy_init(dwc2, dwc2->ghwcfg2_bm.hs_phy_type); + + // Reset core after selecting PHY + reset_core(dwc2); + + // Set turn-around, must after core reset otherwise it will be clear + // - 9 if using 8-bit PHY interface + // - 5 if using 16-bit PHY interface + gusbcfg &= ~GUSBCFG_TRDT_Msk; + gusbcfg |= (dwc2->ghwcfg4_bm.utmi_phy_data_width ? 5u : 9u) << GUSBCFG_TRDT_Pos; + dwc2->gusbcfg = gusbcfg; + + // MCU specific PHY update post reset + dwc2_phy_update(dwc2, dwc2->ghwcfg2_bm.hs_phy_type); + + // Set max speed + uint32_t dcfg = dwc2->dcfg; + dcfg &= ~DCFG_DSPD_Msk; + dcfg |= DCFG_DSPD_HS << DCFG_DSPD_Pos; + + // XCVRDLY: transceiver delay between xcvr_sel and txvalid during device chirp is required + // when using with some PHYs such as USB334x (USB3341, USB3343, USB3346, USB3347) + if (dwc2->ghwcfg2_bm.hs_phy_type == HS_PHY_TYPE_ULPI) dcfg |= DCFG_XCVRDLY; + + dwc2->dcfg = dcfg; +} + +static bool check_dwc2(dwc2_regs_t* dwc2) { +#if CFG_TUSB_DEBUG >= DWC2_DEBUG + print_dwc2_info(dwc2); +#endif + + // For some reasons: GD32VF103 snpsid and all hwcfg register are always zero (skip it) + (void) dwc2; +#if !TU_CHECK_MCU(OPT_MCU_GD32VF103) + uint32_t const gsnpsid = dwc2->gsnpsid & GSNPSID_ID_MASK; + TU_ASSERT(gsnpsid == DWC2_OTG_ID || gsnpsid == DWC2_FS_IOT_ID || gsnpsid == DWC2_HS_IOT_ID); +#endif + + return true; +} + +void dcd_init(uint8_t rhport) { + // Programming model begins in the last section of the chapter on the USB + // peripheral in each Reference Manual. + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + + // Check Synopsys ID register, failed if controller clock/power is not enabled + if (!check_dwc2(dwc2)) return; + dcd_disconnect(rhport); + + // max number of endpoints & total_fifo_size are: + // hw_cfg2->num_dev_ep, hw_cfg2->total_fifo_size + + if (phy_hs_supported(dwc2)) { + phy_hs_init(dwc2); // Highspeed + } else { + phy_fs_init(dwc2); // core does not support highspeed or hs phy is not present + } + + // Restart PHY clock + dwc2->pcgctl &= ~(PCGCTL_STOPPCLK | PCGCTL_GATEHCLK | PCGCTL_PWRCLMP | PCGCTL_RSTPDWNMODULE); + + /* Set HS/FS Timeout Calibration to 7 (max available value). + * The number of PHY clocks that the application programs in + * this field is added to the high/full speed interpacket timeout + * duration in the core to account for any additional delays + * introduced by the PHY. This can be required, because the delay + * introduced by the PHY in generating the linestate condition + * can vary from one PHY to another. + */ + dwc2->gusbcfg |= (7ul << GUSBCFG_TOCAL_Pos); + + // Force device mode + dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FHMOD) | GUSBCFG_FDMOD; + + // Clear A override, force B Valid + dwc2->gotgctl = (dwc2->gotgctl & ~GOTGCTL_AVALOEN) | GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL; + + // If USB host misbehaves during status portion of control xfer + // (non zero-length packet), send STALL back and discard. + dwc2->dcfg |= DCFG_NZLSOHSK; + + fifo_flush_tx(dwc2, 0x10); // all tx fifo + fifo_flush_rx(dwc2); + + // Clear all interrupts + uint32_t int_mask = dwc2->gintsts; + dwc2->gintsts |= int_mask; + int_mask = dwc2->gotgint; + dwc2->gotgint |= int_mask; + + // Required as part of core initialization. + dwc2->gintmsk = GINTMSK_OTGINT | GINTMSK_RXFLVLM | + GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM; + + // Configure TX FIFO empty level for interrupt. Default is complete empty + dwc2->gahbcfg |= GAHBCFG_TXFELVL; + + // Enable global interrupt + dwc2->gahbcfg |= GAHBCFG_GINT; + + // make sure we are in device mode +// TU_ASSERT(!(dwc2->gintsts & GINTSTS_CMOD), ); + +// TU_LOG_HEX(DWC2_DEBUG, dwc2->gotgctl); +// TU_LOG_HEX(DWC2_DEBUG, dwc2->gusbcfg); +// TU_LOG_HEX(DWC2_DEBUG, dwc2->dcfg); +// TU_LOG_HEX(DWC2_DEBUG, dwc2->gahbcfg); + + dcd_connect(rhport); +} + +void dcd_int_enable(uint8_t rhport) { + dwc2_dcd_int_enable(rhport); +} + +void dcd_int_disable(uint8_t rhport) { + dwc2_dcd_int_disable(rhport); +} + +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + dwc2->dcfg = (dwc2->dcfg & ~DCFG_DAD_Msk) | (dev_addr << DCFG_DAD_Pos); + + // Response with status after changing device address + dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); +} + +void dcd_remote_wakeup(uint8_t rhport) { + (void) rhport; + + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + + // set remote wakeup + dwc2->dctl |= DCTL_RWUSIG; + + // enable SOF to detect bus resume + dwc2->gintsts = GINTSTS_SOF; + dwc2->gintmsk |= GINTMSK_SOFM; + + // Per specs: remote wakeup signal bit must be clear within 1-15ms + dwc2_remote_wakeup_delay(); + + dwc2->dctl &= ~DCTL_RWUSIG; +} + +void dcd_connect(uint8_t rhport) { + (void) rhport; + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + dwc2->dctl &= ~DCTL_SDIS; +} + +void dcd_disconnect(uint8_t rhport) { + (void) rhport; + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + dwc2->dctl |= DCTL_SDIS; +} + +// Be advised: audio, video and possibly other iso-ep classes use dcd_sof_enable() to enable/disable its corresponding ISR on purpose! +void dcd_sof_enable(uint8_t rhport, bool en) { + (void) rhport; + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + + _sof_en = en; + + if (en) { + dwc2->gintsts = GINTSTS_SOF; + dwc2->gintmsk |= GINTMSK_SOFM; + } else { + dwc2->gintmsk &= ~GINTMSK_SOFM; + } +} + +/*------------------------------------------------------------------*/ +/* DCD Endpoint port + *------------------------------------------------------------------*/ + +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) { + TU_ASSERT(fifo_alloc(rhport, desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt))); + edpt_activate(rhport, desc_edpt); + return true; +} + +// Close all non-control endpoints, cancel all pending transfers if any. +void dcd_edpt_close_all(uint8_t rhport) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + uint8_t const ep_count = _dwc2_controller[rhport].ep_count; + + // Disable non-control interrupt + dwc2->daintmsk = (1 << DAINTMSK_OEPM_Pos) | (1 << DAINTMSK_IEPM_Pos); + + for (uint8_t n = 1; n < ep_count; n++) { + // disable OUT endpoint + if (dwc2->epout[n].doepctl & DOEPCTL_EPENA) { + dwc2->epout[n].doepctl |= DOEPCTL_SNAK | DOEPCTL_EPDIS; + } + xfer_status[n][TUSB_DIR_OUT].max_size = 0; + + // disable IN endpoint + if (dwc2->epin[n].diepctl & DIEPCTL_EPENA) { + dwc2->epin[n].diepctl |= DIEPCTL_SNAK | DIEPCTL_EPDIS; + } + xfer_status[n][TUSB_DIR_IN].max_size = 0; + } + + // reset allocated fifo OUT + dwc2->grxfsiz = calc_grxfsiz(64, ep_count); + // reset allocated fifo IN + _allocated_fifo_words_tx = 16; + + fifo_flush_tx(dwc2, 0x10); // all tx fifo + fifo_flush_rx(dwc2); +} + +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + TU_ASSERT(fifo_alloc(rhport, ep_addr, largest_packet_size)); + return true; +} + +bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) { + // Disable EP to clear potential incomplete transfers + edpt_disable(rhport, p_endpoint_desc->bEndpointAddress, false); + + edpt_activate(rhport, p_endpoint_desc); + + return true; +} + +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir); + xfer->buffer = buffer; + xfer->ff = NULL; + xfer->total_len = total_bytes; + + // EP0 can only handle one packet + if (epnum == 0) { + ep0_pending[dir] = total_bytes; + + // Schedule the first transaction for EP0 transfer + edpt_schedule_packets(rhport, epnum, dir, 1, ep0_pending[dir]); + } else { + uint16_t num_packets = (total_bytes / xfer->max_size); + uint16_t const short_packet_size = total_bytes % xfer->max_size; + + // Zero-size packet is special case. + if ((short_packet_size > 0) || (total_bytes == 0)) num_packets++; + + // Schedule packets to be sent within interrupt + edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes); + } + + return true; +} + +// The number of bytes has to be given explicitly to allow more flexible control of how many +// bytes should be written and second to keep the return value free to give back a boolean +// success message. If total_bytes is too big, the FIFO will copy only what is available +// into the USB buffer! +bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t total_bytes) { + // USB buffers always work in bytes so to avoid unnecessary divisions we demand item_size = 1 + TU_ASSERT(ff->item_size == 1); + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir); + xfer->buffer = NULL; + xfer->ff = ff; + xfer->total_len = total_bytes; + + uint16_t num_packets = (total_bytes / xfer->max_size); + uint16_t const short_packet_size = total_bytes % xfer->max_size; + + // Zero-size packet is special case. + if (short_packet_size > 0 || (total_bytes == 0)) num_packets++; + + // Schedule packets to be sent within interrupt + edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes); + + return true; +} + +void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { + edpt_disable(rhport, ep_addr, false); +} + +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { + edpt_disable(rhport, ep_addr, true); +} + +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { + (void) rhport; + + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + // Clear stall and reset data toggle + if (dir == TUSB_DIR_IN) { + dwc2->epin[epnum].diepctl &= ~DIEPCTL_STALL; + dwc2->epin[epnum].diepctl |= DIEPCTL_SD0PID_SEVNFRM; + } else { + dwc2->epout[epnum].doepctl &= ~DOEPCTL_STALL; + dwc2->epout[epnum].doepctl |= DOEPCTL_SD0PID_SEVNFRM; + } +} + +/*------------------------------------------------------------------*/ + +// Read a single data packet from receive FIFO +static void read_fifo_packet(uint8_t rhport, uint8_t* dst, uint16_t len) { + (void) rhport; + + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + volatile const uint32_t* rx_fifo = dwc2->fifo[0]; + + // Reading full available 32 bit words from fifo + uint16_t full_words = len >> 2; + while (full_words--) { + tu_unaligned_write32(dst, *rx_fifo); + dst += 4; + } + + // Read the remaining 1-3 bytes from fifo + uint8_t const bytes_rem = len & 0x03; + if (bytes_rem != 0) { + uint32_t const tmp = *rx_fifo; + dst[0] = tu_u32_byte0(tmp); + if (bytes_rem > 1) dst[1] = tu_u32_byte1(tmp); + if (bytes_rem > 2) dst[2] = tu_u32_byte2(tmp); + } +} + +// Write a single data packet to EPIN FIFO +static void write_fifo_packet(uint8_t rhport, uint8_t fifo_num, uint8_t const* src, uint16_t len) { + (void) rhport; + + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + volatile uint32_t* tx_fifo = dwc2->fifo[fifo_num]; + + // Pushing full available 32 bit words to fifo + uint16_t full_words = len >> 2; + while (full_words--) { + *tx_fifo = tu_unaligned_read32(src); + src += 4; + } + + // Write the remaining 1-3 bytes into fifo + uint8_t const bytes_rem = len & 0x03; + if (bytes_rem) { + uint32_t tmp_word = src[0]; + if (bytes_rem > 1) tmp_word |= (src[1] << 8); + if (bytes_rem > 2) tmp_word |= (src[2] << 16); + + *tx_fifo = tmp_word; + } +} + +static void handle_rxflvl_irq(uint8_t rhport) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + volatile uint32_t const* rx_fifo = dwc2->fifo[0]; + + // Pop control word off FIFO + uint32_t const ctl_word = dwc2->grxstsp; + uint8_t const pktsts = (ctl_word & GRXSTSP_PKTSTS_Msk) >> GRXSTSP_PKTSTS_Pos; + uint8_t const epnum = (ctl_word & GRXSTSP_EPNUM_Msk) >> GRXSTSP_EPNUM_Pos; + uint16_t const bcnt = (ctl_word & GRXSTSP_BCNT_Msk) >> GRXSTSP_BCNT_Pos; + + dwc2_epout_t* epout = &dwc2->epout[epnum]; + +//#if CFG_TUSB_DEBUG >= DWC2_DEBUG +// const char * pktsts_str[] = +// { +// "ASSERT", "Global NAK (ISR)", "Out Data Received", "Out Transfer Complete (ISR)", +// "Setup Complete (ISR)", "ASSERT", "Setup Data Received" +// }; +// TU_LOG_LOCATION(); +// TU_LOG(DWC2_DEBUG, " EP %02X, Byte Count %u, %s\r\n", epnum, bcnt, pktsts_str[pktsts]); +// TU_LOG(DWC2_DEBUG, " daint = %08lX, doepint = %04X\r\n", (unsigned long) dwc2->daint, (unsigned int) epout->doepint); +//#endif + + switch (pktsts) { + // Global OUT NAK: do nothing + case GRXSTS_PKTSTS_GLOBALOUTNAK: + break; + + case GRXSTS_PKTSTS_SETUPRX: + // Setup packet received + + // We can receive up to three setup packets in succession, but + // only the last one is valid. + _setup_packet[0] = (*rx_fifo); + _setup_packet[1] = (*rx_fifo); + break; + + case GRXSTS_PKTSTS_SETUPDONE: + // Setup packet done (Interrupt) + epout->doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos); + break; + + case GRXSTS_PKTSTS_OUTRX: { + // Out packet received + xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT); + + // Read packet off RxFIFO + if (xfer->ff) { + // Ring buffer + tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void*) (uintptr_t) rx_fifo, bcnt); + } else { + // Linear buffer + read_fifo_packet(rhport, xfer->buffer, bcnt); + + // Increment pointer to xfer data + xfer->buffer += bcnt; + } + + // Truncate transfer length in case of short packet + if (bcnt < xfer->max_size) { + xfer->total_len -= (epout->doeptsiz & DOEPTSIZ_XFRSIZ_Msk) >> DOEPTSIZ_XFRSIZ_Pos; + if (epnum == 0) { + xfer->total_len -= ep0_pending[TUSB_DIR_OUT]; + ep0_pending[TUSB_DIR_OUT] = 0; + } + } + } + break; + + // Out packet done (Interrupt) + case GRXSTS_PKTSTS_OUTDONE: + // Occurred on STM32L47 with dwc2 version 3.10a but not found on other version like 2.80a or 3.30a + // May (or not) be 3.10a specific feature/bug or depending on MCU configuration + // XFRC complete is additionally generated when + // - setup packet is received + // - complete the data stage of control write is complete + if ((epnum == 0) && (bcnt == 0) && (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a)) { + uint32_t doepint = epout->doepint; + + if (doepint & (DOEPINT_STPKTRX | DOEPINT_OTEPSPR)) { + // skip this "no-data" transfer complete event + // Note: STPKTRX will be clear later by setup received handler + uint32_t clear_flags = DOEPINT_XFRC; + + if (doepint & DOEPINT_OTEPSPR) clear_flags |= DOEPINT_OTEPSPR; + + epout->doepint = clear_flags; + + // TU_LOG(DWC2_DEBUG, " FIX extra transfer complete on setup/data compete\r\n"); + } + } + break; + + default: // Invalid + TU_BREAKPOINT(); + break; + } +} + +static void handle_epout_irq(uint8_t rhport) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + uint8_t const ep_count = _dwc2_controller[rhport].ep_count; + + // DAINT for a given EP clears when DOEPINTx is cleared. + // OEPINT will be cleared when DAINT's out bits are cleared. + for (uint8_t n = 0; n < ep_count; n++) { + if (dwc2->daint & TU_BIT(DAINT_OEPINT_Pos + n)) { + dwc2_epout_t* epout = &dwc2->epout[n]; + + uint32_t const doepint = epout->doepint; + + // SETUP packet Setup Phase done. + if (doepint & DOEPINT_STUP) { + uint32_t clear_flag = DOEPINT_STUP; + + // STPKTRX is only available for version from 3_00a + if ((doepint & DOEPINT_STPKTRX) && (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a)) { + clear_flag |= DOEPINT_STPKTRX; + } + + epout->doepint = clear_flag; + dcd_event_setup_received(rhport, (uint8_t*) _setup_packet, true); + } + + // OUT XFER complete + if (epout->doepint & DOEPINT_XFRC) { + epout->doepint = DOEPINT_XFRC; + + xfer_ctl_t* xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT); + + // EP0 can only handle one packet + if ((n == 0) && ep0_pending[TUSB_DIR_OUT]) { + // Schedule another packet to be received. + edpt_schedule_packets(rhport, n, TUSB_DIR_OUT, 1, ep0_pending[TUSB_DIR_OUT]); + } else { + dcd_event_xfer_complete(rhport, n, xfer->total_len, XFER_RESULT_SUCCESS, true); + } + } + } + } +} + +static void handle_epin_irq(uint8_t rhport) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + uint8_t const ep_count = _dwc2_controller[rhport].ep_count; + dwc2_epin_t* epin = dwc2->epin; + + // DAINT for a given EP clears when DIEPINTx is cleared. + // IEPINT will be cleared when DAINT's out bits are cleared. + for (uint8_t n = 0; n < ep_count; n++) { + if (dwc2->daint & TU_BIT(DAINT_IEPINT_Pos + n)) { + // IN XFER complete (entire xfer). + xfer_ctl_t* xfer = XFER_CTL_BASE(n, TUSB_DIR_IN); + + if (epin[n].diepint & DIEPINT_XFRC) { + epin[n].diepint = DIEPINT_XFRC; + + // EP0 can only handle one packet + if ((n == 0) && ep0_pending[TUSB_DIR_IN]) { + // Schedule another packet to be transmitted. + edpt_schedule_packets(rhport, n, TUSB_DIR_IN, 1, ep0_pending[TUSB_DIR_IN]); + } else { + dcd_event_xfer_complete(rhport, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true); + } + } + + // XFER FIFO empty + if ((epin[n].diepint & DIEPINT_TXFE) && (dwc2->diepempmsk & (1 << n))) { + // diepint's TXFE bit is read-only, software cannot clear it. + // It will only be cleared by hardware when written bytes is more than + // - 64 bytes or + // - Half of TX FIFO size (configured by DIEPTXF) + + uint16_t remaining_packets = (epin[n].dieptsiz & DIEPTSIZ_PKTCNT_Msk) >> DIEPTSIZ_PKTCNT_Pos; + + // Process every single packet (only whole packets can be written to fifo) + for (uint16_t i = 0; i < remaining_packets; i++) { + uint16_t const remaining_bytes = (epin[n].dieptsiz & DIEPTSIZ_XFRSIZ_Msk) >> DIEPTSIZ_XFRSIZ_Pos; + + // Packet can not be larger than ep max size + uint16_t const packet_size = tu_min16(remaining_bytes, xfer->max_size); + + // It's only possible to write full packets into FIFO. Therefore DTXFSTS register of current + // EP has to be checked if the buffer can take another WHOLE packet + if (packet_size > ((epin[n].dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) break; + + // Push packet to Tx-FIFO + if (xfer->ff) { + volatile uint32_t* tx_fifo = dwc2->fifo[n]; + tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*) (uintptr_t) tx_fifo, packet_size); + } else { + write_fifo_packet(rhport, n, xfer->buffer, packet_size); + + // Increment pointer to xfer data + xfer->buffer += packet_size; + } + } + + // Turn off TXFE if all bytes are written. + if (((epin[n].dieptsiz & DIEPTSIZ_XFRSIZ_Msk) >> DIEPTSIZ_XFRSIZ_Pos) == 0) { + dwc2->diepempmsk &= ~(1 << n); + } + } + } + } +} + +void dcd_int_handler(uint8_t rhport) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + + uint32_t const int_mask = dwc2->gintmsk; + uint32_t const int_status = dwc2->gintsts & int_mask; + + if (int_status & GINTSTS_USBRST) { + // USBRST is start of reset. + dwc2->gintsts = GINTSTS_USBRST; + bus_reset(rhport); + } + + if (int_status & GINTSTS_ENUMDNE) { + // ENUMDNE is the end of reset where speed of the link is detected + dwc2->gintsts = GINTSTS_ENUMDNE; + + tusb_speed_t speed; + switch ((dwc2->dsts & DSTS_ENUMSPD_Msk) >> DSTS_ENUMSPD_Pos) { + case DSTS_ENUMSPD_HS: + speed = TUSB_SPEED_HIGH; + break; + + case DSTS_ENUMSPD_LS: + speed = TUSB_SPEED_LOW; + break; + + case DSTS_ENUMSPD_FS_HSPHY: + case DSTS_ENUMSPD_FS: + default: + speed = TUSB_SPEED_FULL; + break; + } + + // TODO must update GUSBCFG_TRDT according to link speed + + dcd_event_bus_reset(rhport, speed, true); + } + + if (int_status & GINTSTS_USBSUSP) { + dwc2->gintsts = GINTSTS_USBSUSP; + dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); + } + + if (int_status & GINTSTS_WKUINT) { + dwc2->gintsts = GINTSTS_WKUINT; + dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); + } + + // TODO check GINTSTS_DISCINT for disconnect detection + // if(int_status & GINTSTS_DISCINT) + + if (int_status & GINTSTS_OTGINT) { + // OTG INT bit is read-only + uint32_t const otg_int = dwc2->gotgint; + + if (otg_int & GOTGINT_SEDET) { + dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true); + } + + dwc2->gotgint = otg_int; + } + + if(int_status & GINTSTS_SOF) { + dwc2->gintsts = GINTSTS_SOF; + const uint32_t frame = (dwc2->dsts & DSTS_FNSOF) >> DSTS_FNSOF_Pos; + + // Disable SOF interrupt if SOF was not explicitly enabled since SOF was used for remote wakeup detection + if (!_sof_en) { + dwc2->gintmsk &= ~GINTMSK_SOFM; + } + + dcd_event_sof(rhport, frame, true); + } + + // RxFIFO non-empty interrupt handling. + if (int_status & GINTSTS_RXFLVL) { + // RXFLVL bit is read-only + + // Mask out RXFLVL while reading data from FIFO + dwc2->gintmsk &= ~GINTMSK_RXFLVLM; + + // Loop until all available packets were handled + do { + handle_rxflvl_irq(rhport); + } while(dwc2->gintsts & GINTSTS_RXFLVL); + + dwc2->gintmsk |= GINTMSK_RXFLVLM; + } + + // OUT endpoint interrupt handling. + if (int_status & GINTSTS_OEPINT) { + // OEPINT is read-only, clear using DOEPINTn + handle_epout_irq(rhport); + } + + // IN endpoint interrupt handling. + if (int_status & GINTSTS_IEPINT) { + // IEPINT bit read-only, clear using DIEPINTn + handle_epin_irq(rhport); + } + + // // Check for Incomplete isochronous IN transfer + // if(int_status & GINTSTS_IISOIXFR) { + // printf(" IISOIXFR!\r\n"); + //// TU_LOG(DWC2_DEBUG, " IISOIXFR!\r\n"); + // } +} + +#endif diff --git a/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_bcm.h b/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_bcm.h new file mode 100644 index 00000000..732d96ae --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_bcm.h @@ -0,0 +1,89 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021, Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_DWC2_BCM_H_ +#define _TUSB_DWC2_BCM_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include "broadcom/defines.h" +#include "broadcom/interrupts.h" +#include "broadcom/caches.h" + +#define DWC2_EP_MAX 8 + +static const dwc2_controller_t _dwc2_controller[] = +{ + { .reg_base = USB_OTG_GLOBAL_BASE, .irqnum = USB_IRQn, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 4096 } +}; + +#define dcache_clean(_addr, _size) data_clean(_addr, _size) +#define dcache_invalidate(_addr, _size) data_invalidate(_addr, _size) +#define dcache_clean_invalidate(_addr, _size) data_clean_and_invalidate(_addr, _size) + +TU_ATTR_ALWAYS_INLINE +static inline void dwc2_dcd_int_enable(uint8_t rhport) +{ + BP_EnableIRQ(_dwc2_controller[rhport].irqnum); +} + +TU_ATTR_ALWAYS_INLINE +static inline void dwc2_dcd_int_disable (uint8_t rhport) +{ + BP_DisableIRQ(_dwc2_controller[rhport].irqnum); +} + +static inline void dwc2_remote_wakeup_delay(void) +{ + // try to delay for 1 ms + // TODO implement later +} + +// MCU specific PHY init, called BEFORE core reset +static inline void dwc2_phy_init(dwc2_regs_t * dwc2, uint8_t hs_phy_type) +{ + (void) dwc2; + (void) hs_phy_type; + + // nothing to do +} + +// MCU specific PHY update, it is called AFTER init() and core reset +static inline void dwc2_phy_update(dwc2_regs_t * dwc2, uint8_t hs_phy_type) +{ + (void) dwc2; + (void) hs_phy_type; + + // nothing to do +} + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_efm32.h b/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_efm32.h new file mode 100644 index 00000000..0e3570cb --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_efm32.h @@ -0,0 +1,89 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 Rafael Silva (@perigoso) + * Copyright (c) 2021, Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _DWC2_EFM32_H_ +#define _DWC2_EFM32_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include "em_device.h" + +// EFM32 has custom control register before DWC registers +#define DWC2_REG_BASE (USB_BASE + offsetof(USB_TypeDef, GOTGCTL)) +#define DWC2_EP_MAX 7 + +static const dwc2_controller_t _dwc2_controller[] = +{ + { .reg_base = DWC2_REG_BASE, .irqnum = USB_IRQn, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 2048 } +}; + +TU_ATTR_ALWAYS_INLINE +static inline void dwc2_dcd_int_enable(uint8_t rhport) +{ + NVIC_EnableIRQ(_dwc2_controller[rhport].irqnum); +} + +TU_ATTR_ALWAYS_INLINE +static inline void dwc2_dcd_int_disable (uint8_t rhport) +{ + NVIC_DisableIRQ(_dwc2_controller[rhport].irqnum); +} + +static inline void dwc2_remote_wakeup_delay(void) +{ + // try to delay for 1 ms +// uint32_t count = SystemCoreClock / 1000; +// while ( count-- ) __NOP(); +} + +// MCU specific PHY init, called BEFORE core reset +static inline void dwc2_phy_init(dwc2_regs_t * dwc2, uint8_t hs_phy_type) +{ + (void) dwc2; + (void) hs_phy_type; + + // Enable PHY + USB->ROUTE = USB_ROUTE_PHYPEN; +} + +// MCU specific PHY update, it is called AFTER init() and core reset +static inline void dwc2_phy_update(dwc2_regs_t * dwc2, uint8_t hs_phy_type) +{ + (void) dwc2; + (void) hs_phy_type; + + // EFM32 Manual: turn around must be 5 (reset & default value) + // dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_TRDT_Msk) | (5u << GUSBCFG_TRDT_Pos); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_esp32.h b/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_esp32.h new file mode 100644 index 00000000..c50dd66b --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_esp32.h @@ -0,0 +1,96 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021, Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + + +#ifndef _DWC2_ESP32_H_ +#define _DWC2_ESP32_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include "esp_intr_alloc.h" +#include "soc/periph_defs.h" +//#include "soc/usb_periph.h" + +#define DWC2_REG_BASE 0x60080000UL +#define DWC2_EP_MAX 6 // USB_OUT_EP_NUM. TODO ESP32Sx only has 5 tx fifo (5 endpoint IN) + +static const dwc2_controller_t _dwc2_controller[] = +{ + { .reg_base = DWC2_REG_BASE, .irqnum = 0, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 1024 } +}; + +static intr_handle_t usb_ih; + +static void dcd_int_handler_wrap(void* arg) +{ + (void) arg; + dcd_int_handler(0); +} + +TU_ATTR_ALWAYS_INLINE +static inline void dwc2_dcd_int_enable (uint8_t rhport) +{ + (void) rhport; + esp_intr_alloc(ETS_USB_INTR_SOURCE, ESP_INTR_FLAG_LOWMED, dcd_int_handler_wrap, NULL, &usb_ih); +} + +TU_ATTR_ALWAYS_INLINE +static inline void dwc2_dcd_int_disable (uint8_t rhport) +{ + (void) rhport; + esp_intr_free(usb_ih); +} + +static inline void dwc2_remote_wakeup_delay(void) +{ + vTaskDelay(pdMS_TO_TICKS(1)); +} + +// MCU specific PHY init, called BEFORE core reset +static inline void dwc2_phy_init(dwc2_regs_t * dwc2, uint8_t hs_phy_type) +{ + (void) dwc2; + (void) hs_phy_type; + + // nothing to do +} + +// MCU specific PHY update, it is called AFTER init() and core reset +static inline void dwc2_phy_update(dwc2_regs_t * dwc2, uint8_t hs_phy_type) +{ + (void) dwc2; + (void) hs_phy_type; + + // nothing to do +} + +#ifdef __cplusplus +} +#endif + +#endif /* _DWC2_ESP32_H_ */ diff --git a/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_gd32.h b/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_gd32.h new file mode 100644 index 00000000..0375fffe --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_gd32.h @@ -0,0 +1,101 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021, Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + + +#ifndef DWC2_GD32_H_ +#define DWC2_GD32_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#define DWC2_REG_BASE 0x50000000UL +#define DWC2_EP_MAX 4 + +static const dwc2_controller_t _dwc2_controller[] = +{ + { .reg_base = DWC2_REG_BASE, .irqnum = 86, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 1280 } +}; + +extern uint32_t SystemCoreClock; + +// The GD32VF103 is a RISC-V MCU, which implements the ECLIC Core-Local +// Interrupt Controller by Nuclei. It is nearly API compatible to the +// NVIC used by ARM MCUs. +#define ECLIC_INTERRUPT_ENABLE_BASE 0xD2001001UL + +TU_ATTR_ALWAYS_INLINE +static inline void __eclic_enable_interrupt (uint32_t irq) { + *(volatile uint8_t*)(ECLIC_INTERRUPT_ENABLE_BASE + (irq * 4)) = 1; +} + +TU_ATTR_ALWAYS_INLINE +static inline void __eclic_disable_interrupt (uint32_t irq){ + *(volatile uint8_t*)(ECLIC_INTERRUPT_ENABLE_BASE + (irq * 4)) = 0; +} + +TU_ATTR_ALWAYS_INLINE +static inline void dwc2_dcd_int_enable(uint8_t rhport) +{ + __eclic_enable_interrupt(_dwc2_controller[rhport].irqnum); +} + +TU_ATTR_ALWAYS_INLINE +static inline void dwc2_dcd_int_disable (uint8_t rhport) +{ + __eclic_disable_interrupt(_dwc2_controller[rhport].irqnum); +} + +static inline void dwc2_remote_wakeup_delay(void) +{ + // try to delay for 1 ms + uint32_t count = SystemCoreClock / 1000; + while ( count-- ) __asm volatile ("nop"); +} + +// MCU specific PHY init, called BEFORE core reset +static inline void dwc2_phy_init(dwc2_regs_t * dwc2, uint8_t hs_phy_type) +{ + (void) dwc2; + (void) hs_phy_type; + + // nothing to do +} + +// MCU specific PHY update, it is called AFTER init() and core reset +static inline void dwc2_phy_update(dwc2_regs_t * dwc2, uint8_t hs_phy_type) +{ + (void) dwc2; + (void) hs_phy_type; + + // nothing to do +} + +#ifdef __cplusplus +} +#endif + +#endif /* DWC2_GD32_H_ */ diff --git a/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_info.md b/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_info.md new file mode 100644 index 00000000..8690a075 --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_info.md @@ -0,0 +1,55 @@ +| | BCM2711 (Pi4) | EFM32GG FullSpeed | ESP32-S2 | STM32F407 Fullspeed | STM32F407 Highspeed | STM32F411 Fullspeed | STM32F412 Fullspeed | STM32F429 Fullspeed | STM32F429 Highspeed | STM32F723 Fullspeed | STM32F723 HighSpeed | STM32F767 Fullspeed | STM32H743 Highspeed | STM32L476 Fullspeed | STM32U5A5 Highspeed | GD32VF103 Fullspeed | XMC4500 | +|:----------------------------|:----------------|:--------------------|:-----------|:----------------------|:----------------------|:----------------------|:----------------------|:----------------------|:----------------------|:----------------------|:----------------------|:----------------------|:----------------------|:----------------------|:----------------------|:----------------------|:-----------| +| guid | 0x2708A000 | 0x00000000 | 0x00000000 | 0x00001200 | 0x00001100 | 0x00001200 | 0x00002000 | 0x00001200 | 0x00001100 | 0x00003000 | 0x00003100 | 0x00002000 | 0x00002300 | 0x00002000 | 0x00005000 | 0x00001000 | 0x00AEC000 | +| gsnpsid | 0x4F54280A | 0x4F54330A | 0x4F54400A | 0x4F54281A | 0x4F54281A | 0x4F54281A | 0x4F54320A | 0x4F54281A | 0x4F54281A | 0x4F54330A | 0x4F54330A | 0x4F54320A | 0x4F54330A | 0x4F54310A | 0x4F54411A | 0x00000000 | 0x4F54292A | +| - specs version | 2.80a | 3.30a | 4.00a | 2.81a | 2.81a | 2.81a | 3.20a | 2.81a | 2.81a | 3.30a | 3.30a | 3.20a | 3.30a | 3.10a | 4.11a | 0.00W | 2.92a | +| ghwcfg1 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | +| ghwcfg2 | 0x228DDD50 | 0x228F5910 | 0x224DD930 | 0x229DCD20 | 0x229ED590 | 0x229DCD20 | 0x229ED520 | 0x229DCD20 | 0x229ED590 | 0x229ED520 | 0x229FE1D0 | 0x229ED520 | 0x229FE190 | 0x229ED520 | 0x228FE052 | 0x00000000 | 0x228F5930 | +| - op_mode | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 2 | 0 | 0 | +| - arch | 2 | 2 | 2 | 0 | 2 | 0 | 0 | 0 | 2 | 0 | 2 | 0 | 2 | 0 | 2 | 0 | 2 | +| - point2point | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | +| - hs_phy_type | 1 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | 2 | 0 | 3 | 0 | 2 | 0 | 1 | 0 | 0 | +| - fs_phy_type | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | +| - num_dev_ep | 7 | 6 | 6 | 3 | 5 | 3 | 5 | 3 | 5 | 5 | 8 | 5 | 8 | 5 | 8 | 0 | 6 | +| - num_host_ch | 7 | 13 | 7 | 7 | 11 | 7 | 11 | 7 | 11 | 11 | 15 | 11 | 15 | 11 | 15 | 0 | 13 | +| - period_channel_support | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | +| - enable_dynamic_fifo | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | +| - mul_cpu_int | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | +| - reserved21 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - nperiod_tx_q_depth | 2 | 2 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 0 | 2 | +| - host_period_tx_q_depth | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 0 | 2 | +| - dev_token_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 | 8 | +| - otg_enable_ic_usb | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| ghwcfg3 | 0x0FF000E8 | 0x01F204E8 | 0x00C804B5 | 0x020001E8 | 0x03F403E8 | 0x020001E8 | 0x0200D1E8 | 0x020001E8 | 0x03F403E8 | 0x0200D1E8 | 0x03EED2E8 | 0x0200D1E8 | 0x03B8D2E8 | 0x0200D1E8 | 0x03B882E8 | 0x00000000 | 0x027A01E5 | +| - xfer_size_width | 8 | 8 | 5 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 | 5 | +| - packet_size_width | 6 | 6 | 3 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 0 | 6 | +| - otg_enable | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | +| - i2c_enable | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | +| - vendor_ctrl_itf | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | +| - optional_feature_removed | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - synch_reset | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - otg_adp_support | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | +| - otg_enable_hsic | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - battery_charger_support | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | +| - lpm_mode | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | +| - total_fifo_size | 4080 | 498 | 200 | 512 | 1012 | 512 | 512 | 512 | 1012 | 512 | 1006 | 512 | 952 | 512 | 952 | 0 | 634 | +| ghwcfg4 | 0x1FF00020 | 0x1BF08030 | 0xD3F0A030 | 0x0FF08030 | 0x17F00030 | 0x0FF08030 | 0x17F08030 | 0x0FF08030 | 0x17F00030 | 0x17F08030 | 0x23F00030 | 0x17F08030 | 0xE3F00030 | 0x17F08030 | 0xE2103E30 | 0x00000000 | 0xDBF08030 | +| - num_dev_period_in_ep | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - power_optimized | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | +| - ahb_freq_min | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | +| - hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - reserved7 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 4 | 0 | 0 | +| - service_interval_mode | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +| - ipg_isoc_en | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +| - acg_enable | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +| - reserved13 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +| - utmi_phy_data_width | 0 | 2 | 2 | 2 | 0 | 2 | 2 | 2 | 0 | 2 | 0 | 2 | 0 | 2 | 0 | 0 | 2 | +| - dev_ctrl_ep_num | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - iddg_filter_enabled | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | +| - vbus_valid_filter_enabled | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | +| - a_valid_filter_enabled | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | +| - b_valid_filter_enabled | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | +| - dedicated_fifos | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | +| - num_dev_in_eps | 15 | 13 | 9 | 7 | 11 | 7 | 11 | 7 | 11 | 11 | 1 | 11 | 1 | 11 | 1 | 0 | 13 | +| - dma_desc_enable | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | +| - dma_dynamic | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | diff --git a/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_info.py b/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_info.py new file mode 100644 index 00000000..55bec3d2 --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_info.py @@ -0,0 +1,169 @@ +import click +import ctypes +import pandas as pd + +# hex value for register: guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4 +dwc2_reg_list = ['guid', 'gsnpsid', 'ghwcfg1', 'ghwcfg2', 'ghwcfg3', 'ghwcfg4'] +dwc2_reg_value = { + 'BCM2711 (Pi4)': [0x2708A000, 0x4F54280A, 0, 0x228DDD50, 0xFF000E8, 0x1FF00020], + 'EFM32GG FullSpeed': [0, 0x4F54330A, 0, 0x228F5910, 0x1F204E8, 0x1BF08030], + 'ESP32-S2': [0, 0x4F54400A, 0, 0x224DD930, 0xC804B5, 0xD3F0A030], + 'STM32F407 Fullspeed': [0x1200, 0x4F54281A, 0, 0x229DCD20, 0x20001E8, 0xFF08030], + 'STM32F407 Highspeed': [0x1100, 0x4F54281A, 0, 0x229ED590, 0x3F403E8, 0x17F00030], + 'STM32F411 Fullspeed': [0x1200, 0x4F54281A, 0, 0x229DCD20, 0x20001E8, 0xFF08030], + 'STM32F412 Fullspeed': [0x2000, 0x4F54320A, 0, 0x229ED520, 0x200D1E8, 0x17F08030], + 'STM32F429 Fullspeed': [0x1200, 0x4F54281A, 0, 0x229DCD20, 0x20001E8, 0xFF08030], + 'STM32F429 Highspeed': [0x1100, 0x4F54281A, 0, 0x229ED590, 0x3F403E8, 0x17F00030], + 'STM32F723 Fullspeed': [0x3000, 0x4F54330A, 0, 0x229ED520, 0x200D1E8, 0x17F08030], + 'STM32F723 HighSpeed': [0x3100, 0x4F54330A, 0, 0x229FE1D0, 0x3EED2E8, 0x23F00030], + 'STM32F767 Fullspeed': [0x2000, 0x4F54320A, 0, 0x229ED520, 0x200D1E8, 0x17F08030], + 'STM32H743 Highspeed': [0x2300, 0x4F54330A, 0, 0x229FE190, 0x3B8D2E8, 0xE3F00030], # both HS cores + 'STM32L476 Fullspeed': [0x2000, 0x4F54310A, 0, 0x229ED520, 0x200D1E8, 0x17F08030], + 'STM32U5A5 Highspeed': [0x00005000, 0x4F54411A, 0x00000000, 0x228FE052, 0x03B882E8, 0xE2103E30], + 'GD32VF103 Fullspeed': [0x1000, 0, 0, 0, 0, 0], + 'XMC4500': [0xAEC000, 0x4F54292A, 0, 0x228F5930, 0x27A01E5, 0xDBF08030] +} + +# Combine dwc2_info with dwc2_reg_list +# dwc2_info = { +# 'BCM2711 (Pi4)': { +# 'guid': 0x2708A000, +# 'gsnpsid': 0x4F54280A, +# 'ghwcfg1': 0, +# 'ghwcfg2': 0x228DDD50, +# 'ghwcfg3': 0xFF000E8, +# 'ghwcfg4': 0x1FF00020 +# }, +dwc2_info = {key: {field: value for field, value in zip(dwc2_reg_list, values)} for key, values in dwc2_reg_value.items()} + + +class GHWCFG2(ctypes.LittleEndianStructure): + _fields_ = [ + ("op_mode", ctypes.c_uint32, 3), + ("arch", ctypes.c_uint32, 2), + ("point2point", ctypes.c_uint32, 1), + ("hs_phy_type", ctypes.c_uint32, 2), + ("fs_phy_type", ctypes.c_uint32, 2), + ("num_dev_ep", ctypes.c_uint32, 4), + ("num_host_ch", ctypes.c_uint32, 4), + ("period_channel_support", ctypes.c_uint32, 1), + ("enable_dynamic_fifo", ctypes.c_uint32, 1), + ("mul_cpu_int", ctypes.c_uint32, 1), + ("reserved21", ctypes.c_uint32, 1), + ("nperiod_tx_q_depth", ctypes.c_uint32, 2), + ("host_period_tx_q_depth", ctypes.c_uint32, 2), + ("dev_token_q_depth", ctypes.c_uint32, 5), + ("otg_enable_ic_usb", ctypes.c_uint32, 1) + ] + + +class GHWCFG3(ctypes.LittleEndianStructure): + _fields_ = [ + ("xfer_size_width", ctypes.c_uint32, 4), + ("packet_size_width", ctypes.c_uint32, 3), + ("otg_enable", ctypes.c_uint32, 1), + ("i2c_enable", ctypes.c_uint32, 1), + ("vendor_ctrl_itf", ctypes.c_uint32, 1), + ("optional_feature_removed", ctypes.c_uint32, 1), + ("synch_reset", ctypes.c_uint32, 1), + ("otg_adp_support", ctypes.c_uint32, 1), + ("otg_enable_hsic", ctypes.c_uint32, 1), + ("battery_charger_support", ctypes.c_uint32, 1), + ("lpm_mode", ctypes.c_uint32, 1), + ("total_fifo_size", ctypes.c_uint32, 16) + ] + + +class GHWCFG4(ctypes.LittleEndianStructure): + _fields_ = [ + ("num_dev_period_in_ep", ctypes.c_uint32, 4), + ("power_optimized", ctypes.c_uint32, 1), + ("ahb_freq_min", ctypes.c_uint32, 1), + ("hibernation", ctypes.c_uint32, 1), + ("reserved7", ctypes.c_uint32, 3), + ("service_interval_mode", ctypes.c_uint32, 1), + ("ipg_isoc_en", ctypes.c_uint32, 1), + ("acg_enable", ctypes.c_uint32, 1), + ("reserved13", ctypes.c_uint32, 1), + ("utmi_phy_data_width", ctypes.c_uint32, 2), + ("dev_ctrl_ep_num", ctypes.c_uint32, 4), + ("iddg_filter_enabled", ctypes.c_uint32, 1), + ("vbus_valid_filter_enabled", ctypes.c_uint32, 1), + ("a_valid_filter_enabled", ctypes.c_uint32, 1), + ("b_valid_filter_enabled", ctypes.c_uint32, 1), + ("dedicated_fifos", ctypes.c_uint32, 1), + ("num_dev_in_eps", ctypes.c_uint32, 4), + ("dma_desc_enable", ctypes.c_uint32, 1), + ("dma_dynamic", ctypes.c_uint32, 1) + ] + + +@click.group() +def cli(): + pass + + +@cli.command() +@click.argument('mcus', nargs=-1) +@click.option('-a', '--all', is_flag=True, help='Print all bit-field values') +def info(mcus, all): + """Print DWC2 register values for given MCU(s)""" + if len(mcus) == 0: + mcus = dwc2_info + + for mcu in mcus: + for entry in dwc2_info: + if mcu.lower() in entry.lower(): + print(f"## {entry}") + for r_name, r_value in dwc2_info[entry].items(): + print(f"{r_name} = 0x{r_value:08X}") + # Print bit-field values + if all and r_name.upper() in globals(): + class_name = globals()[r_name.upper()] + ghwcfg = class_name.from_buffer_copy(r_value.to_bytes(4, byteorder='little')) + for field_name, field_type, _ in class_name._fields_: + print(f" {field_name} = {getattr(ghwcfg, field_name)}") + + +@cli.command() +def render_md(): + """Render dwc2_info to Markdown table""" + # Create an empty list to hold the dictionaries + dwc2_info_list = [] + + # Iterate over the dwc2_info dictionary and extract fields + for device, reg_values in dwc2_info.items(): + entry_dict = {"Device": device} + for r_name, r_value in reg_values.items(): + entry_dict[r_name] = f"0x{r_value:08X}" + + if r_name == 'gsnpsid': + # Get dwc2 specs version + major = ((r_value >> 8) >> 4) & 0x0F + minor = (r_value >> 4) & 0xFF + patch = chr((r_value & 0x0F) + ord('a') - 0xA) + entry_dict[f' - specs version'] = f"{major:X}.{minor:02X}{patch}" + elif r_name.upper() in globals(): + # Get bit-field values which exist as ctypes structures + class_name = globals()[r_name.upper()] + ghwcfg = class_name.from_buffer_copy(r_value.to_bytes(4, byteorder='little')) + for field_name, field_type, _ in class_name._fields_: + entry_dict[f' - {field_name}'] = getattr(ghwcfg, field_name) + + dwc2_info_list.append(entry_dict) + + # Create a Pandas DataFrame from the list of dictionaries + df = pd.DataFrame(dwc2_info_list).set_index('Device') + + # Transpose the DataFrame to switch rows and columns + df = df.T + #print(df) + + # Write the Markdown table to a file + with open('dwc2_info.md', 'w') as md_file: + md_file.write(df.to_markdown()) + md_file.write('\n') + + +if __name__ == '__main__': + cli() diff --git a/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_stm32.h b/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_stm32.h new file mode 100644 index 00000000..3237a50f --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_stm32.h @@ -0,0 +1,261 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021, Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef DWC2_STM32_H_ +#define DWC2_STM32_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// EP_MAX : Max number of bi-directional endpoints including EP0 +// EP_FIFO_SIZE : Size of dedicated USB SRAM +#if CFG_TUSB_MCU == OPT_MCU_STM32F1 + #include "stm32f1xx.h" + #define EP_MAX_FS 4 + #define EP_FIFO_SIZE_FS 1280 + +#elif CFG_TUSB_MCU == OPT_MCU_STM32F2 + #include "stm32f2xx.h" + #define EP_MAX_FS USB_OTG_FS_MAX_IN_ENDPOINTS + #define EP_FIFO_SIZE_FS USB_OTG_FS_TOTAL_FIFO_SIZE + + #define EP_MAX_HS USB_OTG_HS_MAX_IN_ENDPOINTS + #define EP_FIFO_SIZE_HS USB_OTG_HS_TOTAL_FIFO_SIZE + +#elif CFG_TUSB_MCU == OPT_MCU_STM32F4 + #include "stm32f4xx.h" + #define EP_MAX_FS USB_OTG_FS_MAX_IN_ENDPOINTS + #define EP_FIFO_SIZE_FS USB_OTG_FS_TOTAL_FIFO_SIZE + + #define EP_MAX_HS USB_OTG_HS_MAX_IN_ENDPOINTS + #define EP_FIFO_SIZE_HS USB_OTG_HS_TOTAL_FIFO_SIZE + +#elif CFG_TUSB_MCU == OPT_MCU_STM32H7 + #include "stm32h7xx.h" + #define EP_MAX_FS 9 + #define EP_FIFO_SIZE_FS 4096 + + #define EP_MAX_HS 9 + #define EP_FIFO_SIZE_HS 4096 + + // NOTE: H7 with only 1 USB port: H72x / H73x / H7Ax / H7Bx + // USB_OTG_FS_PERIPH_BASE and OTG_FS_IRQn not defined + #if (! defined USB2_OTG_FS) + #define USB_OTG_FS_PERIPH_BASE USB1_OTG_HS_PERIPH_BASE + #define OTG_FS_IRQn OTG_HS_IRQn + #endif + +#elif CFG_TUSB_MCU == OPT_MCU_STM32F7 + #include "stm32f7xx.h" + #define EP_MAX_FS 6 + #define EP_FIFO_SIZE_FS 1280 + + #define EP_MAX_HS 9 + #define EP_FIFO_SIZE_HS 4096 + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L4 + #include "stm32l4xx.h" + #define EP_MAX_FS 6 + #define EP_FIFO_SIZE_FS 1280 + +#elif CFG_TUSB_MCU == OPT_MCU_STM32U5 + #include "stm32u5xx.h" + // U59x/5Ax/5Fx/5Gx are highspeed with built-in HS PHY + #ifdef USB_OTG_FS + #define USB_OTG_FS_PERIPH_BASE USB_OTG_FS_BASE + #define EP_MAX_FS 6 + #define EP_FIFO_SIZE_FS 1280 + #else + #define USB_OTG_HS_PERIPH_BASE USB_OTG_HS_BASE + #define EP_MAX_HS 9 + #define EP_FIFO_SIZE_HS 4096 + #endif +#else + #error "Unsupported MCUs" +#endif + +// OTG HS always has higher number of endpoints than FS +#ifdef USB_OTG_HS_PERIPH_BASE + #define DWC2_EP_MAX EP_MAX_HS +#else + #define DWC2_EP_MAX EP_MAX_FS +#endif + +// On STM32 for consistency we associate +// - Port0 to OTG_FS, and Port1 to OTG_HS +static const dwc2_controller_t _dwc2_controller[] = { + #ifdef USB_OTG_FS_PERIPH_BASE + { .reg_base = USB_OTG_FS_PERIPH_BASE, .irqnum = OTG_FS_IRQn, .ep_count = EP_MAX_FS, .ep_fifo_size = EP_FIFO_SIZE_FS }, + #endif + + #ifdef USB_OTG_HS_PERIPH_BASE + { .reg_base = USB_OTG_HS_PERIPH_BASE, .irqnum = OTG_HS_IRQn, .ep_count = EP_MAX_HS, .ep_fifo_size = EP_FIFO_SIZE_HS }, + #endif +}; + +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ + +// SystemCoreClock is already included by family header +// extern uint32_t SystemCoreClock; + +TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_enable(uint8_t rhport) { + NVIC_EnableIRQ((IRQn_Type) _dwc2_controller[rhport].irqnum); +} + +TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_disable(uint8_t rhport) { + NVIC_DisableIRQ((IRQn_Type) _dwc2_controller[rhport].irqnum); +} + +TU_ATTR_ALWAYS_INLINE static inline void dwc2_remote_wakeup_delay(void) { + // try to delay for 1 ms + uint32_t count = SystemCoreClock / 1000; + while (count--) __NOP(); +} + +// MCU specific PHY init, called BEFORE core reset +// - dwc2 3.30a (H5) use USB_HS_PHYC +// - dwc2 4.11a (U5) use femtoPHY +static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) { + if (hs_phy_type == HS_PHY_TYPE_NONE) { + // Enable on-chip FS PHY + dwc2->stm32_gccfg |= STM32_GCCFG_PWRDWN; + + // https://community.st.com/t5/stm32cubemx-mcus/why-stm32h743-usb-fs-doesn-t-work-if-freertos-tickless-idle/m-p/349480#M18867 + // H7 running on full-speed phy need to disable ULPI clock in sleep mode. + // Otherwise, USB won't work when mcu executing WFI/WFE instruction i.e tick-less RTOS. + // Note: there may be other family that is affected by this, but only H7 and F7 is tested so far + #if defined(USB_OTG_FS_PERIPH_BASE) && defined(RCC_AHB1LPENR_USB2OTGFSULPILPEN) + if ( USB_OTG_FS_PERIPH_BASE == (uint32_t) dwc2 ) { + RCC->AHB1LPENR &= ~RCC_AHB1LPENR_USB2OTGFSULPILPEN; + } + #endif + + #if defined(USB_OTG_HS_PERIPH_BASE) && defined(RCC_AHB1LPENR_USB1OTGHSULPILPEN) + if ( USB_OTG_HS_PERIPH_BASE == (uint32_t) dwc2 ) { + RCC->AHB1LPENR &= ~RCC_AHB1LPENR_USB1OTGHSULPILPEN; + } + #endif + + #if defined(USB_OTG_HS_PERIPH_BASE) && defined(RCC_AHB1LPENR_OTGHSULPILPEN) + if ( USB_OTG_HS_PERIPH_BASE == (uint32_t) dwc2 ) { + RCC->AHB1LPENR &= ~RCC_AHB1LPENR_OTGHSULPILPEN; + } + #endif + + } else { +#if CFG_TUSB_MCU != OPT_MCU_STM32U5 + // Disable FS PHY, TODO on U5A5 (dwc2 4.11a) 16th bit is 'Host CDP behavior enable' + dwc2->stm32_gccfg &= ~STM32_GCCFG_PWRDWN; +#endif + + // Enable on-chip HS PHY + if (hs_phy_type == HS_PHY_TYPE_UTMI || hs_phy_type == HS_PHY_TYPE_UTMI_ULPI) { + #ifdef USB_HS_PHYC + // Enable UTMI HS PHY + dwc2->stm32_gccfg |= STM32_GCCFG_PHYHSEN; + + // Enable LDO + USB_HS_PHYC->USB_HS_PHYC_LDO |= USB_HS_PHYC_LDO_ENABLE; + + // Wait until LDO ready + while ( 0 == (USB_HS_PHYC->USB_HS_PHYC_LDO & USB_HS_PHYC_LDO_STATUS) ) {} + + uint32_t phyc_pll = 0; + + // TODO Try to get HSE_VALUE from registers instead of depending CFLAGS + switch ( HSE_VALUE ) + { + case 12000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_12MHZ ; break; + case 12500000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_12_5MHZ ; break; + case 16000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_16MHZ ; break; + case 24000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_24MHZ ; break; + case 25000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_25MHZ ; break; + case 32000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_Msk ; break; // Value not defined in header + default: + TU_ASSERT(false, ); + } + USB_HS_PHYC->USB_HS_PHYC_PLL = phyc_pll; + + // Control the tuning interface of the High Speed PHY + // Use magic value (USB_HS_PHYC_TUNE_VALUE) from ST driver for F7 + USB_HS_PHYC->USB_HS_PHYC_TUNE |= 0x00000F13U; + + // Enable PLL internal PHY + USB_HS_PHYC->USB_HS_PHYC_PLL |= USB_HS_PHYC_PLL_PLLEN; + #else + + #endif + } + } +} + +// MCU specific PHY update, it is called AFTER init() and core reset +static inline void dwc2_phy_update(dwc2_regs_t* dwc2, uint8_t hs_phy_type) { + // used to set turnaround time for fullspeed, nothing to do in highspeed mode + if (hs_phy_type == HS_PHY_TYPE_NONE) { + // Turnaround timeout depends on the AHB clock dictated by STM32 Reference Manual + uint32_t turnaround; + + if (SystemCoreClock >= 32000000u) { + turnaround = 0x6u; + } else if (SystemCoreClock >= 27500000u) { + turnaround = 0x7u; + } else if (SystemCoreClock >= 24000000u) { + turnaround = 0x8u; + } else if (SystemCoreClock >= 21800000u) { + turnaround = 0x9u; + } + else if (SystemCoreClock >= 20000000u) { + turnaround = 0xAu; + } + else if (SystemCoreClock >= 18500000u) { + turnaround = 0xBu; + } + else if (SystemCoreClock >= 17200000u) { + turnaround = 0xCu; + } + else if (SystemCoreClock >= 16000000u) { + turnaround = 0xDu; + } + else if (SystemCoreClock >= 15000000u) { + turnaround = 0xEu; + } + else { + turnaround = 0xFu; + } + + dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_TRDT_Msk) | (turnaround << GUSBCFG_TRDT_Pos); + } +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_type.h b/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_type.h new file mode 100644 index 00000000..c1577123 --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_type.h @@ -0,0 +1,1770 @@ +/** + * @author MCD Application Team + * Ha Thach (tinyusb.org) + * + * @attention + * + *

© Copyright (c) 2019 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + */ + +#ifndef _TUSB_DWC2_TYPES_H_ +#define _TUSB_DWC2_TYPES_H_ + +#include "stdint.h" + +#ifdef __cplusplus + extern "C" { +#endif + +// Controller +typedef struct +{ + uintptr_t reg_base; + uint32_t irqnum; + uint8_t ep_count; + uint32_t ep_fifo_size; +}dwc2_controller_t; + +// DWC OTG HW Release versions +#define DWC2_CORE_REV_2_71a 0x4f54271a +#define DWC2_CORE_REV_2_72a 0x4f54272a +#define DWC2_CORE_REV_2_80a 0x4f54280a +#define DWC2_CORE_REV_2_90a 0x4f54290a +#define DWC2_CORE_REV_2_91a 0x4f54291a +#define DWC2_CORE_REV_2_92a 0x4f54292a +#define DWC2_CORE_REV_2_94a 0x4f54294a +#define DWC2_CORE_REV_3_00a 0x4f54300a +#define DWC2_CORE_REV_3_10a 0x4f54310a +#define DWC2_CORE_REV_4_00a 0x4f54400a +#define DWC2_CORE_REV_4_11a 0x4f54411a +#define DWC2_CORE_REV_4_20a 0x4f54420a +#define DWC2_FS_IOT_REV_1_00a 0x5531100a +#define DWC2_HS_IOT_REV_1_00a 0x5532100a +#define DWC2_CORE_REV_MASK 0x0000ffff + +// DWC OTG HW Core ID +#define DWC2_OTG_ID 0x4f540000 +#define DWC2_FS_IOT_ID 0x55310000 +#define DWC2_HS_IOT_ID 0x55320000 + +#if 0 +// HS PHY +typedef struct +{ + volatile uint32_t HS_PHYC_PLL; // 000h This register is used to control the PLL of the HS PHY. + volatile uint32_t Reserved04; // 004h Reserved + volatile uint32_t Reserved08; // 008h Reserved + volatile uint32_t HS_PHYC_TUNE; // 00Ch This register is used to control the tuning interface of the High Speed PHY. + volatile uint32_t Reserved10; // 010h Reserved + volatile uint32_t Reserved14; // 014h Reserved + volatile uint32_t HS_PHYC_LDO; // 018h This register is used to control the regulator (LDO). +} HS_PHYC_GlobalTypeDef; +#endif + +enum { + HS_PHY_TYPE_NONE = 0 , // not supported + HS_PHY_TYPE_UTMI , // internal PHY (mostly) + HS_PHY_TYPE_ULPI , // external PHY + HS_PHY_TYPE_UTMI_ULPI , +}; + +enum { + FS_PHY_TYPE_NONE = 0, // not supported + FS_PHY_TYPE_DEDICATED, + FS_PHY_TYPE_UTMI, + FS_PHY_TYPE_ULPI, +}; + +typedef struct TU_ATTR_PACKED +{ + uint32_t op_mode : 3; // 0: HNP and SRP | 1: SRP | 2: non-HNP, non-SRP + uint32_t arch : 2; // 0: slave-only | 1: External DMA | 2: Internal DMA | 3: others + uint32_t point2point : 1; // 0: support hub and split | 1: no hub, no split + uint32_t hs_phy_type : 2; // 0: not supported | 1: UTMI+ | 2: ULPI | 3: UTMI+ and ULPI + uint32_t fs_phy_type : 2; // 0: not supported | 1: dedicated | 2: UTMI+ | 3: ULPI + uint32_t num_dev_ep : 4; // Number of device endpoints (not including EP0) + uint32_t num_host_ch : 4; // Number of host channel + uint32_t period_channel_support : 1; // Support Periodic OUT Host Channel + uint32_t enable_dynamic_fifo : 1; // Dynamic FIFO Sizing Enabled + uint32_t mul_cpu_int : 1; // Multi-Processor Interrupt Enabled + uint32_t reserved21 : 1; + uint32_t nperiod_tx_q_depth : 2; // Non-periodic request queue depth: 0 = 2. 1 = 4, 2 = 8 + uint32_t host_period_tx_q_depth : 2; // Host periodic request queue depth: 0 = 2. 1 = 4, 2 = 8 + uint32_t dev_token_q_depth : 5; // Device IN token sequence learning queue depth: 0-30 + uint32_t otg_enable_ic_usb : 1; // IC_USB mode specified for mode of operation +} dwc2_ghwcfg2_t; + +TU_VERIFY_STATIC(sizeof(dwc2_ghwcfg2_t) == 4, "incorrect size"); + +typedef struct TU_ATTR_PACKED +{ + uint32_t xfer_size_width : 4; // Transfer size counter in bits = 11 + n (max 19 bits) + uint32_t packet_size_width : 3; // Packet size counter in bits = 4 + n (max 10 bits) + uint32_t otg_enable : 1; // 1 is OTG capable + uint32_t i2c_enable : 1; // I2C interface is available + uint32_t vendor_ctrl_itf : 1; // Vendor control interface is available + uint32_t optional_feature_removed : 1; // remove User ID, GPIO, SOF toggle & counter + uint32_t synch_reset : 1; // 0: async reset | 1: synch reset + uint32_t otg_adp_support : 1; // ADP logic is present along with HSOTG controller + uint32_t otg_enable_hsic : 1; // 1: HSIC-capable with shared UTMI PHY interface | 0: non-HSIC + uint32_t battery_charger_support : 1; // support battery charger + uint32_t lpm_mode : 1; // LPC mode + uint32_t total_fifo_size : 16; // DFIFO depth value in terms of 32-bit words +}dwc2_ghwcfg3_t; + +TU_VERIFY_STATIC(sizeof(dwc2_ghwcfg3_t) == 4, "incorrect size"); + +typedef struct TU_ATTR_PACKED +{ + uint32_t num_dev_period_in_ep : 4; // Number of Device Periodic IN Endpoints + uint32_t power_optimized : 1; // Partial Power Down Enabled + uint32_t ahb_freq_min : 1; // 1: minimum of AHB frequency is less than 60 MHz + uint32_t hibernation : 1; // Hibernation feature is enabled + uint32_t reserved7 : 3; + uint32_t service_interval_mode : 1; // Service Interval supported + uint32_t ipg_isoc_en : 1; // IPG ISOC supported + uint32_t acg_enable : 1; // ACG enabled + uint32_t reserved13 : 1; + uint32_t utmi_phy_data_width : 2; // 0: 8 bits | 1: 16 bits | 2: 8/16 software selectable + uint32_t dev_ctrl_ep_num : 4; // Number of Device control endpoints in addition to EP0 + uint32_t iddg_filter_enabled : 1; + uint32_t vbus_valid_filter_enabled : 1; + uint32_t a_valid_filter_enabled : 1; + uint32_t b_valid_filter_enabled : 1; + uint32_t dedicated_fifos : 1; // Dedicated tx fifo for device IN Endpoint is enabled + uint32_t num_dev_in_eps : 4; // Number of Device IN Endpoints including EP0 + uint32_t dma_desc_enable : 1; // scatter/gather DMA configuration + uint32_t dma_dynamic : 1; // Dynamic scatter/gather DMA +}dwc2_ghwcfg4_t; + +TU_VERIFY_STATIC(sizeof(dwc2_ghwcfg4_t) == 4, "incorrect size"); + +// Host Channel +typedef struct +{ + volatile uint32_t hcchar; // 500 + 20*ch Host Channel Characteristics + volatile uint32_t hcsplt; // 504 + 20*ch Host Channel Split Control + volatile uint32_t hcint; // 508 + 20*ch Host Channel Interrupt + volatile uint32_t hcintmsk; // 50C + 20*ch Host Channel Interrupt Mask + volatile uint32_t hctsiz; // 510 + 20*ch Host Channel Transfer Size + volatile uint32_t hcdma; // 514 + 20*ch Host Channel DMA Address + uint32_t reserved518; // 518 + 20*ch + volatile uint32_t hcdmab; // 51C + 20*ch Host Channel DMA Address +} dwc2_channel_t; + +// Endpoint IN +typedef struct +{ + volatile uint32_t diepctl; // 900 + 20*ep Device IN Endpoint Control + uint32_t reserved04; // 904 + volatile uint32_t diepint; // 908 + 20*ep Device IN Endpoint Interrupt + uint32_t reserved0c; // 90C + volatile uint32_t dieptsiz; // 910 + 20*ep Device IN Endpoint Transfer Size + volatile uint32_t diepdma; // 914 + 20*ep Device IN Endpoint DMA Address + volatile uint32_t dtxfsts; // 918 + 20*ep Device IN Endpoint Tx FIFO Status + uint32_t reserved1c; // 91C +} dwc2_epin_t; + +// Endpoint OUT +typedef struct +{ + volatile uint32_t doepctl; // B00 + 20*ep Device OUT Endpoint Control + uint32_t reserved04; // B04 + volatile uint32_t doepint; // B08 + 20*ep Device OUT Endpoint Interrupt + uint32_t reserved0c; // B0C + volatile uint32_t doeptsiz; // B10 + 20*ep Device OUT Endpoint Transfer Size + volatile uint32_t doepdma; // B14 + 20*ep Device OUT Endpoint DMA Address + uint32_t reserved18[2]; // B18..B1C +} dwc2_epout_t; + +typedef struct +{ + //------------- Core Global -------------// + volatile uint32_t gotgctl; // 000 OTG Control and Status + volatile uint32_t gotgint; // 004 OTG Interrupt + volatile uint32_t gahbcfg; // 008 AHB Configuration + volatile uint32_t gusbcfg; // 00c USB Configuration + volatile uint32_t grstctl; // 010 Reset + volatile uint32_t gintsts; // 014 Interrupt + volatile uint32_t gintmsk; // 018 Interrupt Mask + volatile uint32_t grxstsr; // 01c Receive Status Debug Read + volatile uint32_t grxstsp; // 020 Receive Status Read/Pop + volatile uint32_t grxfsiz; // 024 Receive FIFO Size +union { + volatile uint32_t dieptxf0; // 028 EP0 Tx FIFO Size + volatile uint32_t gnptxfsiz; // 028 Non-periodic Transmit FIFO Size +}; + volatile uint32_t gnptxsts; // 02c Non-periodic Transmit FIFO/Queue Status + volatile uint32_t gi2cctl; // 030 I2C Address + volatile uint32_t gpvndctl; // 034 PHY Vendor Control +union { + volatile uint32_t ggpio; // 038 General Purpose IO + volatile uint32_t stm32_gccfg; // 038 STM32 General Core Configuration +}; + volatile uint32_t guid; // 03C User (Application programmable) ID + volatile uint32_t gsnpsid; // 040 Synopsys ID + Release version + volatile uint32_t ghwcfg1; // 044 User Hardware Configuration1: endpoint dir (2 bit per ep) +union { + volatile uint32_t ghwcfg2; // 048 User Hardware Configuration2 + dwc2_ghwcfg2_t ghwcfg2_bm; +}; +union { + volatile uint32_t ghwcfg3; // 04C User Hardware Configuration3 + dwc2_ghwcfg3_t ghwcfg3_bm; +}; +union { + volatile uint32_t ghwcfg4; // 050 User Hardware Configuration4 + dwc2_ghwcfg4_t ghwcfg4_bm; +}; + volatile uint32_t glpmcfg; // 054 Core LPM Configuration + volatile uint32_t gpwrdn; // 058 Power Down + volatile uint32_t gdfifocfg; // 05C DFIFO Software Configuration + volatile uint32_t gadpctl; // 060 ADP Timer, Control and Status + uint32_t reserved64[39]; // 064..0FF + volatile uint32_t hptxfsiz; // 100 Host Periodic Tx FIFO Size + volatile uint32_t dieptxf[15]; // 104..13C Device Periodic Transmit FIFO Size + uint32_t reserved140[176]; // 140..3FF + + //------------- Host -------------// + volatile uint32_t hcfg; // 400 Host Configuration + volatile uint32_t hfir; // 404 Host Frame Interval + volatile uint32_t hfnum; // 408 Host Frame Number / Frame Remaining + uint32_t reserved40c; // 40C + volatile uint32_t hptxsts; // 410 Host Periodic TX FIFO / Queue Status + volatile uint32_t haint; // 414 Host All Channels Interrupt + volatile uint32_t haintmsk; // 418 Host All Channels Interrupt Mask + volatile uint32_t hflbaddr; // 41C Host Frame List Base Address + uint32_t reserved420[8]; // 420..43F + volatile uint32_t hprt; // 440 Host Port Control and Status + uint32_t reserved444[47]; // 444..4FF + + //------------- Host Channel -------------// + dwc2_channel_t channel[16]; // 500..6FF Host Channels 0-15 + uint32_t reserved700[64]; // 700..7FF + + //------------- Device -------------// + volatile uint32_t dcfg; // 800 Device Configuration + volatile uint32_t dctl; // 804 Device Control + volatile uint32_t dsts; // 808 Device Status (RO) + uint32_t reserved80c; // 80C + volatile uint32_t diepmsk; // 810 Device IN Endpoint Interrupt Mask + volatile uint32_t doepmsk; // 814 Device OUT Endpoint Interrupt Mask + volatile uint32_t daint; // 818 Device All Endpoints Interrupt + volatile uint32_t daintmsk; // 81C Device All Endpoints Interrupt Mask + volatile uint32_t dtknqr1; // 820 Device IN token sequence learning queue read1 + volatile uint32_t dtknqr2; // 824 Device IN token sequence learning queue read2 + volatile uint32_t dvbusdis; // 828 Device VBUS Discharge Time + volatile uint32_t dvbuspulse; // 82C Device VBUS Pulsing Time + volatile uint32_t dthrctl; // 830 Device threshold Control + volatile uint32_t diepempmsk; // 834 Device IN Endpoint FIFO Empty Interrupt Mask + volatile uint32_t deachint; // 838 Device Each Endpoint Interrupt + volatile uint32_t deachmsk; // 83C Device Each Endpoint Interrupt msk + volatile uint32_t diepeachmsk[16]; // 840..87C Device Each IN Endpoint mask + volatile uint32_t doepeachmsk[16]; // 880..8BF Device Each OUT Endpoint mask + uint32_t reserved8c0[16]; // 8C0..8FF + + //------------- Device Endpoint -------------// + dwc2_epin_t epin[16]; // 900..AFF IN Endpoints + dwc2_epout_t epout[16]; // B00..CFF OUT Endpoints + uint32_t reservedd00[64]; // D00..DFF + + //------------- Power Clock -------------// + volatile uint32_t pcgctl; // E00 Power and Clock Gating Control + volatile uint32_t pcgctl1; // E04 + uint32_t reservede08[126]; // E08..FFF + + //------------- FIFOs -------------// + // Word-accessed only using first pointer since it auto shift + volatile uint32_t fifo[16][0x400]; // 1000..FFFF Endpoint FIFO +} dwc2_regs_t; + +TU_VERIFY_STATIC(offsetof(dwc2_regs_t, hcfg ) == 0x0400, "incorrect size"); +TU_VERIFY_STATIC(offsetof(dwc2_regs_t, channel) == 0x0500, "incorrect size"); +TU_VERIFY_STATIC(offsetof(dwc2_regs_t, dcfg ) == 0x0800, "incorrect size"); +TU_VERIFY_STATIC(offsetof(dwc2_regs_t, epin ) == 0x0900, "incorrect size"); +TU_VERIFY_STATIC(offsetof(dwc2_regs_t, epout ) == 0x0B00, "incorrect size"); +TU_VERIFY_STATIC(offsetof(dwc2_regs_t, pcgctl ) == 0x0E00, "incorrect size"); +TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); + +//--------------------------------------------------------------------+ +// Register Bit Definitions +//--------------------------------------------------------------------+ + +/******************** Bit definition for GOTGCTL register ********************/ +#define GOTGCTL_SRQSCS_Pos (0U) +#define GOTGCTL_SRQSCS_Msk (0x1UL << GOTGCTL_SRQSCS_Pos) // 0x00000001 +#define GOTGCTL_SRQSCS GOTGCTL_SRQSCS_Msk // Session request success +#define GOTGCTL_SRQ_Pos (1U) +#define GOTGCTL_SRQ_Msk (0x1UL << GOTGCTL_SRQ_Pos) // 0x00000002 +#define GOTGCTL_SRQ GOTGCTL_SRQ_Msk // Session request +#define GOTGCTL_VBVALOEN_Pos (2U) +#define GOTGCTL_VBVALOEN_Msk (0x1UL << GOTGCTL_VBVALOEN_Pos) // 0x00000004 +#define GOTGCTL_VBVALOEN GOTGCTL_VBVALOEN_Msk // VBUS valid override enable +#define GOTGCTL_VBVALOVAL_Pos (3U) +#define GOTGCTL_VBVALOVAL_Msk (0x1UL << GOTGCTL_VBVALOVAL_Pos) // 0x00000008 +#define GOTGCTL_VBVALOVAL GOTGCTL_VBVALOVAL_Msk // VBUS valid override value +#define GOTGCTL_AVALOEN_Pos (4U) +#define GOTGCTL_AVALOEN_Msk (0x1UL << GOTGCTL_AVALOEN_Pos) // 0x00000010 +#define GOTGCTL_AVALOEN GOTGCTL_AVALOEN_Msk // A-peripheral session valid override enable +#define GOTGCTL_AVALOVAL_Pos (5U) +#define GOTGCTL_AVALOVAL_Msk (0x1UL << GOTGCTL_AVALOVAL_Pos) // 0x00000020 +#define GOTGCTL_AVALOVAL GOTGCTL_AVALOVAL_Msk // A-peripheral session valid override value +#define GOTGCTL_BVALOEN_Pos (6U) +#define GOTGCTL_BVALOEN_Msk (0x1UL << GOTGCTL_BVALOEN_Pos) // 0x00000040 +#define GOTGCTL_BVALOEN GOTGCTL_BVALOEN_Msk // B-peripheral session valid override enable +#define GOTGCTL_BVALOVAL_Pos (7U) +#define GOTGCTL_BVALOVAL_Msk (0x1UL << GOTGCTL_BVALOVAL_Pos) // 0x00000080 +#define GOTGCTL_BVALOVAL GOTGCTL_BVALOVAL_Msk // B-peripheral session valid override value +#define GOTGCTL_HNGSCS_Pos (8U) +#define GOTGCTL_HNGSCS_Msk (0x1UL << GOTGCTL_HNGSCS_Pos) // 0x00000100 +#define GOTGCTL_HNGSCS GOTGCTL_HNGSCS_Msk // Host set HNP enable +#define GOTGCTL_HNPRQ_Pos (9U) +#define GOTGCTL_HNPRQ_Msk (0x1UL << GOTGCTL_HNPRQ_Pos) // 0x00000200 +#define GOTGCTL_HNPRQ GOTGCTL_HNPRQ_Msk // HNP request +#define GOTGCTL_HSHNPEN_Pos (10U) +#define GOTGCTL_HSHNPEN_Msk (0x1UL << GOTGCTL_HSHNPEN_Pos) // 0x00000400 +#define GOTGCTL_HSHNPEN GOTGCTL_HSHNPEN_Msk // Host set HNP enable +#define GOTGCTL_DHNPEN_Pos (11U) +#define GOTGCTL_DHNPEN_Msk (0x1UL << GOTGCTL_DHNPEN_Pos) // 0x00000800 +#define GOTGCTL_DHNPEN GOTGCTL_DHNPEN_Msk // Device HNP enabled +#define GOTGCTL_EHEN_Pos (12U) +#define GOTGCTL_EHEN_Msk (0x1UL << GOTGCTL_EHEN_Pos) // 0x00001000 +#define GOTGCTL_EHEN GOTGCTL_EHEN_Msk // Embedded host enable +#define GOTGCTL_CIDSTS_Pos (16U) +#define GOTGCTL_CIDSTS_Msk (0x1UL << GOTGCTL_CIDSTS_Pos) // 0x00010000 +#define GOTGCTL_CIDSTS GOTGCTL_CIDSTS_Msk // Connector ID status +#define GOTGCTL_DBCT_Pos (17U) +#define GOTGCTL_DBCT_Msk (0x1UL << GOTGCTL_DBCT_Pos) // 0x00020000 +#define GOTGCTL_DBCT GOTGCTL_DBCT_Msk // Long/short debounce time +#define GOTGCTL_ASVLD_Pos (18U) +#define GOTGCTL_ASVLD_Msk (0x1UL << GOTGCTL_ASVLD_Pos) // 0x00040000 +#define GOTGCTL_ASVLD GOTGCTL_ASVLD_Msk // A-session valid +#define GOTGCTL_BSESVLD_Pos (19U) +#define GOTGCTL_BSESVLD_Msk (0x1UL << GOTGCTL_BSESVLD_Pos) // 0x00080000 +#define GOTGCTL_BSESVLD GOTGCTL_BSESVLD_Msk // B-session valid +#define GOTGCTL_OTGVER_Pos (20U) +#define GOTGCTL_OTGVER_Msk (0x1UL << GOTGCTL_OTGVER_Pos) // 0x00100000 +#define GOTGCTL_OTGVER GOTGCTL_OTGVER_Msk // OTG version + +/******************** Bit definition for HCFG register ********************/ +#define HCFG_FSLSPCS_Pos (0U) +#define HCFG_FSLSPCS_Msk (0x3UL << HCFG_FSLSPCS_Pos) // 0x00000003 +#define HCFG_FSLSPCS HCFG_FSLSPCS_Msk // FS/LS PHY clock select +#define HCFG_FSLSPCS_0 (0x1UL << HCFG_FSLSPCS_Pos) // 0x00000001 +#define HCFG_FSLSPCS_1 (0x2UL << HCFG_FSLSPCS_Pos) // 0x00000002 +#define HCFG_FSLSS_Pos (2U) +#define HCFG_FSLSS_Msk (0x1UL << HCFG_FSLSS_Pos) // 0x00000004 +#define HCFG_FSLSS HCFG_FSLSS_Msk // FS- and LS-only support + +/******************** Bit definition for PCGCR register ********************/ +#define PCGCR_STPPCLK_Pos (0U) +#define PCGCR_STPPCLK_Msk (0x1UL << PCGCR_STPPCLK_Pos) // 0x00000001 +#define PCGCR_STPPCLK PCGCR_STPPCLK_Msk // Stop PHY clock +#define PCGCR_GATEHCLK_Pos (1U) +#define PCGCR_GATEHCLK_Msk (0x1UL << PCGCR_GATEHCLK_Pos) // 0x00000002 +#define PCGCR_GATEHCLK PCGCR_GATEHCLK_Msk // Gate HCLK +#define PCGCR_PHYSUSP_Pos (4U) +#define PCGCR_PHYSUSP_Msk (0x1UL << PCGCR_PHYSUSP_Pos) // 0x00000010 +#define PCGCR_PHYSUSP PCGCR_PHYSUSP_Msk // PHY suspended + +/******************** Bit definition for GOTGINT register ********************/ +#define GOTGINT_SEDET_Pos (2U) +#define GOTGINT_SEDET_Msk (0x1UL << GOTGINT_SEDET_Pos) // 0x00000004 +#define GOTGINT_SEDET GOTGINT_SEDET_Msk // Session end detected +#define GOTGINT_SRSSCHG_Pos (8U) +#define GOTGINT_SRSSCHG_Msk (0x1UL << GOTGINT_SRSSCHG_Pos) // 0x00000100 +#define GOTGINT_SRSSCHG GOTGINT_SRSSCHG_Msk // Session request success status change +#define GOTGINT_HNSSCHG_Pos (9U) +#define GOTGINT_HNSSCHG_Msk (0x1UL << GOTGINT_HNSSCHG_Pos) // 0x00000200 +#define GOTGINT_HNSSCHG GOTGINT_HNSSCHG_Msk // Host negotiation success status change +#define GOTGINT_HNGDET_Pos (17U) +#define GOTGINT_HNGDET_Msk (0x1UL << GOTGINT_HNGDET_Pos) // 0x00020000 +#define GOTGINT_HNGDET GOTGINT_HNGDET_Msk // Host negotiation detected +#define GOTGINT_ADTOCHG_Pos (18U) +#define GOTGINT_ADTOCHG_Msk (0x1UL << GOTGINT_ADTOCHG_Pos) // 0x00040000 +#define GOTGINT_ADTOCHG GOTGINT_ADTOCHG_Msk // A-device timeout change +#define GOTGINT_DBCDNE_Pos (19U) +#define GOTGINT_DBCDNE_Msk (0x1UL << GOTGINT_DBCDNE_Pos) // 0x00080000 +#define GOTGINT_DBCDNE GOTGINT_DBCDNE_Msk // Debounce done +#define GOTGINT_IDCHNG_Pos (20U) +#define GOTGINT_IDCHNG_Msk (0x1UL << GOTGINT_IDCHNG_Pos) // 0x00100000 +#define GOTGINT_IDCHNG GOTGINT_IDCHNG_Msk // Change in ID pin input value + +/******************** Bit definition for DCFG register ********************/ +#define DCFG_DSPD_Pos (0U) +#define DCFG_DSPD_Msk (0x3UL << DCFG_DSPD_Pos) // 0x00000003 +#define DCFG_DSPD_HS 0 // Highspeed +#define DCFG_DSPD_FS_HSPHY 1 // Fullspeed on HS PHY +#define DCFG_DSPD_LS 2 // Lowspeed +#define DCFG_DSPD_FS 3 // Fullspeed on FS PHY + +#define DCFG_NZLSOHSK_Pos (2U) +#define DCFG_NZLSOHSK_Msk (0x1UL << DCFG_NZLSOHSK_Pos) // 0x00000004 +#define DCFG_NZLSOHSK DCFG_NZLSOHSK_Msk // Nonzero-length status OUT handshake + +#define DCFG_DAD_Pos (4U) +#define DCFG_DAD_Msk (0x7FUL << DCFG_DAD_Pos) // 0x000007F0 +#define DCFG_DAD DCFG_DAD_Msk // Device address +#define DCFG_DAD_0 (0x01UL << DCFG_DAD_Pos) // 0x00000010 +#define DCFG_DAD_1 (0x02UL << DCFG_DAD_Pos) // 0x00000020 +#define DCFG_DAD_2 (0x04UL << DCFG_DAD_Pos) // 0x00000040 +#define DCFG_DAD_3 (0x08UL << DCFG_DAD_Pos) // 0x00000080 +#define DCFG_DAD_4 (0x10UL << DCFG_DAD_Pos) // 0x00000100 +#define DCFG_DAD_5 (0x20UL << DCFG_DAD_Pos) // 0x00000200 +#define DCFG_DAD_6 (0x40UL << DCFG_DAD_Pos) // 0x00000400 + +#define DCFG_PFIVL_Pos (11U) +#define DCFG_PFIVL_Msk (0x3UL << DCFG_PFIVL_Pos) // 0x00001800 +#define DCFG_PFIVL DCFG_PFIVL_Msk // Periodic (micro)frame interval +#define DCFG_PFIVL_0 (0x1UL << DCFG_PFIVL_Pos) // 0x00000800 +#define DCFG_PFIVL_1 (0x2UL << DCFG_PFIVL_Pos) // 0x00001000 + +#define DCFG_XCVRDLY_Pos (14U) +#define DCFG_XCVRDLY_Msk (0x1UL << DCFG_XCVRDLY_Pos) // 0x00004000 +#define DCFG_XCVRDLY DCFG_XCVRDLY_Msk // Enables delay between xcvr_sel and txvalid during device chirp + +#define DCFG_PERSCHIVL_Pos (24U) +#define DCFG_PERSCHIVL_Msk (0x3UL << DCFG_PERSCHIVL_Pos) // 0x03000000 +#define DCFG_PERSCHIVL DCFG_PERSCHIVL_Msk // Periodic scheduling interval +#define DCFG_PERSCHIVL_0 (0x1UL << DCFG_PERSCHIVL_Pos) // 0x01000000 +#define DCFG_PERSCHIVL_1 (0x2UL << DCFG_PERSCHIVL_Pos) // 0x02000000 + +/******************** Bit definition for DCTL register ********************/ +#define DCTL_RWUSIG_Pos (0U) +#define DCTL_RWUSIG_Msk (0x1UL << DCTL_RWUSIG_Pos) // 0x00000001 +#define DCTL_RWUSIG DCTL_RWUSIG_Msk // Remote wakeup signaling +#define DCTL_SDIS_Pos (1U) +#define DCTL_SDIS_Msk (0x1UL << DCTL_SDIS_Pos) // 0x00000002 +#define DCTL_SDIS DCTL_SDIS_Msk // Soft disconnect +#define DCTL_GINSTS_Pos (2U) +#define DCTL_GINSTS_Msk (0x1UL << DCTL_GINSTS_Pos) // 0x00000004 +#define DCTL_GINSTS DCTL_GINSTS_Msk // Global IN NAK status +#define DCTL_GONSTS_Pos (3U) +#define DCTL_GONSTS_Msk (0x1UL << DCTL_GONSTS_Pos) // 0x00000008 +#define DCTL_GONSTS DCTL_GONSTS_Msk // Global OUT NAK status + +#define DCTL_TCTL_Pos (4U) +#define DCTL_TCTL_Msk (0x7UL << DCTL_TCTL_Pos) // 0x00000070 +#define DCTL_TCTL DCTL_TCTL_Msk // Test control +#define DCTL_TCTL_0 (0x1UL << DCTL_TCTL_Pos) // 0x00000010 +#define DCTL_TCTL_1 (0x2UL << DCTL_TCTL_Pos) // 0x00000020 +#define DCTL_TCTL_2 (0x4UL << DCTL_TCTL_Pos) // 0x00000040 +#define DCTL_SGINAK_Pos (7U) +#define DCTL_SGINAK_Msk (0x1UL << DCTL_SGINAK_Pos) // 0x00000080 +#define DCTL_SGINAK DCTL_SGINAK_Msk // Set global IN NAK +#define DCTL_CGINAK_Pos (8U) +#define DCTL_CGINAK_Msk (0x1UL << DCTL_CGINAK_Pos) // 0x00000100 +#define DCTL_CGINAK DCTL_CGINAK_Msk // Clear global IN NAK +#define DCTL_SGONAK_Pos (9U) +#define DCTL_SGONAK_Msk (0x1UL << DCTL_SGONAK_Pos) // 0x00000200 +#define DCTL_SGONAK DCTL_SGONAK_Msk // Set global OUT NAK +#define DCTL_CGONAK_Pos (10U) +#define DCTL_CGONAK_Msk (0x1UL << DCTL_CGONAK_Pos) // 0x00000400 +#define DCTL_CGONAK DCTL_CGONAK_Msk // Clear global OUT NAK +#define DCTL_POPRGDNE_Pos (11U) +#define DCTL_POPRGDNE_Msk (0x1UL << DCTL_POPRGDNE_Pos) // 0x00000800 +#define DCTL_POPRGDNE DCTL_POPRGDNE_Msk // Power-on programming done + +/******************** Bit definition for HFIR register ********************/ +#define HFIR_FRIVL_Pos (0U) +#define HFIR_FRIVL_Msk (0xFFFFUL << HFIR_FRIVL_Pos) // 0x0000FFFF +#define HFIR_FRIVL HFIR_FRIVL_Msk // Frame interval + +/******************** Bit definition for HFNUM register ********************/ +#define HFNUM_FRNUM_Pos (0U) +#define HFNUM_FRNUM_Msk (0xFFFFUL << HFNUM_FRNUM_Pos) // 0x0000FFFF +#define HFNUM_FRNUM HFNUM_FRNUM_Msk // Frame number +#define HFNUM_FTREM_Pos (16U) +#define HFNUM_FTREM_Msk (0xFFFFUL << HFNUM_FTREM_Pos) // 0xFFFF0000 +#define HFNUM_FTREM HFNUM_FTREM_Msk // Frame time remaining + +/******************** Bit definition for DSTS register ********************/ +#define DSTS_SUSPSTS_Pos (0U) +#define DSTS_SUSPSTS_Msk (0x1UL << DSTS_SUSPSTS_Pos) // 0x00000001 +#define DSTS_SUSPSTS DSTS_SUSPSTS_Msk // Suspend status +#define DSTS_ENUMSPD_Pos (1U) +#define DSTS_ENUMSPD_Msk (0x3UL << DSTS_ENUMSPD_Pos) // 0x00000006 +#define DSTS_ENUMSPD DSTS_ENUMSPD_Msk // Enumerated speed +#define DSTS_ENUMSPD_HS 0 // Highspeed +#define DSTS_ENUMSPD_FS_HSPHY 1 // Fullspeed on HS PHY +#define DSTS_ENUMSPD_LS 2 // Lowspeed +#define DSTS_ENUMSPD_FS 3 // Fullspeed on FS PHY + + +#define DSTS_EERR_Pos (3U) +#define DSTS_EERR_Msk (0x1UL << DSTS_EERR_Pos) // 0x00000008 +#define DSTS_EERR DSTS_EERR_Msk // Erratic error +#define DSTS_FNSOF_Pos (8U) +#define DSTS_FNSOF_Msk (0x3FFFUL << DSTS_FNSOF_Pos) // 0x003FFF00 +#define DSTS_FNSOF DSTS_FNSOF_Msk // Frame number of the received SOF + +/******************** Bit definition for GAHBCFG register ********************/ +#define GAHBCFG_GINT_Pos (0U) +#define GAHBCFG_GINT_Msk (0x1UL << GAHBCFG_GINT_Pos) // 0x00000001 +#define GAHBCFG_GINT GAHBCFG_GINT_Msk // Global interrupt mask +#define GAHBCFG_HBSTLEN_Pos (1U) +#define GAHBCFG_HBSTLEN_Msk (0xFUL << GAHBCFG_HBSTLEN_Pos) // 0x0000001E +#define GAHBCFG_HBSTLEN GAHBCFG_HBSTLEN_Msk // Burst length/type +#define GAHBCFG_HBSTLEN_0 (0x0UL << GAHBCFG_HBSTLEN_Pos) // Single +#define GAHBCFG_HBSTLEN_1 (0x1UL << GAHBCFG_HBSTLEN_Pos) // INCR +#define GAHBCFG_HBSTLEN_2 (0x3UL << GAHBCFG_HBSTLEN_Pos) // INCR4 +#define GAHBCFG_HBSTLEN_3 (0x5UL << GAHBCFG_HBSTLEN_Pos) // INCR8 +#define GAHBCFG_HBSTLEN_4 (0x7UL << GAHBCFG_HBSTLEN_Pos) // INCR16 +#define GAHBCFG_DMAEN_Pos (5U) +#define GAHBCFG_DMAEN_Msk (0x1UL << GAHBCFG_DMAEN_Pos) // 0x00000020 +#define GAHBCFG_DMAEN GAHBCFG_DMAEN_Msk // DMA enable +#define GAHBCFG_TXFELVL_Pos (7U) +#define GAHBCFG_TXFELVL_Msk (0x1UL << GAHBCFG_TXFELVL_Pos) // 0x00000080 +#define GAHBCFG_TXFELVL GAHBCFG_TXFELVL_Msk // TxFIFO empty level +#define GAHBCFG_PTXFELVL_Pos (8U) +#define GAHBCFG_PTXFELVL_Msk (0x1UL << GAHBCFG_PTXFELVL_Pos) // 0x00000100 +#define GAHBCFG_PTXFELVL GAHBCFG_PTXFELVL_Msk // Periodic TxFIFO empty level + +#define GSNPSID_ID_MASK TU_GENMASK(31, 16) + +/******************** Bit definition for GUSBCFG register ********************/ +#define GUSBCFG_TOCAL_Pos (0U) +#define GUSBCFG_TOCAL_Msk (0x7UL << GUSBCFG_TOCAL_Pos) // 0x00000007 +#define GUSBCFG_TOCAL GUSBCFG_TOCAL_Msk // FS timeout calibration +#define GUSBCFG_PHYIF16_Pos (3U) +#define GUSBCFG_PHYIF16_Msk (0x1UL << GUSBCFG_PHYIF16_Pos) // 0x00000008 +#define GUSBCFG_PHYIF16 GUSBCFG_PHYIF16_Msk // PHY Interface (PHYIf) +#define GUSBCFG_ULPI_UTMI_SEL_Pos (4U) +#define GUSBCFG_ULPI_UTMI_SEL_Msk (0x1UL << GUSBCFG_ULPI_UTMI_SEL_Pos) // 0x00000010 +#define GUSBCFG_ULPI_UTMI_SEL GUSBCFG_ULPI_UTMI_SEL_Msk // ULPI or UTMI+ Select (ULPI_UTMI_Sel) +#define GUSBCFG_PHYSEL_Pos (6U) +#define GUSBCFG_PHYSEL_Msk (0x1UL << GUSBCFG_PHYSEL_Pos) // 0x00000040 +#define GUSBCFG_PHYSEL GUSBCFG_PHYSEL_Msk // USB 2.0 high-speed ULPI PHY or USB 1.1 full-speed serial transceiver select +#define GUSBCFG_DDRSEL TU_BIT(7) // Single Data Rate (SDR) or Double Data Rate (DDR) or ULPI interface. +#define GUSBCFG_SRPCAP_Pos (8U) +#define GUSBCFG_SRPCAP_Msk (0x1UL << GUSBCFG_SRPCAP_Pos) // 0x00000100 +#define GUSBCFG_SRPCAP GUSBCFG_SRPCAP_Msk // SRP-capable +#define GUSBCFG_HNPCAP_Pos (9U) +#define GUSBCFG_HNPCAP_Msk (0x1UL << GUSBCFG_HNPCAP_Pos) // 0x00000200 +#define GUSBCFG_HNPCAP GUSBCFG_HNPCAP_Msk // HNP-capable +#define GUSBCFG_TRDT_Pos (10U) +#define GUSBCFG_TRDT_Msk (0xFUL << GUSBCFG_TRDT_Pos) // 0x00003C00 +#define GUSBCFG_TRDT GUSBCFG_TRDT_Msk // USB turnaround time +#define GUSBCFG_PHYLPCS_Pos (15U) +#define GUSBCFG_PHYLPCS_Msk (0x1UL << GUSBCFG_PHYLPCS_Pos) // 0x00008000 +#define GUSBCFG_PHYLPCS GUSBCFG_PHYLPCS_Msk // PHY Low-power clock select +#define GUSBCFG_ULPIFSLS_Pos (17U) +#define GUSBCFG_ULPIFSLS_Msk (0x1UL << GUSBCFG_ULPIFSLS_Pos) // 0x00020000 +#define GUSBCFG_ULPIFSLS GUSBCFG_ULPIFSLS_Msk // ULPI FS/LS select +#define GUSBCFG_ULPIAR_Pos (18U) +#define GUSBCFG_ULPIAR_Msk (0x1UL << GUSBCFG_ULPIAR_Pos) // 0x00040000 +#define GUSBCFG_ULPIAR GUSBCFG_ULPIAR_Msk // ULPI Auto-resume +#define GUSBCFG_ULPICSM_Pos (19U) +#define GUSBCFG_ULPICSM_Msk (0x1UL << GUSBCFG_ULPICSM_Pos) // 0x00080000 +#define GUSBCFG_ULPICSM GUSBCFG_ULPICSM_Msk // ULPI Clock SuspendM +#define GUSBCFG_ULPIEVBUSD_Pos (20U) +#define GUSBCFG_ULPIEVBUSD_Msk (0x1UL << GUSBCFG_ULPIEVBUSD_Pos) // 0x00100000 +#define GUSBCFG_ULPIEVBUSD GUSBCFG_ULPIEVBUSD_Msk // ULPI External VBUS Drive +#define GUSBCFG_ULPIEVBUSI_Pos (21U) +#define GUSBCFG_ULPIEVBUSI_Msk (0x1UL << GUSBCFG_ULPIEVBUSI_Pos) // 0x00200000 +#define GUSBCFG_ULPIEVBUSI GUSBCFG_ULPIEVBUSI_Msk // ULPI external VBUS indicator +#define GUSBCFG_TSDPS_Pos (22U) +#define GUSBCFG_TSDPS_Msk (0x1UL << GUSBCFG_TSDPS_Pos) // 0x00400000 +#define GUSBCFG_TSDPS GUSBCFG_TSDPS_Msk // TermSel DLine pulsing selection +#define GUSBCFG_PCCI_Pos (23U) +#define GUSBCFG_PCCI_Msk (0x1UL << GUSBCFG_PCCI_Pos) // 0x00800000 +#define GUSBCFG_PCCI GUSBCFG_PCCI_Msk // Indicator complement +#define GUSBCFG_PTCI_Pos (24U) +#define GUSBCFG_PTCI_Msk (0x1UL << GUSBCFG_PTCI_Pos) // 0x01000000 +#define GUSBCFG_PTCI GUSBCFG_PTCI_Msk // Indicator pass through +#define GUSBCFG_ULPIIPD_Pos (25U) +#define GUSBCFG_ULPIIPD_Msk (0x1UL << GUSBCFG_ULPIIPD_Pos) // 0x02000000 +#define GUSBCFG_ULPIIPD GUSBCFG_ULPIIPD_Msk // ULPI interface protect disable +#define GUSBCFG_FHMOD_Pos (29U) +#define GUSBCFG_FHMOD_Msk (0x1UL << GUSBCFG_FHMOD_Pos) // 0x20000000 +#define GUSBCFG_FHMOD GUSBCFG_FHMOD_Msk // Forced host mode +#define GUSBCFG_FDMOD_Pos (30U) +#define GUSBCFG_FDMOD_Msk (0x1UL << GUSBCFG_FDMOD_Pos) // 0x40000000 +#define GUSBCFG_FDMOD GUSBCFG_FDMOD_Msk // Forced peripheral mode +#define GUSBCFG_CTXPKT_Pos (31U) +#define GUSBCFG_CTXPKT_Msk (0x1UL << GUSBCFG_CTXPKT_Pos) // 0x80000000 +#define GUSBCFG_CTXPKT GUSBCFG_CTXPKT_Msk // Corrupt Tx packet + +/******************** Bit definition for GRSTCTL register ********************/ +#define GRSTCTL_CSRST_Pos (0U) +#define GRSTCTL_CSRST_Msk (0x1UL << GRSTCTL_CSRST_Pos) // 0x00000001 +#define GRSTCTL_CSRST GRSTCTL_CSRST_Msk // Core soft reset +#define GRSTCTL_HSRST_Pos (1U) +#define GRSTCTL_HSRST_Msk (0x1UL << GRSTCTL_HSRST_Pos) // 0x00000002 +#define GRSTCTL_HSRST GRSTCTL_HSRST_Msk // HCLK soft reset +#define GRSTCTL_FCRST_Pos (2U) +#define GRSTCTL_FCRST_Msk (0x1UL << GRSTCTL_FCRST_Pos) // 0x00000004 +#define GRSTCTL_FCRST GRSTCTL_FCRST_Msk // Host frame counter reset +#define GRSTCTL_RXFFLSH_Pos (4U) +#define GRSTCTL_RXFFLSH_Msk (0x1UL << GRSTCTL_RXFFLSH_Pos) // 0x00000010 +#define GRSTCTL_RXFFLSH GRSTCTL_RXFFLSH_Msk // RxFIFO flush +#define GRSTCTL_TXFFLSH_Pos (5U) +#define GRSTCTL_TXFFLSH_Msk (0x1UL << GRSTCTL_TXFFLSH_Pos) // 0x00000020 +#define GRSTCTL_TXFFLSH GRSTCTL_TXFFLSH_Msk // TxFIFO flush +#define GRSTCTL_TXFNUM_Pos (6U) +#define GRSTCTL_TXFNUM_Msk (0x1FUL << GRSTCTL_TXFNUM_Pos) // 0x000007C0 +#define GRSTCTL_TXFNUM GRSTCTL_TXFNUM_Msk // TxFIFO number +#define GRSTCTL_TXFNUM_0 (0x01UL << GRSTCTL_TXFNUM_Pos) // 0x00000040 +#define GRSTCTL_TXFNUM_1 (0x02UL << GRSTCTL_TXFNUM_Pos) // 0x00000080 +#define GRSTCTL_TXFNUM_2 (0x04UL << GRSTCTL_TXFNUM_Pos) // 0x00000100 +#define GRSTCTL_TXFNUM_3 (0x08UL << GRSTCTL_TXFNUM_Pos) // 0x00000200 +#define GRSTCTL_TXFNUM_4 (0x10UL << GRSTCTL_TXFNUM_Pos) // 0x00000400 +#define GRSTCTL_CSFTRST_DONE_Pos (29) +#define GRSTCTL_CSFTRST_DONE (1u << GRSTCTL_CSFTRST_DONE_Pos) // Reset Done, only available from v4.20a +#define GRSTCTL_DMAREQ_Pos (30U) +#define GRSTCTL_DMAREQ_Msk (0x1UL << GRSTCTL_DMAREQ_Pos) // 0x40000000 +#define GRSTCTL_DMAREQ GRSTCTL_DMAREQ_Msk // DMA request signal +#define GRSTCTL_AHBIDL_Pos (31U) +#define GRSTCTL_AHBIDL_Msk (0x1UL << GRSTCTL_AHBIDL_Pos) // 0x80000000 +#define GRSTCTL_AHBIDL GRSTCTL_AHBIDL_Msk // AHB master idle + +/******************** Bit definition for DIEPMSK register ********************/ +#define DIEPMSK_XFRCM_Pos (0U) +#define DIEPMSK_XFRCM_Msk (0x1UL << DIEPMSK_XFRCM_Pos) // 0x00000001 +#define DIEPMSK_XFRCM DIEPMSK_XFRCM_Msk // Transfer completed interrupt mask +#define DIEPMSK_EPDM_Pos (1U) +#define DIEPMSK_EPDM_Msk (0x1UL << DIEPMSK_EPDM_Pos) // 0x00000002 +#define DIEPMSK_EPDM DIEPMSK_EPDM_Msk // Endpoint disabled interrupt mask +#define DIEPMSK_TOM_Pos (3U) +#define DIEPMSK_TOM_Msk (0x1UL << DIEPMSK_TOM_Pos) // 0x00000008 +#define DIEPMSK_TOM DIEPMSK_TOM_Msk // Timeout condition mask (nonisochronous endpoints) +#define DIEPMSK_ITTXFEMSK_Pos (4U) +#define DIEPMSK_ITTXFEMSK_Msk (0x1UL << DIEPMSK_ITTXFEMSK_Pos) // 0x00000010 +#define DIEPMSK_ITTXFEMSK DIEPMSK_ITTXFEMSK_Msk // IN token received when TxFIFO empty mask +#define DIEPMSK_INEPNMM_Pos (5U) +#define DIEPMSK_INEPNMM_Msk (0x1UL << DIEPMSK_INEPNMM_Pos) // 0x00000020 +#define DIEPMSK_INEPNMM DIEPMSK_INEPNMM_Msk // IN token received with EP mismatch mask +#define DIEPMSK_INEPNEM_Pos (6U) +#define DIEPMSK_INEPNEM_Msk (0x1UL << DIEPMSK_INEPNEM_Pos) // 0x00000040 +#define DIEPMSK_INEPNEM DIEPMSK_INEPNEM_Msk // IN endpoint NAK effective mask +#define DIEPMSK_TXFURM_Pos (8U) +#define DIEPMSK_TXFURM_Msk (0x1UL << DIEPMSK_TXFURM_Pos) // 0x00000100 +#define DIEPMSK_TXFURM DIEPMSK_TXFURM_Msk // FIFO underrun mask +#define DIEPMSK_BIM_Pos (9U) +#define DIEPMSK_BIM_Msk (0x1UL << DIEPMSK_BIM_Pos) // 0x00000200 +#define DIEPMSK_BIM DIEPMSK_BIM_Msk // BNA interrupt mask + +/******************** Bit definition for HPTXSTS register ********************/ +#define HPTXSTS_PTXFSAVL_Pos (0U) +#define HPTXSTS_PTXFSAVL_Msk (0xFFFFUL << HPTXSTS_PTXFSAVL_Pos) // 0x0000FFFF +#define HPTXSTS_PTXFSAVL HPTXSTS_PTXFSAVL_Msk // Periodic transmit data FIFO space available +#define HPTXSTS_PTXQSAV_Pos (16U) +#define HPTXSTS_PTXQSAV_Msk (0xFFUL << HPTXSTS_PTXQSAV_Pos) // 0x00FF0000 +#define HPTXSTS_PTXQSAV HPTXSTS_PTXQSAV_Msk // Periodic transmit request queue space available +#define HPTXSTS_PTXQSAV_0 (0x01UL << HPTXSTS_PTXQSAV_Pos) // 0x00010000 +#define HPTXSTS_PTXQSAV_1 (0x02UL << HPTXSTS_PTXQSAV_Pos) // 0x00020000 +#define HPTXSTS_PTXQSAV_2 (0x04UL << HPTXSTS_PTXQSAV_Pos) // 0x00040000 +#define HPTXSTS_PTXQSAV_3 (0x08UL << HPTXSTS_PTXQSAV_Pos) // 0x00080000 +#define HPTXSTS_PTXQSAV_4 (0x10UL << HPTXSTS_PTXQSAV_Pos) // 0x00100000 +#define HPTXSTS_PTXQSAV_5 (0x20UL << HPTXSTS_PTXQSAV_Pos) // 0x00200000 +#define HPTXSTS_PTXQSAV_6 (0x40UL << HPTXSTS_PTXQSAV_Pos) // 0x00400000 +#define HPTXSTS_PTXQSAV_7 (0x80UL << HPTXSTS_PTXQSAV_Pos) // 0x00800000 + +#define HPTXSTS_PTXQTOP_Pos (24U) +#define HPTXSTS_PTXQTOP_Msk (0xFFUL << HPTXSTS_PTXQTOP_Pos) // 0xFF000000 +#define HPTXSTS_PTXQTOP HPTXSTS_PTXQTOP_Msk // Top of the periodic transmit request queue +#define HPTXSTS_PTXQTOP_0 (0x01UL << HPTXSTS_PTXQTOP_Pos) // 0x01000000 +#define HPTXSTS_PTXQTOP_1 (0x02UL << HPTXSTS_PTXQTOP_Pos) // 0x02000000 +#define HPTXSTS_PTXQTOP_2 (0x04UL << HPTXSTS_PTXQTOP_Pos) // 0x04000000 +#define HPTXSTS_PTXQTOP_3 (0x08UL << HPTXSTS_PTXQTOP_Pos) // 0x08000000 +#define HPTXSTS_PTXQTOP_4 (0x10UL << HPTXSTS_PTXQTOP_Pos) // 0x10000000 +#define HPTXSTS_PTXQTOP_5 (0x20UL << HPTXSTS_PTXQTOP_Pos) // 0x20000000 +#define HPTXSTS_PTXQTOP_6 (0x40UL << HPTXSTS_PTXQTOP_Pos) // 0x40000000 +#define HPTXSTS_PTXQTOP_7 (0x80UL << HPTXSTS_PTXQTOP_Pos) // 0x80000000 + +/******************** Bit definition for HAINT register ********************/ +#define HAINT_HAINT_Pos (0U) +#define HAINT_HAINT_Msk (0xFFFFUL << HAINT_HAINT_Pos) // 0x0000FFFF +#define HAINT_HAINT HAINT_HAINT_Msk // Channel interrupts + +/******************** Bit definition for DOEPMSK register ********************/ +#define DOEPMSK_XFRCM_Pos (0U) +#define DOEPMSK_XFRCM_Msk (0x1UL << DOEPMSK_XFRCM_Pos) // 0x00000001 +#define DOEPMSK_XFRCM DOEPMSK_XFRCM_Msk // Transfer completed interrupt mask +#define DOEPMSK_EPDM_Pos (1U) +#define DOEPMSK_EPDM_Msk (0x1UL << DOEPMSK_EPDM_Pos) // 0x00000002 +#define DOEPMSK_EPDM DOEPMSK_EPDM_Msk // Endpoint disabled interrupt mask +#define DOEPMSK_AHBERRM_Pos (2U) +#define DOEPMSK_AHBERRM_Msk (0x1UL << DOEPMSK_AHBERRM_Pos) // 0x00000004 +#define DOEPMSK_AHBERRM DOEPMSK_AHBERRM_Msk // OUT transaction AHB Error interrupt mask +#define DOEPMSK_STUPM_Pos (3U) +#define DOEPMSK_STUPM_Msk (0x1UL << DOEPMSK_STUPM_Pos) // 0x00000008 +#define DOEPMSK_STUPM DOEPMSK_STUPM_Msk // SETUP phase done mask +#define DOEPMSK_OTEPDM_Pos (4U) +#define DOEPMSK_OTEPDM_Msk (0x1UL << DOEPMSK_OTEPDM_Pos) // 0x00000010 +#define DOEPMSK_OTEPDM DOEPMSK_OTEPDM_Msk // OUT token received when endpoint disabled mask +#define DOEPMSK_OTEPSPRM_Pos (5U) +#define DOEPMSK_OTEPSPRM_Msk (0x1UL << DOEPMSK_OTEPSPRM_Pos) // 0x00000020 +#define DOEPMSK_OTEPSPRM DOEPMSK_OTEPSPRM_Msk // Status Phase Received mask +#define DOEPMSK_B2BSTUP_Pos (6U) +#define DOEPMSK_B2BSTUP_Msk (0x1UL << DOEPMSK_B2BSTUP_Pos) // 0x00000040 +#define DOEPMSK_B2BSTUP DOEPMSK_B2BSTUP_Msk // Back-to-back SETUP packets received mask +#define DOEPMSK_OPEM_Pos (8U) +#define DOEPMSK_OPEM_Msk (0x1UL << DOEPMSK_OPEM_Pos) // 0x00000100 +#define DOEPMSK_OPEM DOEPMSK_OPEM_Msk // OUT packet error mask +#define DOEPMSK_BOIM_Pos (9U) +#define DOEPMSK_BOIM_Msk (0x1UL << DOEPMSK_BOIM_Pos) // 0x00000200 +#define DOEPMSK_BOIM DOEPMSK_BOIM_Msk // BNA interrupt mask +#define DOEPMSK_BERRM_Pos (12U) +#define DOEPMSK_BERRM_Msk (0x1UL << DOEPMSK_BERRM_Pos) // 0x00001000 +#define DOEPMSK_BERRM DOEPMSK_BERRM_Msk // Babble error interrupt mask +#define DOEPMSK_NAKM_Pos (13U) +#define DOEPMSK_NAKM_Msk (0x1UL << DOEPMSK_NAKM_Pos) // 0x00002000 +#define DOEPMSK_NAKM DOEPMSK_NAKM_Msk // OUT Packet NAK interrupt mask +#define DOEPMSK_NYETM_Pos (14U) +#define DOEPMSK_NYETM_Msk (0x1UL << DOEPMSK_NYETM_Pos) // 0x00004000 +#define DOEPMSK_NYETM DOEPMSK_NYETM_Msk // NYET interrupt mask + +/******************** Bit definition for GINTSTS register ********************/ +#define GINTSTS_CMOD_Pos (0U) +#define GINTSTS_CMOD_Msk (0x1UL << GINTSTS_CMOD_Pos) // 0x00000001 +#define GINTSTS_CMOD GINTSTS_CMOD_Msk // Current mode of operation +#define GINTSTS_MMIS_Pos (1U) +#define GINTSTS_MMIS_Msk (0x1UL << GINTSTS_MMIS_Pos) // 0x00000002 +#define GINTSTS_MMIS GINTSTS_MMIS_Msk // Mode mismatch interrupt +#define GINTSTS_OTGINT_Pos (2U) +#define GINTSTS_OTGINT_Msk (0x1UL << GINTSTS_OTGINT_Pos) // 0x00000004 +#define GINTSTS_OTGINT GINTSTS_OTGINT_Msk // OTG interrupt +#define GINTSTS_SOF_Pos (3U) +#define GINTSTS_SOF_Msk (0x1UL << GINTSTS_SOF_Pos) // 0x00000008 +#define GINTSTS_SOF GINTSTS_SOF_Msk // Start of frame +#define GINTSTS_RXFLVL_Pos (4U) +#define GINTSTS_RXFLVL_Msk (0x1UL << GINTSTS_RXFLVL_Pos) // 0x00000010 +#define GINTSTS_RXFLVL GINTSTS_RXFLVL_Msk // RxFIFO nonempty +#define GINTSTS_NPTXFE_Pos (5U) +#define GINTSTS_NPTXFE_Msk (0x1UL << GINTSTS_NPTXFE_Pos) // 0x00000020 +#define GINTSTS_NPTXFE GINTSTS_NPTXFE_Msk // Nonperiodic TxFIFO empty +#define GINTSTS_GINAKEFF_Pos (6U) +#define GINTSTS_GINAKEFF_Msk (0x1UL << GINTSTS_GINAKEFF_Pos) // 0x00000040 +#define GINTSTS_GINAKEFF GINTSTS_GINAKEFF_Msk // Global IN nonperiodic NAK effective +#define GINTSTS_BOUTNAKEFF_Pos (7U) +#define GINTSTS_BOUTNAKEFF_Msk (0x1UL << GINTSTS_BOUTNAKEFF_Pos) // 0x00000080 +#define GINTSTS_BOUTNAKEFF GINTSTS_BOUTNAKEFF_Msk // Global OUT NAK effective +#define GINTSTS_ESUSP_Pos (10U) +#define GINTSTS_ESUSP_Msk (0x1UL << GINTSTS_ESUSP_Pos) // 0x00000400 +#define GINTSTS_ESUSP GINTSTS_ESUSP_Msk // Early suspend +#define GINTSTS_USBSUSP_Pos (11U) +#define GINTSTS_USBSUSP_Msk (0x1UL << GINTSTS_USBSUSP_Pos) // 0x00000800 +#define GINTSTS_USBSUSP GINTSTS_USBSUSP_Msk // USB suspend +#define GINTSTS_USBRST_Pos (12U) +#define GINTSTS_USBRST_Msk (0x1UL << GINTSTS_USBRST_Pos) // 0x00001000 +#define GINTSTS_USBRST GINTSTS_USBRST_Msk // USB reset +#define GINTSTS_ENUMDNE_Pos (13U) +#define GINTSTS_ENUMDNE_Msk (0x1UL << GINTSTS_ENUMDNE_Pos) // 0x00002000 +#define GINTSTS_ENUMDNE GINTSTS_ENUMDNE_Msk // Enumeration done +#define GINTSTS_ISOODRP_Pos (14U) +#define GINTSTS_ISOODRP_Msk (0x1UL << GINTSTS_ISOODRP_Pos) // 0x00004000 +#define GINTSTS_ISOODRP GINTSTS_ISOODRP_Msk // Isochronous OUT packet dropped interrupt +#define GINTSTS_EOPF_Pos (15U) +#define GINTSTS_EOPF_Msk (0x1UL << GINTSTS_EOPF_Pos) // 0x00008000 +#define GINTSTS_EOPF GINTSTS_EOPF_Msk // End of periodic frame interrupt +#define GINTSTS_IEPINT_Pos (18U) +#define GINTSTS_IEPINT_Msk (0x1UL << GINTSTS_IEPINT_Pos) // 0x00040000 +#define GINTSTS_IEPINT GINTSTS_IEPINT_Msk // IN endpoint interrupt +#define GINTSTS_OEPINT_Pos (19U) +#define GINTSTS_OEPINT_Msk (0x1UL << GINTSTS_OEPINT_Pos) // 0x00080000 +#define GINTSTS_OEPINT GINTSTS_OEPINT_Msk // OUT endpoint interrupt +#define GINTSTS_IISOIXFR_Pos (20U) +#define GINTSTS_IISOIXFR_Msk (0x1UL << GINTSTS_IISOIXFR_Pos) // 0x00100000 +#define GINTSTS_IISOIXFR GINTSTS_IISOIXFR_Msk // Incomplete isochronous IN transfer +#define GINTSTS_PXFR_INCOMPISOOUT_Pos (21U) +#define GINTSTS_PXFR_INCOMPISOOUT_Msk (0x1UL << GINTSTS_PXFR_INCOMPISOOUT_Pos) // 0x00200000 +#define GINTSTS_PXFR_INCOMPISOOUT GINTSTS_PXFR_INCOMPISOOUT_Msk // Incomplete periodic transfer +#define GINTSTS_DATAFSUSP_Pos (22U) +#define GINTSTS_DATAFSUSP_Msk (0x1UL << GINTSTS_DATAFSUSP_Pos) // 0x00400000 +#define GINTSTS_DATAFSUSP GINTSTS_DATAFSUSP_Msk // Data fetch suspended +#define GINTSTS_RSTDET_Pos (23U) +#define GINTSTS_RSTDET_Msk (0x1UL << GINTSTS_RSTDET_Pos) // 0x00800000 +#define GINTSTS_RSTDET GINTSTS_RSTDET_Msk // Reset detected interrupt +#define GINTSTS_HPRTINT_Pos (24U) +#define GINTSTS_HPRTINT_Msk (0x1UL << GINTSTS_HPRTINT_Pos) // 0x01000000 +#define GINTSTS_HPRTINT GINTSTS_HPRTINT_Msk // Host port interrupt +#define GINTSTS_HCINT_Pos (25U) +#define GINTSTS_HCINT_Msk (0x1UL << GINTSTS_HCINT_Pos) // 0x02000000 +#define GINTSTS_HCINT GINTSTS_HCINT_Msk // Host channels interrupt +#define GINTSTS_PTXFE_Pos (26U) +#define GINTSTS_PTXFE_Msk (0x1UL << GINTSTS_PTXFE_Pos) // 0x04000000 +#define GINTSTS_PTXFE GINTSTS_PTXFE_Msk // Periodic TxFIFO empty +#define GINTSTS_LPMINT_Pos (27U) +#define GINTSTS_LPMINT_Msk (0x1UL << GINTSTS_LPMINT_Pos) // 0x08000000 +#define GINTSTS_LPMINT GINTSTS_LPMINT_Msk // LPM interrupt +#define GINTSTS_CIDSCHG_Pos (28U) +#define GINTSTS_CIDSCHG_Msk (0x1UL << GINTSTS_CIDSCHG_Pos) // 0x10000000 +#define GINTSTS_CIDSCHG GINTSTS_CIDSCHG_Msk // Connector ID status change +#define GINTSTS_DISCINT_Pos (29U) +#define GINTSTS_DISCINT_Msk (0x1UL << GINTSTS_DISCINT_Pos) // 0x20000000 +#define GINTSTS_DISCINT GINTSTS_DISCINT_Msk // Disconnect detected interrupt +#define GINTSTS_SRQINT_Pos (30U) +#define GINTSTS_SRQINT_Msk (0x1UL << GINTSTS_SRQINT_Pos) // 0x40000000 +#define GINTSTS_SRQINT GINTSTS_SRQINT_Msk // Session request/new session detected interrupt +#define GINTSTS_WKUINT_Pos (31U) +#define GINTSTS_WKUINT_Msk (0x1UL << GINTSTS_WKUINT_Pos) // 0x80000000 +#define GINTSTS_WKUINT GINTSTS_WKUINT_Msk // Resume/remote wakeup detected interrupt + +/******************** Bit definition for GINTMSK register ********************/ +#define GINTMSK_MMISM_Pos (1U) +#define GINTMSK_MMISM_Msk (0x1UL << GINTMSK_MMISM_Pos) // 0x00000002 +#define GINTMSK_MMISM GINTMSK_MMISM_Msk // Mode mismatch interrupt mask +#define GINTMSK_OTGINT_Pos (2U) +#define GINTMSK_OTGINT_Msk (0x1UL << GINTMSK_OTGINT_Pos) // 0x00000004 +#define GINTMSK_OTGINT GINTMSK_OTGINT_Msk // OTG interrupt mask +#define GINTMSK_SOFM_Pos (3U) +#define GINTMSK_SOFM_Msk (0x1UL << GINTMSK_SOFM_Pos) // 0x00000008 +#define GINTMSK_SOFM GINTMSK_SOFM_Msk // Start of frame mask +#define GINTMSK_RXFLVLM_Pos (4U) +#define GINTMSK_RXFLVLM_Msk (0x1UL << GINTMSK_RXFLVLM_Pos) // 0x00000010 +#define GINTMSK_RXFLVLM GINTMSK_RXFLVLM_Msk // Receive FIFO nonempty mask +#define GINTMSK_NPTXFEM_Pos (5U) +#define GINTMSK_NPTXFEM_Msk (0x1UL << GINTMSK_NPTXFEM_Pos) // 0x00000020 +#define GINTMSK_NPTXFEM GINTMSK_NPTXFEM_Msk // Nonperiodic TxFIFO empty mask +#define GINTMSK_GINAKEFFM_Pos (6U) +#define GINTMSK_GINAKEFFM_Msk (0x1UL << GINTMSK_GINAKEFFM_Pos) // 0x00000040 +#define GINTMSK_GINAKEFFM GINTMSK_GINAKEFFM_Msk // Global nonperiodic IN NAK effective mask +#define GINTMSK_GONAKEFFM_Pos (7U) +#define GINTMSK_GONAKEFFM_Msk (0x1UL << GINTMSK_GONAKEFFM_Pos) // 0x00000080 +#define GINTMSK_GONAKEFFM GINTMSK_GONAKEFFM_Msk // Global OUT NAK effective mask +#define GINTMSK_ESUSPM_Pos (10U) +#define GINTMSK_ESUSPM_Msk (0x1UL << GINTMSK_ESUSPM_Pos) // 0x00000400 +#define GINTMSK_ESUSPM GINTMSK_ESUSPM_Msk // Early suspend mask +#define GINTMSK_USBSUSPM_Pos (11U) +#define GINTMSK_USBSUSPM_Msk (0x1UL << GINTMSK_USBSUSPM_Pos) // 0x00000800 +#define GINTMSK_USBSUSPM GINTMSK_USBSUSPM_Msk // USB suspend mask +#define GINTMSK_USBRST_Pos (12U) +#define GINTMSK_USBRST_Msk (0x1UL << GINTMSK_USBRST_Pos) // 0x00001000 +#define GINTMSK_USBRST GINTMSK_USBRST_Msk // USB reset mask +#define GINTMSK_ENUMDNEM_Pos (13U) +#define GINTMSK_ENUMDNEM_Msk (0x1UL << GINTMSK_ENUMDNEM_Pos) // 0x00002000 +#define GINTMSK_ENUMDNEM GINTMSK_ENUMDNEM_Msk // Enumeration done mask +#define GINTMSK_ISOODRPM_Pos (14U) +#define GINTMSK_ISOODRPM_Msk (0x1UL << GINTMSK_ISOODRPM_Pos) // 0x00004000 +#define GINTMSK_ISOODRPM GINTMSK_ISOODRPM_Msk // Isochronous OUT packet dropped interrupt mask +#define GINTMSK_EOPFM_Pos (15U) +#define GINTMSK_EOPFM_Msk (0x1UL << GINTMSK_EOPFM_Pos) // 0x00008000 +#define GINTMSK_EOPFM GINTMSK_EOPFM_Msk // End of periodic frame interrupt mask +#define GINTMSK_EPMISM_Pos (17U) +#define GINTMSK_EPMISM_Msk (0x1UL << GINTMSK_EPMISM_Pos) // 0x00020000 +#define GINTMSK_EPMISM GINTMSK_EPMISM_Msk // Endpoint mismatch interrupt mask +#define GINTMSK_IEPINT_Pos (18U) +#define GINTMSK_IEPINT_Msk (0x1UL << GINTMSK_IEPINT_Pos) // 0x00040000 +#define GINTMSK_IEPINT GINTMSK_IEPINT_Msk // IN endpoints interrupt mask +#define GINTMSK_OEPINT_Pos (19U) +#define GINTMSK_OEPINT_Msk (0x1UL << GINTMSK_OEPINT_Pos) // 0x00080000 +#define GINTMSK_OEPINT GINTMSK_OEPINT_Msk // OUT endpoints interrupt mask +#define GINTMSK_IISOIXFRM_Pos (20U) +#define GINTMSK_IISOIXFRM_Msk (0x1UL << GINTMSK_IISOIXFRM_Pos) // 0x00100000 +#define GINTMSK_IISOIXFRM GINTMSK_IISOIXFRM_Msk // Incomplete isochronous IN transfer mask +#define GINTMSK_PXFRM_IISOOXFRM_Pos (21U) +#define GINTMSK_PXFRM_IISOOXFRM_Msk (0x1UL << GINTMSK_PXFRM_IISOOXFRM_Pos) // 0x00200000 +#define GINTMSK_PXFRM_IISOOXFRM GINTMSK_PXFRM_IISOOXFRM_Msk // Incomplete periodic transfer mask +#define GINTMSK_FSUSPM_Pos (22U) +#define GINTMSK_FSUSPM_Msk (0x1UL << GINTMSK_FSUSPM_Pos) // 0x00400000 +#define GINTMSK_FSUSPM GINTMSK_FSUSPM_Msk // Data fetch suspended mask +#define GINTMSK_RSTDEM_Pos (23U) +#define GINTMSK_RSTDEM_Msk (0x1UL << GINTMSK_RSTDEM_Pos) // 0x00800000 +#define GINTMSK_RSTDEM GINTMSK_RSTDEM_Msk // Reset detected interrupt mask +#define GINTMSK_PRTIM_Pos (24U) +#define GINTMSK_PRTIM_Msk (0x1UL << GINTMSK_PRTIM_Pos) // 0x01000000 +#define GINTMSK_PRTIM GINTMSK_PRTIM_Msk // Host port interrupt mask +#define GINTMSK_HCIM_Pos (25U) +#define GINTMSK_HCIM_Msk (0x1UL << GINTMSK_HCIM_Pos) // 0x02000000 +#define GINTMSK_HCIM GINTMSK_HCIM_Msk // Host channels interrupt mask +#define GINTMSK_PTXFEM_Pos (26U) +#define GINTMSK_PTXFEM_Msk (0x1UL << GINTMSK_PTXFEM_Pos) // 0x04000000 +#define GINTMSK_PTXFEM GINTMSK_PTXFEM_Msk // Periodic TxFIFO empty mask +#define GINTMSK_LPMINTM_Pos (27U) +#define GINTMSK_LPMINTM_Msk (0x1UL << GINTMSK_LPMINTM_Pos) // 0x08000000 +#define GINTMSK_LPMINTM GINTMSK_LPMINTM_Msk // LPM interrupt Mask +#define GINTMSK_CIDSCHGM_Pos (28U) +#define GINTMSK_CIDSCHGM_Msk (0x1UL << GINTMSK_CIDSCHGM_Pos) // 0x10000000 +#define GINTMSK_CIDSCHGM GINTMSK_CIDSCHGM_Msk // Connector ID status change mask +#define GINTMSK_DISCINT_Pos (29U) +#define GINTMSK_DISCINT_Msk (0x1UL << GINTMSK_DISCINT_Pos) // 0x20000000 +#define GINTMSK_DISCINT GINTMSK_DISCINT_Msk // Disconnect detected interrupt mask +#define GINTMSK_SRQIM_Pos (30U) +#define GINTMSK_SRQIM_Msk (0x1UL << GINTMSK_SRQIM_Pos) // 0x40000000 +#define GINTMSK_SRQIM GINTMSK_SRQIM_Msk // Session request/new session detected interrupt mask +#define GINTMSK_WUIM_Pos (31U) +#define GINTMSK_WUIM_Msk (0x1UL << GINTMSK_WUIM_Pos) // 0x80000000 +#define GINTMSK_WUIM GINTMSK_WUIM_Msk // Resume/remote wakeup detected interrupt mask + +/******************** Bit definition for DAINT register ********************/ +#define DAINT_IEPINT_Pos (0U) +#define DAINT_IEPINT_Msk (0xFFFFUL << DAINT_IEPINT_Pos) // 0x0000FFFF +#define DAINT_IEPINT DAINT_IEPINT_Msk // IN endpoint interrupt bits +#define DAINT_OEPINT_Pos (16U) +#define DAINT_OEPINT_Msk (0xFFFFUL << DAINT_OEPINT_Pos) // 0xFFFF0000 +#define DAINT_OEPINT DAINT_OEPINT_Msk // OUT endpoint interrupt bits + +/******************** Bit definition for HAINTMSK register ********************/ +#define HAINTMSK_HAINTM_Pos (0U) +#define HAINTMSK_HAINTM_Msk (0xFFFFUL << HAINTMSK_HAINTM_Pos) // 0x0000FFFF +#define HAINTMSK_HAINTM HAINTMSK_HAINTM_Msk // Channel interrupt mask + +/******************** Bit definition for GRXSTSP register ********************/ +#define GRXSTSP_EPNUM_Pos (0U) +#define GRXSTSP_EPNUM_Msk (0xFUL << GRXSTSP_EPNUM_Pos) // 0x0000000F +#define GRXSTSP_EPNUM GRXSTSP_EPNUM_Msk // IN EP interrupt mask bits +#define GRXSTSP_BCNT_Pos (4U) +#define GRXSTSP_BCNT_Msk (0x7FFUL << GRXSTSP_BCNT_Pos) // 0x00007FF0 +#define GRXSTSP_BCNT GRXSTSP_BCNT_Msk // OUT EP interrupt mask bits +#define GRXSTSP_DPID_Pos (15U) +#define GRXSTSP_DPID_Msk (0x3UL << GRXSTSP_DPID_Pos) // 0x00018000 +#define GRXSTSP_DPID GRXSTSP_DPID_Msk // OUT EP interrupt mask bits +#define GRXSTSP_PKTSTS_Pos (17U) +#define GRXSTSP_PKTSTS_Msk (0xFUL << GRXSTSP_PKTSTS_Pos) // 0x001E0000 +#define GRXSTSP_PKTSTS GRXSTSP_PKTSTS_Msk // OUT EP interrupt mask bits + +#define GRXSTS_PKTSTS_GLOBALOUTNAK 1 +#define GRXSTS_PKTSTS_OUTRX 2 +#define GRXSTS_PKTSTS_HCHIN 2 +#define GRXSTS_PKTSTS_OUTDONE 3 +#define GRXSTS_PKTSTS_HCHIN_XFER_COMP 3 +#define GRXSTS_PKTSTS_SETUPDONE 4 +#define GRXSTS_PKTSTS_DATATOGGLEERR 5 +#define GRXSTS_PKTSTS_SETUPRX 6 +#define GRXSTS_PKTSTS_HCHHALTED 7 + + +/******************** Bit definition for DAINTMSK register ********************/ +#define DAINTMSK_IEPM_Pos (0U) +#define DAINTMSK_IEPM_Msk (0xFFFFUL << DAINTMSK_IEPM_Pos) // 0x0000FFFF +#define DAINTMSK_IEPM DAINTMSK_IEPM_Msk // IN EP interrupt mask bits +#define DAINTMSK_OEPM_Pos (16U) +#define DAINTMSK_OEPM_Msk (0xFFFFUL << DAINTMSK_OEPM_Pos) // 0xFFFF0000 +#define DAINTMSK_OEPM DAINTMSK_OEPM_Msk // OUT EP interrupt mask bits + +#if 0 +/******************** Bit definition for OTG register ********************/ +#define CHNUM_Pos (0U) +#define CHNUM_Msk (0xFUL << CHNUM_Pos) // 0x0000000F +#define CHNUM CHNUM_Msk // Channel number +#define CHNUM_0 (0x1UL << CHNUM_Pos) // 0x00000001 +#define CHNUM_1 (0x2UL << CHNUM_Pos) // 0x00000002 +#define CHNUM_2 (0x4UL << CHNUM_Pos) // 0x00000004 +#define CHNUM_3 (0x8UL << CHNUM_Pos) // 0x00000008 +#define BCNT_Pos (4U) +#define BCNT_Msk (0x7FFUL << BCNT_Pos) // 0x00007FF0 +#define BCNT BCNT_Msk // Byte count + +#define DPID_Pos (15U) +#define DPID_Msk (0x3UL << DPID_Pos) // 0x00018000 +#define DPID DPID_Msk // Data PID +#define DPID_0 (0x1UL << DPID_Pos) // 0x00008000 +#define DPID_1 (0x2UL << DPID_Pos) // 0x00010000 + +#define PKTSTS_Pos (17U) +#define PKTSTS_Msk (0xFUL << PKTSTS_Pos) // 0x001E0000 +#define PKTSTS PKTSTS_Msk // Packet status +#define PKTSTS_0 (0x1UL << PKTSTS_Pos) // 0x00020000 +#define PKTSTS_1 (0x2UL << PKTSTS_Pos) // 0x00040000 +#define PKTSTS_2 (0x4UL << PKTSTS_Pos) // 0x00080000 +#define PKTSTS_3 (0x8UL << PKTSTS_Pos) // 0x00100000 + +#define EPNUM_Pos (0U) +#define EPNUM_Msk (0xFUL << EPNUM_Pos) // 0x0000000F +#define EPNUM EPNUM_Msk // Endpoint number +#define EPNUM_0 (0x1UL << EPNUM_Pos) // 0x00000001 +#define EPNUM_1 (0x2UL << EPNUM_Pos) // 0x00000002 +#define EPNUM_2 (0x4UL << EPNUM_Pos) // 0x00000004 +#define EPNUM_3 (0x8UL << EPNUM_Pos) // 0x00000008 + +#define FRMNUM_Pos (21U) +#define FRMNUM_Msk (0xFUL << FRMNUM_Pos) // 0x01E00000 +#define FRMNUM FRMNUM_Msk // Frame number +#define FRMNUM_0 (0x1UL << FRMNUM_Pos) // 0x00200000 +#define FRMNUM_1 (0x2UL << FRMNUM_Pos) // 0x00400000 +#define FRMNUM_2 (0x4UL << FRMNUM_Pos) // 0x00800000 +#define FRMNUM_3 (0x8UL << FRMNUM_Pos) // 0x01000000 +#endif + +/******************** Bit definition for GRXFSIZ register ********************/ +#define GRXFSIZ_RXFD_Pos (0U) +#define GRXFSIZ_RXFD_Msk (0xFFFFUL << GRXFSIZ_RXFD_Pos) // 0x0000FFFF +#define GRXFSIZ_RXFD GRXFSIZ_RXFD_Msk // RxFIFO depth + +/******************** Bit definition for DVBUSDIS register ********************/ +#define DVBUSDIS_VBUSDT_Pos (0U) +#define DVBUSDIS_VBUSDT_Msk (0xFFFFUL << DVBUSDIS_VBUSDT_Pos) // 0x0000FFFF +#define DVBUSDIS_VBUSDT DVBUSDIS_VBUSDT_Msk // Device VBUS discharge time + +/******************** Bit definition for OTG register ********************/ +#define GNPTXFSIZ_NPTXFSA_Pos (0U) +#define GNPTXFSIZ_NPTXFSA_Msk (0xFFFFUL << GNPTXFSIZ_NPTXFSA_Pos) // 0x0000FFFF +#define GNPTXFSIZ_NPTXFSA GNPTXFSIZ_NPTXFSA_Msk // Nonperiodic transmit RAM start address +#define GNPTXFSIZ_NPTXFD_Pos (16U) +#define GNPTXFSIZ_NPTXFD_Msk (0xFFFFUL << GNPTXFSIZ_NPTXFD_Pos) // 0xFFFF0000 +#define GNPTXFSIZ_NPTXFD GNPTXFSIZ_NPTXFD_Msk // Nonperiodic TxFIFO depth +#define DIEPTXF0_TX0FSA_Pos (0U) +#define DIEPTXF0_TX0FSA_Msk (0xFFFFUL << DIEPTXF0_TX0FSA_Pos) // 0x0000FFFF +#define DIEPTXF0_TX0FSA DIEPTXF0_TX0FSA_Msk // Endpoint 0 transmit RAM start address +#define DIEPTXF0_TX0FD_Pos (16U) +#define DIEPTXF0_TX0FD_Msk (0xFFFFUL << DIEPTXF0_TX0FD_Pos) // 0xFFFF0000 +#define DIEPTXF0_TX0FD DIEPTXF0_TX0FD_Msk // Endpoint 0 TxFIFO depth + +/******************** Bit definition for DVBUSPULSE register ********************/ +#define DVBUSPULSE_DVBUSP_Pos (0U) +#define DVBUSPULSE_DVBUSP_Msk (0xFFFUL << DVBUSPULSE_DVBUSP_Pos) // 0x00000FFF +#define DVBUSPULSE_DVBUSP DVBUSPULSE_DVBUSP_Msk // Device VBUS pulsing time + +/******************** Bit definition for GNPTXSTS register ********************/ +#define GNPTXSTS_NPTXFSAV_Pos (0U) +#define GNPTXSTS_NPTXFSAV_Msk (0xFFFFUL << GNPTXSTS_NPTXFSAV_Pos) // 0x0000FFFF +#define GNPTXSTS_NPTXFSAV GNPTXSTS_NPTXFSAV_Msk // Nonperiodic TxFIFO space available + +#define GNPTXSTS_NPTQXSAV_Pos (16U) +#define GNPTXSTS_NPTQXSAV_Msk (0xFFUL << GNPTXSTS_NPTQXSAV_Pos) // 0x00FF0000 +#define GNPTXSTS_NPTQXSAV GNPTXSTS_NPTQXSAV_Msk // Nonperiodic transmit request queue space available +#define GNPTXSTS_NPTQXSAV_0 (0x01UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00010000 +#define GNPTXSTS_NPTQXSAV_1 (0x02UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00020000 +#define GNPTXSTS_NPTQXSAV_2 (0x04UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00040000 +#define GNPTXSTS_NPTQXSAV_3 (0x08UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00080000 +#define GNPTXSTS_NPTQXSAV_4 (0x10UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00100000 +#define GNPTXSTS_NPTQXSAV_5 (0x20UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00200000 +#define GNPTXSTS_NPTQXSAV_6 (0x40UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00400000 +#define GNPTXSTS_NPTQXSAV_7 (0x80UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00800000 + +#define GNPTXSTS_NPTXQTOP_Pos (24U) +#define GNPTXSTS_NPTXQTOP_Msk (0x7FUL << GNPTXSTS_NPTXQTOP_Pos) // 0x7F000000 +#define GNPTXSTS_NPTXQTOP GNPTXSTS_NPTXQTOP_Msk // Top of the nonperiodic transmit request queue +#define GNPTXSTS_NPTXQTOP_0 (0x01UL << GNPTXSTS_NPTXQTOP_Pos) // 0x01000000 +#define GNPTXSTS_NPTXQTOP_1 (0x02UL << GNPTXSTS_NPTXQTOP_Pos) // 0x02000000 +#define GNPTXSTS_NPTXQTOP_2 (0x04UL << GNPTXSTS_NPTXQTOP_Pos) // 0x04000000 +#define GNPTXSTS_NPTXQTOP_3 (0x08UL << GNPTXSTS_NPTXQTOP_Pos) // 0x08000000 +#define GNPTXSTS_NPTXQTOP_4 (0x10UL << GNPTXSTS_NPTXQTOP_Pos) // 0x10000000 +#define GNPTXSTS_NPTXQTOP_5 (0x20UL << GNPTXSTS_NPTXQTOP_Pos) // 0x20000000 +#define GNPTXSTS_NPTXQTOP_6 (0x40UL << GNPTXSTS_NPTXQTOP_Pos) // 0x40000000 + +/******************** Bit definition for DTHRCTL register ********************/ +#define DTHRCTL_NONISOTHREN_Pos (0U) +#define DTHRCTL_NONISOTHREN_Msk (0x1UL << DTHRCTL_NONISOTHREN_Pos) // 0x00000001 +#define DTHRCTL_NONISOTHREN DTHRCTL_NONISOTHREN_Msk // Nonisochronous IN endpoints threshold enable +#define DTHRCTL_ISOTHREN_Pos (1U) +#define DTHRCTL_ISOTHREN_Msk (0x1UL << DTHRCTL_ISOTHREN_Pos) // 0x00000002 +#define DTHRCTL_ISOTHREN DTHRCTL_ISOTHREN_Msk // ISO IN endpoint threshold enable + +#define DTHRCTL_TXTHRLEN_Pos (2U) +#define DTHRCTL_TXTHRLEN_Msk (0x1FFUL << DTHRCTL_TXTHRLEN_Pos) // 0x000007FC +#define DTHRCTL_TXTHRLEN DTHRCTL_TXTHRLEN_Msk // Transmit threshold length +#define DTHRCTL_TXTHRLEN_0 (0x001UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000004 +#define DTHRCTL_TXTHRLEN_1 (0x002UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000008 +#define DTHRCTL_TXTHRLEN_2 (0x004UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000010 +#define DTHRCTL_TXTHRLEN_3 (0x008UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000020 +#define DTHRCTL_TXTHRLEN_4 (0x010UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000040 +#define DTHRCTL_TXTHRLEN_5 (0x020UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000080 +#define DTHRCTL_TXTHRLEN_6 (0x040UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000100 +#define DTHRCTL_TXTHRLEN_7 (0x080UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000200 +#define DTHRCTL_TXTHRLEN_8 (0x100UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000400 +#define DTHRCTL_RXTHREN_Pos (16U) +#define DTHRCTL_RXTHREN_Msk (0x1UL << DTHRCTL_RXTHREN_Pos) // 0x00010000 +#define DTHRCTL_RXTHREN DTHRCTL_RXTHREN_Msk // Receive threshold enable + +#define DTHRCTL_RXTHRLEN_Pos (17U) +#define DTHRCTL_RXTHRLEN_Msk (0x1FFUL << DTHRCTL_RXTHRLEN_Pos) // 0x03FE0000 +#define DTHRCTL_RXTHRLEN DTHRCTL_RXTHRLEN_Msk // Receive threshold length +#define DTHRCTL_RXTHRLEN_0 (0x001UL << DTHRCTL_RXTHRLEN_Pos) // 0x00020000 +#define DTHRCTL_RXTHRLEN_1 (0x002UL << DTHRCTL_RXTHRLEN_Pos) // 0x00040000 +#define DTHRCTL_RXTHRLEN_2 (0x004UL << DTHRCTL_RXTHRLEN_Pos) // 0x00080000 +#define DTHRCTL_RXTHRLEN_3 (0x008UL << DTHRCTL_RXTHRLEN_Pos) // 0x00100000 +#define DTHRCTL_RXTHRLEN_4 (0x010UL << DTHRCTL_RXTHRLEN_Pos) // 0x00200000 +#define DTHRCTL_RXTHRLEN_5 (0x020UL << DTHRCTL_RXTHRLEN_Pos) // 0x00400000 +#define DTHRCTL_RXTHRLEN_6 (0x040UL << DTHRCTL_RXTHRLEN_Pos) // 0x00800000 +#define DTHRCTL_RXTHRLEN_7 (0x080UL << DTHRCTL_RXTHRLEN_Pos) // 0x01000000 +#define DTHRCTL_RXTHRLEN_8 (0x100UL << DTHRCTL_RXTHRLEN_Pos) // 0x02000000 +#define DTHRCTL_ARPEN_Pos (27U) +#define DTHRCTL_ARPEN_Msk (0x1UL << DTHRCTL_ARPEN_Pos) // 0x08000000 +#define DTHRCTL_ARPEN DTHRCTL_ARPEN_Msk // Arbiter parking enable + +/******************** Bit definition for DIEPEMPMSK register ********************/ +#define DIEPEMPMSK_INEPTXFEM_Pos (0U) +#define DIEPEMPMSK_INEPTXFEM_Msk (0xFFFFUL << DIEPEMPMSK_INEPTXFEM_Pos) // 0x0000FFFF +#define DIEPEMPMSK_INEPTXFEM DIEPEMPMSK_INEPTXFEM_Msk // IN EP Tx FIFO empty interrupt mask bits + +/******************** Bit definition for DEACHINT register ********************/ +#define DEACHINT_IEP1INT_Pos (1U) +#define DEACHINT_IEP1INT_Msk (0x1UL << DEACHINT_IEP1INT_Pos) // 0x00000002 +#define DEACHINT_IEP1INT DEACHINT_IEP1INT_Msk // IN endpoint 1interrupt bit +#define DEACHINT_OEP1INT_Pos (17U) +#define DEACHINT_OEP1INT_Msk (0x1UL << DEACHINT_OEP1INT_Pos) // 0x00020000 +#define DEACHINT_OEP1INT DEACHINT_OEP1INT_Msk // OUT endpoint 1 interrupt bit + +/******************** Bit definition for GCCFG register ********************/ +#define STM32_GCCFG_DCDET_Pos (0U) +#define STM32_GCCFG_DCDET_Msk (0x1UL << STM32_GCCFG_DCDET_Pos) // 0x00000001 +#define STM32_GCCFG_DCDET STM32_GCCFG_DCDET_Msk // Data contact detection (DCD) status + +#define STM32_GCCFG_PDET_Pos (1U) +#define STM32_GCCFG_PDET_Msk (0x1UL << STM32_GCCFG_PDET_Pos) // 0x00000002 +#define STM32_GCCFG_PDET STM32_GCCFG_PDET_Msk // Primary detection (PD) status + +#define STM32_GCCFG_SDET_Pos (2U) +#define STM32_GCCFG_SDET_Msk (0x1UL << STM32_GCCFG_SDET_Pos) // 0x00000004 +#define STM32_GCCFG_SDET STM32_GCCFG_SDET_Msk // Secondary detection (SD) status + +#define STM32_GCCFG_PS2DET_Pos (3U) +#define STM32_GCCFG_PS2DET_Msk (0x1UL << STM32_GCCFG_PS2DET_Pos) // 0x00000008 +#define STM32_GCCFG_PS2DET STM32_GCCFG_PS2DET_Msk // DM pull-up detection status + +#define STM32_GCCFG_PWRDWN_Pos (16U) +#define STM32_GCCFG_PWRDWN_Msk (0x1UL << STM32_GCCFG_PWRDWN_Pos) // 0x00010000 +#define STM32_GCCFG_PWRDWN STM32_GCCFG_PWRDWN_Msk // Power down + +#define STM32_GCCFG_BCDEN_Pos (17U) +#define STM32_GCCFG_BCDEN_Msk (0x1UL << STM32_GCCFG_BCDEN_Pos) // 0x00020000 +#define STM32_GCCFG_BCDEN STM32_GCCFG_BCDEN_Msk // Battery charging detector (BCD) enable + +#define STM32_GCCFG_DCDEN_Pos (18U) +#define STM32_GCCFG_DCDEN_Msk (0x1UL << STM32_GCCFG_DCDEN_Pos) // 0x00040000 +#define STM32_GCCFG_DCDEN STM32_GCCFG_DCDEN_Msk // Data contact detection (DCD) mode enable*/ + +#define STM32_GCCFG_PDEN_Pos (19U) +#define STM32_GCCFG_PDEN_Msk (0x1UL << STM32_GCCFG_PDEN_Pos) // 0x00080000 +#define STM32_GCCFG_PDEN STM32_GCCFG_PDEN_Msk // Primary detection (PD) mode enable*/ + +#define STM32_GCCFG_SDEN_Pos (20U) +#define STM32_GCCFG_SDEN_Msk (0x1UL << STM32_GCCFG_SDEN_Pos) // 0x00100000 +#define STM32_GCCFG_SDEN STM32_GCCFG_SDEN_Msk // Secondary detection (SD) mode enable + +#define STM32_GCCFG_VBDEN_Pos (21U) +#define STM32_GCCFG_VBDEN_Msk (0x1UL << STM32_GCCFG_VBDEN_Pos) // 0x00200000 +#define STM32_GCCFG_VBDEN STM32_GCCFG_VBDEN_Msk // VBUS mode enable + +#define STM32_GCCFG_OTGIDEN_Pos (22U) +#define STM32_GCCFG_OTGIDEN_Msk (0x1UL << STM32_GCCFG_OTGIDEN_Pos) // 0x00400000 +#define STM32_GCCFG_OTGIDEN STM32_GCCFG_OTGIDEN_Msk // OTG Id enable + +#define STM32_GCCFG_PHYHSEN_Pos (23U) +#define STM32_GCCFG_PHYHSEN_Msk (0x1UL << STM32_GCCFG_PHYHSEN_Pos) // 0x00800000 +#define STM32_GCCFG_PHYHSEN STM32_GCCFG_PHYHSEN_Msk // HS PHY enable + +// TODO stm32u5a5 SDEN is 22nd bit, conflict with 20th bit above +//#define STM32_GCCFG_SDEN_Pos (22U) +//#define STM32_GCCFG_SDEN_Msk (0x1U << STM32_GCCFG_SDEN_Pos) // 0x00400000 +//#define STM32_GCCFG_SDEN STM32_GCCFG_SDEN_Msk // Secondary detection (PD) mode enable + +// TODO stm32u5a5 VBVALOVA is 23rd bit, conflict with PHYHSEN bit above +#define STM32_GCCFG_VBVALOVAL_Pos (23U) +#define STM32_GCCFG_VBVALOVAL_Msk (0x1U << STM32_GCCFG_VBVALOVAL_Pos) // 0x00800000 +#define STM32_GCCFG_VBVALOVAL STM32_GCCFG_VBVALOVAL_Msk // Value of VBUSVLDEXT0 femtoPHY input + +#define STM32_GCCFG_VBVALEXTOEN_Pos (24U) +#define STM32_GCCFG_VBVALEXTOEN_Msk (0x1U << STM32_GCCFG_VBVALEXTOEN_Pos) // 0x01000000 +#define STM32_GCCFG_VBVALEXTOEN STM32_GCCFG_VBVALEXTOEN_Msk // Enables of VBUSVLDEXT0 femtoPHY input override + +#define STM32_GCCFG_PULLDOWNEN_Pos (25U) +#define STM32_GCCFG_PULLDOWNEN_Msk (0x1U << STM32_GCCFG_PULLDOWNEN_Pos) // 0x02000000 +#define STM32_GCCFG_PULLDOWNEN STM32_GCCFG_PULLDOWNEN_Msk // Enables of femtoPHY pulldown resistors, used when ID PAD is disabled + + +/******************** Bit definition for DEACHINTMSK register ********************/ +#define DEACHINTMSK_IEP1INTM_Pos (1U) +#define DEACHINTMSK_IEP1INTM_Msk (0x1UL << DEACHINTMSK_IEP1INTM_Pos) // 0x00000002 +#define DEACHINTMSK_IEP1INTM DEACHINTMSK_IEP1INTM_Msk // IN Endpoint 1 interrupt mask bit +#define DEACHINTMSK_OEP1INTM_Pos (17U) +#define DEACHINTMSK_OEP1INTM_Msk (0x1UL << DEACHINTMSK_OEP1INTM_Pos) // 0x00020000 +#define DEACHINTMSK_OEP1INTM DEACHINTMSK_OEP1INTM_Msk // OUT Endpoint 1 interrupt mask bit + +/******************** Bit definition for CID register ********************/ +#define CID_PRODUCT_ID_Pos (0U) +#define CID_PRODUCT_ID_Msk (0xFFFFFFFFUL << CID_PRODUCT_ID_Pos) // 0xFFFFFFFF +#define CID_PRODUCT_ID CID_PRODUCT_ID_Msk // Product ID field + +/******************** Bit definition for GLPMCFG register ********************/ +#define GLPMCFG_LPMEN_Pos (0U) +#define GLPMCFG_LPMEN_Msk (0x1UL << GLPMCFG_LPMEN_Pos) // 0x00000001 +#define GLPMCFG_LPMEN GLPMCFG_LPMEN_Msk // LPM support enable +#define GLPMCFG_LPMACK_Pos (1U) +#define GLPMCFG_LPMACK_Msk (0x1UL << GLPMCFG_LPMACK_Pos) // 0x00000002 +#define GLPMCFG_LPMACK GLPMCFG_LPMACK_Msk // LPM Token acknowledge enable +#define GLPMCFG_BESL_Pos (2U) +#define GLPMCFG_BESL_Msk (0xFUL << GLPMCFG_BESL_Pos) // 0x0000003C +#define GLPMCFG_BESL GLPMCFG_BESL_Msk // BESL value received with last ACKed LPM Token +#define GLPMCFG_REMWAKE_Pos (6U) +#define GLPMCFG_REMWAKE_Msk (0x1UL << GLPMCFG_REMWAKE_Pos) // 0x00000040 +#define GLPMCFG_REMWAKE GLPMCFG_REMWAKE_Msk // bRemoteWake value received with last ACKed LPM Token +#define GLPMCFG_L1SSEN_Pos (7U) +#define GLPMCFG_L1SSEN_Msk (0x1UL << GLPMCFG_L1SSEN_Pos) // 0x00000080 +#define GLPMCFG_L1SSEN GLPMCFG_L1SSEN_Msk // L1 shallow sleep enable +#define GLPMCFG_BESLTHRS_Pos (8U) +#define GLPMCFG_BESLTHRS_Msk (0xFUL << GLPMCFG_BESLTHRS_Pos) // 0x00000F00 +#define GLPMCFG_BESLTHRS GLPMCFG_BESLTHRS_Msk // BESL threshold +#define GLPMCFG_L1DSEN_Pos (12U) +#define GLPMCFG_L1DSEN_Msk (0x1UL << GLPMCFG_L1DSEN_Pos) // 0x00001000 +#define GLPMCFG_L1DSEN GLPMCFG_L1DSEN_Msk // L1 deep sleep enable +#define GLPMCFG_LPMRSP_Pos (13U) +#define GLPMCFG_LPMRSP_Msk (0x3UL << GLPMCFG_LPMRSP_Pos) // 0x00006000 +#define GLPMCFG_LPMRSP GLPMCFG_LPMRSP_Msk // LPM response +#define GLPMCFG_SLPSTS_Pos (15U) +#define GLPMCFG_SLPSTS_Msk (0x1UL << GLPMCFG_SLPSTS_Pos) // 0x00008000 +#define GLPMCFG_SLPSTS GLPMCFG_SLPSTS_Msk // Port sleep status +#define GLPMCFG_L1RSMOK_Pos (16U) +#define GLPMCFG_L1RSMOK_Msk (0x1UL << GLPMCFG_L1RSMOK_Pos) // 0x00010000 +#define GLPMCFG_L1RSMOK GLPMCFG_L1RSMOK_Msk // Sleep State Resume OK +#define GLPMCFG_LPMCHIDX_Pos (17U) +#define GLPMCFG_LPMCHIDX_Msk (0xFUL << GLPMCFG_LPMCHIDX_Pos) // 0x001E0000 +#define GLPMCFG_LPMCHIDX GLPMCFG_LPMCHIDX_Msk // LPM Channel Index +#define GLPMCFG_LPMRCNT_Pos (21U) +#define GLPMCFG_LPMRCNT_Msk (0x7UL << GLPMCFG_LPMRCNT_Pos) // 0x00E00000 +#define GLPMCFG_LPMRCNT GLPMCFG_LPMRCNT_Msk // LPM retry count +#define GLPMCFG_SNDLPM_Pos (24U) +#define GLPMCFG_SNDLPM_Msk (0x1UL << GLPMCFG_SNDLPM_Pos) // 0x01000000 +#define GLPMCFG_SNDLPM GLPMCFG_SNDLPM_Msk // Send LPM transaction +#define GLPMCFG_LPMRCNTSTS_Pos (25U) +#define GLPMCFG_LPMRCNTSTS_Msk (0x7UL << GLPMCFG_LPMRCNTSTS_Pos) // 0x0E000000 +#define GLPMCFG_LPMRCNTSTS GLPMCFG_LPMRCNTSTS_Msk // LPM retry count status +#define GLPMCFG_ENBESL_Pos (28U) +#define GLPMCFG_ENBESL_Msk (0x1UL << GLPMCFG_ENBESL_Pos) // 0x10000000 +#define GLPMCFG_ENBESL GLPMCFG_ENBESL_Msk // Enable best effort service latency + +/******************** Bit definition for DIEPEACHMSK1 register ********************/ +#define DIEPEACHMSK1_XFRCM_Pos (0U) +#define DIEPEACHMSK1_XFRCM_Msk (0x1UL << DIEPEACHMSK1_XFRCM_Pos) // 0x00000001 +#define DIEPEACHMSK1_XFRCM DIEPEACHMSK1_XFRCM_Msk // Transfer completed interrupt mask +#define DIEPEACHMSK1_EPDM_Pos (1U) +#define DIEPEACHMSK1_EPDM_Msk (0x1UL << DIEPEACHMSK1_EPDM_Pos) // 0x00000002 +#define DIEPEACHMSK1_EPDM DIEPEACHMSK1_EPDM_Msk // Endpoint disabled interrupt mask +#define DIEPEACHMSK1_TOM_Pos (3U) +#define DIEPEACHMSK1_TOM_Msk (0x1UL << DIEPEACHMSK1_TOM_Pos) // 0x00000008 +#define DIEPEACHMSK1_TOM DIEPEACHMSK1_TOM_Msk // Timeout condition mask (nonisochronous endpoints) +#define DIEPEACHMSK1_ITTXFEMSK_Pos (4U) +#define DIEPEACHMSK1_ITTXFEMSK_Msk (0x1UL << DIEPEACHMSK1_ITTXFEMSK_Pos) // 0x00000010 +#define DIEPEACHMSK1_ITTXFEMSK DIEPEACHMSK1_ITTXFEMSK_Msk // IN token received when TxFIFO empty mask +#define DIEPEACHMSK1_INEPNMM_Pos (5U) +#define DIEPEACHMSK1_INEPNMM_Msk (0x1UL << DIEPEACHMSK1_INEPNMM_Pos) // 0x00000020 +#define DIEPEACHMSK1_INEPNMM DIEPEACHMSK1_INEPNMM_Msk // IN token received with EP mismatch mask +#define DIEPEACHMSK1_INEPNEM_Pos (6U) +#define DIEPEACHMSK1_INEPNEM_Msk (0x1UL << DIEPEACHMSK1_INEPNEM_Pos) // 0x00000040 +#define DIEPEACHMSK1_INEPNEM DIEPEACHMSK1_INEPNEM_Msk // IN endpoint NAK effective mask +#define DIEPEACHMSK1_TXFURM_Pos (8U) +#define DIEPEACHMSK1_TXFURM_Msk (0x1UL << DIEPEACHMSK1_TXFURM_Pos) // 0x00000100 +#define DIEPEACHMSK1_TXFURM DIEPEACHMSK1_TXFURM_Msk // FIFO underrun mask +#define DIEPEACHMSK1_BIM_Pos (9U) +#define DIEPEACHMSK1_BIM_Msk (0x1UL << DIEPEACHMSK1_BIM_Pos) // 0x00000200 +#define DIEPEACHMSK1_BIM DIEPEACHMSK1_BIM_Msk // BNA interrupt mask +#define DIEPEACHMSK1_NAKM_Pos (13U) +#define DIEPEACHMSK1_NAKM_Msk (0x1UL << DIEPEACHMSK1_NAKM_Pos) // 0x00002000 +#define DIEPEACHMSK1_NAKM DIEPEACHMSK1_NAKM_Msk // NAK interrupt mask + +/******************** Bit definition for HPRT register ********************/ +#define HPRT_PCSTS_Pos (0U) +#define HPRT_PCSTS_Msk (0x1UL << HPRT_PCSTS_Pos) // 0x00000001 +#define HPRT_PCSTS HPRT_PCSTS_Msk // Port connect status +#define HPRT_PCDET_Pos (1U) +#define HPRT_PCDET_Msk (0x1UL << HPRT_PCDET_Pos) // 0x00000002 +#define HPRT_PCDET HPRT_PCDET_Msk // Port connect detected +#define HPRT_PENA_Pos (2U) +#define HPRT_PENA_Msk (0x1UL << HPRT_PENA_Pos) // 0x00000004 +#define HPRT_PENA HPRT_PENA_Msk // Port enable +#define HPRT_PENCHNG_Pos (3U) +#define HPRT_PENCHNG_Msk (0x1UL << HPRT_PENCHNG_Pos) // 0x00000008 +#define HPRT_PENCHNG HPRT_PENCHNG_Msk // Port enable/disable change +#define HPRT_POCA_Pos (4U) +#define HPRT_POCA_Msk (0x1UL << HPRT_POCA_Pos) // 0x00000010 +#define HPRT_POCA HPRT_POCA_Msk // Port overcurrent active +#define HPRT_POCCHNG_Pos (5U) +#define HPRT_POCCHNG_Msk (0x1UL << HPRT_POCCHNG_Pos) // 0x00000020 +#define HPRT_POCCHNG HPRT_POCCHNG_Msk // Port overcurrent change +#define HPRT_PRES_Pos (6U) +#define HPRT_PRES_Msk (0x1UL << HPRT_PRES_Pos) // 0x00000040 +#define HPRT_PRES HPRT_PRES_Msk // Port resume +#define HPRT_PSUSP_Pos (7U) +#define HPRT_PSUSP_Msk (0x1UL << HPRT_PSUSP_Pos) // 0x00000080 +#define HPRT_PSUSP HPRT_PSUSP_Msk // Port suspend +#define HPRT_PRST_Pos (8U) +#define HPRT_PRST_Msk (0x1UL << HPRT_PRST_Pos) // 0x00000100 +#define HPRT_PRST HPRT_PRST_Msk // Port reset + +#define HPRT_PLSTS_Pos (10U) +#define HPRT_PLSTS_Msk (0x3UL << HPRT_PLSTS_Pos) // 0x00000C00 +#define HPRT_PLSTS HPRT_PLSTS_Msk // Port line status +#define HPRT_PLSTS_0 (0x1UL << HPRT_PLSTS_Pos) // 0x00000400 +#define HPRT_PLSTS_1 (0x2UL << HPRT_PLSTS_Pos) // 0x00000800 +#define HPRT_PPWR_Pos (12U) +#define HPRT_PPWR_Msk (0x1UL << HPRT_PPWR_Pos) // 0x00001000 +#define HPRT_PPWR HPRT_PPWR_Msk // Port power + +#define HPRT_PTCTL_Pos (13U) +#define HPRT_PTCTL_Msk (0xFUL << HPRT_PTCTL_Pos) // 0x0001E000 +#define HPRT_PTCTL HPRT_PTCTL_Msk // Port test control +#define HPRT_PTCTL_0 (0x1UL << HPRT_PTCTL_Pos) // 0x00002000 +#define HPRT_PTCTL_1 (0x2UL << HPRT_PTCTL_Pos) // 0x00004000 +#define HPRT_PTCTL_2 (0x4UL << HPRT_PTCTL_Pos) // 0x00008000 +#define HPRT_PTCTL_3 (0x8UL << HPRT_PTCTL_Pos) // 0x00010000 + +#define HPRT_PSPD_Pos (17U) +#define HPRT_PSPD_Msk (0x3UL << HPRT_PSPD_Pos) // 0x00060000 +#define HPRT_PSPD HPRT_PSPD_Msk // Port speed +#define HPRT_PSPD_0 (0x1UL << HPRT_PSPD_Pos) // 0x00020000 +#define HPRT_PSPD_1 (0x2UL << HPRT_PSPD_Pos) // 0x00040000 + +/******************** Bit definition for DOEPEACHMSK1 register ********************/ +#define DOEPEACHMSK1_XFRCM_Pos (0U) +#define DOEPEACHMSK1_XFRCM_Msk (0x1UL << DOEPEACHMSK1_XFRCM_Pos) // 0x00000001 +#define DOEPEACHMSK1_XFRCM DOEPEACHMSK1_XFRCM_Msk // Transfer completed interrupt mask +#define DOEPEACHMSK1_EPDM_Pos (1U) +#define DOEPEACHMSK1_EPDM_Msk (0x1UL << DOEPEACHMSK1_EPDM_Pos) // 0x00000002 +#define DOEPEACHMSK1_EPDM DOEPEACHMSK1_EPDM_Msk // Endpoint disabled interrupt mask +#define DOEPEACHMSK1_TOM_Pos (3U) +#define DOEPEACHMSK1_TOM_Msk (0x1UL << DOEPEACHMSK1_TOM_Pos) // 0x00000008 +#define DOEPEACHMSK1_TOM DOEPEACHMSK1_TOM_Msk // Timeout condition mask +#define DOEPEACHMSK1_ITTXFEMSK_Pos (4U) +#define DOEPEACHMSK1_ITTXFEMSK_Msk (0x1UL << DOEPEACHMSK1_ITTXFEMSK_Pos) // 0x00000010 +#define DOEPEACHMSK1_ITTXFEMSK DOEPEACHMSK1_ITTXFEMSK_Msk // IN token received when TxFIFO empty mask +#define DOEPEACHMSK1_INEPNMM_Pos (5U) +#define DOEPEACHMSK1_INEPNMM_Msk (0x1UL << DOEPEACHMSK1_INEPNMM_Pos) // 0x00000020 +#define DOEPEACHMSK1_INEPNMM DOEPEACHMSK1_INEPNMM_Msk // IN token received with EP mismatch mask +#define DOEPEACHMSK1_INEPNEM_Pos (6U) +#define DOEPEACHMSK1_INEPNEM_Msk (0x1UL << DOEPEACHMSK1_INEPNEM_Pos) // 0x00000040 +#define DOEPEACHMSK1_INEPNEM DOEPEACHMSK1_INEPNEM_Msk // IN endpoint NAK effective mask +#define DOEPEACHMSK1_TXFURM_Pos (8U) +#define DOEPEACHMSK1_TXFURM_Msk (0x1UL << DOEPEACHMSK1_TXFURM_Pos) // 0x00000100 +#define DOEPEACHMSK1_TXFURM DOEPEACHMSK1_TXFURM_Msk // OUT packet error mask +#define DOEPEACHMSK1_BIM_Pos (9U) +#define DOEPEACHMSK1_BIM_Msk (0x1UL << DOEPEACHMSK1_BIM_Pos) // 0x00000200 +#define DOEPEACHMSK1_BIM DOEPEACHMSK1_BIM_Msk // BNA interrupt mask +#define DOEPEACHMSK1_BERRM_Pos (12U) +#define DOEPEACHMSK1_BERRM_Msk (0x1UL << DOEPEACHMSK1_BERRM_Pos) // 0x00001000 +#define DOEPEACHMSK1_BERRM DOEPEACHMSK1_BERRM_Msk // Bubble error interrupt mask +#define DOEPEACHMSK1_NAKM_Pos (13U) +#define DOEPEACHMSK1_NAKM_Msk (0x1UL << DOEPEACHMSK1_NAKM_Pos) // 0x00002000 +#define DOEPEACHMSK1_NAKM DOEPEACHMSK1_NAKM_Msk // NAK interrupt mask +#define DOEPEACHMSK1_NYETM_Pos (14U) +#define DOEPEACHMSK1_NYETM_Msk (0x1UL << DOEPEACHMSK1_NYETM_Pos) // 0x00004000 +#define DOEPEACHMSK1_NYETM DOEPEACHMSK1_NYETM_Msk // NYET interrupt mask + +/******************** Bit definition for HPTXFSIZ register ********************/ +#define HPTXFSIZ_PTXSA_Pos (0U) +#define HPTXFSIZ_PTXSA_Msk (0xFFFFUL << HPTXFSIZ_PTXSA_Pos) // 0x0000FFFF +#define HPTXFSIZ_PTXSA HPTXFSIZ_PTXSA_Msk // Host periodic TxFIFO start address +#define HPTXFSIZ_PTXFD_Pos (16U) +#define HPTXFSIZ_PTXFD_Msk (0xFFFFUL << HPTXFSIZ_PTXFD_Pos) // 0xFFFF0000 +#define HPTXFSIZ_PTXFD HPTXFSIZ_PTXFD_Msk // Host periodic TxFIFO depth + +/******************** Bit definition for DIEPCTL register ********************/ +#define DIEPCTL_MPSIZ_Pos (0U) +#define DIEPCTL_MPSIZ_Msk (0x7FFUL << DIEPCTL_MPSIZ_Pos) // 0x000007FF +#define DIEPCTL_MPSIZ DIEPCTL_MPSIZ_Msk // Maximum packet size +#define DIEPCTL_USBAEP_Pos (15U) +#define DIEPCTL_USBAEP_Msk (0x1UL << DIEPCTL_USBAEP_Pos) // 0x00008000 +#define DIEPCTL_USBAEP DIEPCTL_USBAEP_Msk // USB active endpoint +#define DIEPCTL_EONUM_DPID_Pos (16U) +#define DIEPCTL_EONUM_DPID_Msk (0x1UL << DIEPCTL_EONUM_DPID_Pos) // 0x00010000 +#define DIEPCTL_EONUM_DPID DIEPCTL_EONUM_DPID_Msk // Even/odd frame +#define DIEPCTL_NAKSTS_Pos (17U) +#define DIEPCTL_NAKSTS_Msk (0x1UL << DIEPCTL_NAKSTS_Pos) // 0x00020000 +#define DIEPCTL_NAKSTS DIEPCTL_NAKSTS_Msk // NAK status + +#define DIEPCTL_EPTYP_Pos (18U) +#define DIEPCTL_EPTYP_Msk (0x3UL << DIEPCTL_EPTYP_Pos) // 0x000C0000 +#define DIEPCTL_EPTYP DIEPCTL_EPTYP_Msk // Endpoint type +#define DIEPCTL_EPTYP_0 (0x1UL << DIEPCTL_EPTYP_Pos) // 0x00040000 +#define DIEPCTL_EPTYP_1 (0x2UL << DIEPCTL_EPTYP_Pos) // 0x00080000 +#define DIEPCTL_STALL_Pos (21U) +#define DIEPCTL_STALL_Msk (0x1UL << DIEPCTL_STALL_Pos) // 0x00200000 +#define DIEPCTL_STALL DIEPCTL_STALL_Msk // STALL handshake + +#define DIEPCTL_TXFNUM_Pos (22U) +#define DIEPCTL_TXFNUM_Msk (0xFUL << DIEPCTL_TXFNUM_Pos) // 0x03C00000 +#define DIEPCTL_TXFNUM DIEPCTL_TXFNUM_Msk // TxFIFO number +#define DIEPCTL_TXFNUM_0 (0x1UL << DIEPCTL_TXFNUM_Pos) // 0x00400000 +#define DIEPCTL_TXFNUM_1 (0x2UL << DIEPCTL_TXFNUM_Pos) // 0x00800000 +#define DIEPCTL_TXFNUM_2 (0x4UL << DIEPCTL_TXFNUM_Pos) // 0x01000000 +#define DIEPCTL_TXFNUM_3 (0x8UL << DIEPCTL_TXFNUM_Pos) // 0x02000000 +#define DIEPCTL_CNAK_Pos (26U) +#define DIEPCTL_CNAK_Msk (0x1UL << DIEPCTL_CNAK_Pos) // 0x04000000 +#define DIEPCTL_CNAK DIEPCTL_CNAK_Msk // Clear NAK +#define DIEPCTL_SNAK_Pos (27U) +#define DIEPCTL_SNAK_Msk (0x1UL << DIEPCTL_SNAK_Pos) // 0x08000000 +#define DIEPCTL_SNAK DIEPCTL_SNAK_Msk // Set NAK +#define DIEPCTL_SD0PID_SEVNFRM_Pos (28U) +#define DIEPCTL_SD0PID_SEVNFRM_Msk (0x1UL << DIEPCTL_SD0PID_SEVNFRM_Pos) // 0x10000000 +#define DIEPCTL_SD0PID_SEVNFRM DIEPCTL_SD0PID_SEVNFRM_Msk // Set DATA0 PID +#define DIEPCTL_SODDFRM_Pos (29U) +#define DIEPCTL_SODDFRM_Msk (0x1UL << DIEPCTL_SODDFRM_Pos) // 0x20000000 +#define DIEPCTL_SODDFRM DIEPCTL_SODDFRM_Msk // Set odd frame +#define DIEPCTL_EPDIS_Pos (30U) +#define DIEPCTL_EPDIS_Msk (0x1UL << DIEPCTL_EPDIS_Pos) // 0x40000000 +#define DIEPCTL_EPDIS DIEPCTL_EPDIS_Msk // Endpoint disable +#define DIEPCTL_EPENA_Pos (31U) +#define DIEPCTL_EPENA_Msk (0x1UL << DIEPCTL_EPENA_Pos) // 0x80000000 +#define DIEPCTL_EPENA DIEPCTL_EPENA_Msk // Endpoint enable + +/******************** Bit definition for HCCHAR register ********************/ +#define HCCHAR_MPSIZ_Pos (0U) +#define HCCHAR_MPSIZ_Msk (0x7FFUL << HCCHAR_MPSIZ_Pos) // 0x000007FF +#define HCCHAR_MPSIZ HCCHAR_MPSIZ_Msk // Maximum packet size + +#define HCCHAR_EPNUM_Pos (11U) +#define HCCHAR_EPNUM_Msk (0xFUL << HCCHAR_EPNUM_Pos) // 0x00007800 +#define HCCHAR_EPNUM HCCHAR_EPNUM_Msk // Endpoint number +#define HCCHAR_EPNUM_0 (0x1UL << HCCHAR_EPNUM_Pos) // 0x00000800 +#define HCCHAR_EPNUM_1 (0x2UL << HCCHAR_EPNUM_Pos) // 0x00001000 +#define HCCHAR_EPNUM_2 (0x4UL << HCCHAR_EPNUM_Pos) // 0x00002000 +#define HCCHAR_EPNUM_3 (0x8UL << HCCHAR_EPNUM_Pos) // 0x00004000 +#define HCCHAR_EPDIR_Pos (15U) +#define HCCHAR_EPDIR_Msk (0x1UL << HCCHAR_EPDIR_Pos) // 0x00008000 +#define HCCHAR_EPDIR HCCHAR_EPDIR_Msk // Endpoint direction +#define HCCHAR_LSDEV_Pos (17U) +#define HCCHAR_LSDEV_Msk (0x1UL << HCCHAR_LSDEV_Pos) // 0x00020000 +#define HCCHAR_LSDEV HCCHAR_LSDEV_Msk // Low-speed device + +#define HCCHAR_EPTYP_Pos (18U) +#define HCCHAR_EPTYP_Msk (0x3UL << HCCHAR_EPTYP_Pos) // 0x000C0000 +#define HCCHAR_EPTYP HCCHAR_EPTYP_Msk // Endpoint type +#define HCCHAR_EPTYP_0 (0x1UL << HCCHAR_EPTYP_Pos) // 0x00040000 +#define HCCHAR_EPTYP_1 (0x2UL << HCCHAR_EPTYP_Pos) // 0x00080000 + +#define HCCHAR_MC_Pos (20U) +#define HCCHAR_MC_Msk (0x3UL << HCCHAR_MC_Pos) // 0x00300000 +#define HCCHAR_MC HCCHAR_MC_Msk // Multi Count (MC) / Error Count (EC) +#define HCCHAR_MC_0 (0x1UL << HCCHAR_MC_Pos) // 0x00100000 +#define HCCHAR_MC_1 (0x2UL << HCCHAR_MC_Pos) // 0x00200000 + +#define HCCHAR_DAD_Pos (22U) +#define HCCHAR_DAD_Msk (0x7FUL << HCCHAR_DAD_Pos) // 0x1FC00000 +#define HCCHAR_DAD HCCHAR_DAD_Msk // Device address +#define HCCHAR_DAD_0 (0x01UL << HCCHAR_DAD_Pos) // 0x00400000 +#define HCCHAR_DAD_1 (0x02UL << HCCHAR_DAD_Pos) // 0x00800000 +#define HCCHAR_DAD_2 (0x04UL << HCCHAR_DAD_Pos) // 0x01000000 +#define HCCHAR_DAD_3 (0x08UL << HCCHAR_DAD_Pos) // 0x02000000 +#define HCCHAR_DAD_4 (0x10UL << HCCHAR_DAD_Pos) // 0x04000000 +#define HCCHAR_DAD_5 (0x20UL << HCCHAR_DAD_Pos) // 0x08000000 +#define HCCHAR_DAD_6 (0x40UL << HCCHAR_DAD_Pos) // 0x10000000 +#define HCCHAR_ODDFRM_Pos (29U) +#define HCCHAR_ODDFRM_Msk (0x1UL << HCCHAR_ODDFRM_Pos) // 0x20000000 +#define HCCHAR_ODDFRM HCCHAR_ODDFRM_Msk // Odd frame +#define HCCHAR_CHDIS_Pos (30U) +#define HCCHAR_CHDIS_Msk (0x1UL << HCCHAR_CHDIS_Pos) // 0x40000000 +#define HCCHAR_CHDIS HCCHAR_CHDIS_Msk // Channel disable +#define HCCHAR_CHENA_Pos (31U) +#define HCCHAR_CHENA_Msk (0x1UL << HCCHAR_CHENA_Pos) // 0x80000000 +#define HCCHAR_CHENA HCCHAR_CHENA_Msk // Channel enable + +/******************** Bit definition for HCSPLT register ********************/ + +#define HCSPLT_PRTADDR_Pos (0U) +#define HCSPLT_PRTADDR_Msk (0x7FUL << HCSPLT_PRTADDR_Pos) // 0x0000007F +#define HCSPLT_PRTADDR HCSPLT_PRTADDR_Msk // Port address +#define HCSPLT_PRTADDR_0 (0x01UL << HCSPLT_PRTADDR_Pos) // 0x00000001 +#define HCSPLT_PRTADDR_1 (0x02UL << HCSPLT_PRTADDR_Pos) // 0x00000002 +#define HCSPLT_PRTADDR_2 (0x04UL << HCSPLT_PRTADDR_Pos) // 0x00000004 +#define HCSPLT_PRTADDR_3 (0x08UL << HCSPLT_PRTADDR_Pos) // 0x00000008 +#define HCSPLT_PRTADDR_4 (0x10UL << HCSPLT_PRTADDR_Pos) // 0x00000010 +#define HCSPLT_PRTADDR_5 (0x20UL << HCSPLT_PRTADDR_Pos) // 0x00000020 +#define HCSPLT_PRTADDR_6 (0x40UL << HCSPLT_PRTADDR_Pos) // 0x00000040 + +#define HCSPLT_HUBADDR_Pos (7U) +#define HCSPLT_HUBADDR_Msk (0x7FUL << HCSPLT_HUBADDR_Pos) // 0x00003F80 +#define HCSPLT_HUBADDR HCSPLT_HUBADDR_Msk // Hub address +#define HCSPLT_HUBADDR_0 (0x01UL << HCSPLT_HUBADDR_Pos) // 0x00000080 +#define HCSPLT_HUBADDR_1 (0x02UL << HCSPLT_HUBADDR_Pos) // 0x00000100 +#define HCSPLT_HUBADDR_2 (0x04UL << HCSPLT_HUBADDR_Pos) // 0x00000200 +#define HCSPLT_HUBADDR_3 (0x08UL << HCSPLT_HUBADDR_Pos) // 0x00000400 +#define HCSPLT_HUBADDR_4 (0x10UL << HCSPLT_HUBADDR_Pos) // 0x00000800 +#define HCSPLT_HUBADDR_5 (0x20UL << HCSPLT_HUBADDR_Pos) // 0x00001000 +#define HCSPLT_HUBADDR_6 (0x40UL << HCSPLT_HUBADDR_Pos) // 0x00002000 + +#define HCSPLT_XACTPOS_Pos (14U) +#define HCSPLT_XACTPOS_Msk (0x3UL << HCSPLT_XACTPOS_Pos) // 0x0000C000 +#define HCSPLT_XACTPOS HCSPLT_XACTPOS_Msk // XACTPOS +#define HCSPLT_XACTPOS_0 (0x1UL << HCSPLT_XACTPOS_Pos) // 0x00004000 +#define HCSPLT_XACTPOS_1 (0x2UL << HCSPLT_XACTPOS_Pos) // 0x00008000 +#define HCSPLT_COMPLSPLT_Pos (16U) +#define HCSPLT_COMPLSPLT_Msk (0x1UL << HCSPLT_COMPLSPLT_Pos) // 0x00010000 +#define HCSPLT_COMPLSPLT HCSPLT_COMPLSPLT_Msk // Do complete split +#define HCSPLT_SPLITEN_Pos (31U) +#define HCSPLT_SPLITEN_Msk (0x1UL << HCSPLT_SPLITEN_Pos) // 0x80000000 +#define HCSPLT_SPLITEN HCSPLT_SPLITEN_Msk // Split enable + +/******************** Bit definition for HCINT register ********************/ +#define HCINT_XFRC_Pos (0U) +#define HCINT_XFRC_Msk (0x1UL << HCINT_XFRC_Pos) // 0x00000001 +#define HCINT_XFRC HCINT_XFRC_Msk // Transfer completed +#define HCINT_CHH_Pos (1U) +#define HCINT_CHH_Msk (0x1UL << HCINT_CHH_Pos) // 0x00000002 +#define HCINT_CHH HCINT_CHH_Msk // Channel halted +#define HCINT_AHBERR_Pos (2U) +#define HCINT_AHBERR_Msk (0x1UL << HCINT_AHBERR_Pos) // 0x00000004 +#define HCINT_AHBERR HCINT_AHBERR_Msk // AHB error +#define HCINT_STALL_Pos (3U) +#define HCINT_STALL_Msk (0x1UL << HCINT_STALL_Pos) // 0x00000008 +#define HCINT_STALL HCINT_STALL_Msk // STALL response received interrupt +#define HCINT_NAK_Pos (4U) +#define HCINT_NAK_Msk (0x1UL << HCINT_NAK_Pos) // 0x00000010 +#define HCINT_NAK HCINT_NAK_Msk // NAK response received interrupt +#define HCINT_ACK_Pos (5U) +#define HCINT_ACK_Msk (0x1UL << HCINT_ACK_Pos) // 0x00000020 +#define HCINT_ACK HCINT_ACK_Msk // ACK response received/transmitted interrupt +#define HCINT_NYET_Pos (6U) +#define HCINT_NYET_Msk (0x1UL << HCINT_NYET_Pos) // 0x00000040 +#define HCINT_NYET HCINT_NYET_Msk // Response received interrupt +#define HCINT_TXERR_Pos (7U) +#define HCINT_TXERR_Msk (0x1UL << HCINT_TXERR_Pos) // 0x00000080 +#define HCINT_TXERR HCINT_TXERR_Msk // Transaction error +#define HCINT_BBERR_Pos (8U) +#define HCINT_BBERR_Msk (0x1UL << HCINT_BBERR_Pos) // 0x00000100 +#define HCINT_BBERR HCINT_BBERR_Msk // Babble error +#define HCINT_FRMOR_Pos (9U) +#define HCINT_FRMOR_Msk (0x1UL << HCINT_FRMOR_Pos) // 0x00000200 +#define HCINT_FRMOR HCINT_FRMOR_Msk // Frame overrun +#define HCINT_DTERR_Pos (10U) +#define HCINT_DTERR_Msk (0x1UL << HCINT_DTERR_Pos) // 0x00000400 +#define HCINT_DTERR HCINT_DTERR_Msk // Data toggle error + +/******************** Bit definition for DIEPINT register ********************/ +#define DIEPINT_XFRC_Pos (0U) +#define DIEPINT_XFRC_Msk (0x1UL << DIEPINT_XFRC_Pos) // 0x00000001 +#define DIEPINT_XFRC DIEPINT_XFRC_Msk // Transfer completed interrupt +#define DIEPINT_EPDISD_Pos (1U) +#define DIEPINT_EPDISD_Msk (0x1UL << DIEPINT_EPDISD_Pos) // 0x00000002 +#define DIEPINT_EPDISD DIEPINT_EPDISD_Msk // Endpoint disabled interrupt +#define DIEPINT_AHBERR_Pos (2U) +#define DIEPINT_AHBERR_Msk (0x1UL << DIEPINT_AHBERR_Pos) // 0x00000004 +#define DIEPINT_AHBERR DIEPINT_AHBERR_Msk // AHB Error (AHBErr) during an IN transaction +#define DIEPINT_TOC_Pos (3U) +#define DIEPINT_TOC_Msk (0x1UL << DIEPINT_TOC_Pos) // 0x00000008 +#define DIEPINT_TOC DIEPINT_TOC_Msk // Timeout condition +#define DIEPINT_ITTXFE_Pos (4U) +#define DIEPINT_ITTXFE_Msk (0x1UL << DIEPINT_ITTXFE_Pos) // 0x00000010 +#define DIEPINT_ITTXFE DIEPINT_ITTXFE_Msk // IN token received when TxFIFO is empty +#define DIEPINT_INEPNM_Pos (5U) +#define DIEPINT_INEPNM_Msk (0x1UL << DIEPINT_INEPNM_Pos) // 0x00000020 +#define DIEPINT_INEPNM DIEPINT_INEPNM_Msk // IN token received with EP mismatch +#define DIEPINT_INEPNE_Pos (6U) +#define DIEPINT_INEPNE_Msk (0x1UL << DIEPINT_INEPNE_Pos) // 0x00000040 +#define DIEPINT_INEPNE DIEPINT_INEPNE_Msk // IN endpoint NAK effective +#define DIEPINT_TXFE_Pos (7U) +#define DIEPINT_TXFE_Msk (0x1UL << DIEPINT_TXFE_Pos) // 0x00000080 +#define DIEPINT_TXFE DIEPINT_TXFE_Msk // Transmit FIFO empty +#define DIEPINT_TXFIFOUDRN_Pos (8U) +#define DIEPINT_TXFIFOUDRN_Msk (0x1UL << DIEPINT_TXFIFOUDRN_Pos) // 0x00000100 +#define DIEPINT_TXFIFOUDRN DIEPINT_TXFIFOUDRN_Msk // Transmit Fifo Underrun +#define DIEPINT_BNA_Pos (9U) +#define DIEPINT_BNA_Msk (0x1UL << DIEPINT_BNA_Pos) // 0x00000200 +#define DIEPINT_BNA DIEPINT_BNA_Msk // Buffer not available interrupt +#define DIEPINT_PKTDRPSTS_Pos (11U) +#define DIEPINT_PKTDRPSTS_Msk (0x1UL << DIEPINT_PKTDRPSTS_Pos) // 0x00000800 +#define DIEPINT_PKTDRPSTS DIEPINT_PKTDRPSTS_Msk // Packet dropped status +#define DIEPINT_BERR_Pos (12U) +#define DIEPINT_BERR_Msk (0x1UL << DIEPINT_BERR_Pos) // 0x00001000 +#define DIEPINT_BERR DIEPINT_BERR_Msk // Babble error interrupt +#define DIEPINT_NAK_Pos (13U) +#define DIEPINT_NAK_Msk (0x1UL << DIEPINT_NAK_Pos) // 0x00002000 +#define DIEPINT_NAK DIEPINT_NAK_Msk // NAK interrupt + +/******************** Bit definition for HCINTMSK register ********************/ +#define HCINTMSK_XFRCM_Pos (0U) +#define HCINTMSK_XFRCM_Msk (0x1UL << HCINTMSK_XFRCM_Pos) // 0x00000001 +#define HCINTMSK_XFRCM HCINTMSK_XFRCM_Msk // Transfer completed mask +#define HCINTMSK_CHHM_Pos (1U) +#define HCINTMSK_CHHM_Msk (0x1UL << HCINTMSK_CHHM_Pos) // 0x00000002 +#define HCINTMSK_CHHM HCINTMSK_CHHM_Msk // Channel halted mask +#define HCINTMSK_AHBERR_Pos (2U) +#define HCINTMSK_AHBERR_Msk (0x1UL << HCINTMSK_AHBERR_Pos) // 0x00000004 +#define HCINTMSK_AHBERR HCINTMSK_AHBERR_Msk // AHB error +#define HCINTMSK_STALLM_Pos (3U) +#define HCINTMSK_STALLM_Msk (0x1UL << HCINTMSK_STALLM_Pos) // 0x00000008 +#define HCINTMSK_STALLM HCINTMSK_STALLM_Msk // STALL response received interrupt mask +#define HCINTMSK_NAKM_Pos (4U) +#define HCINTMSK_NAKM_Msk (0x1UL << HCINTMSK_NAKM_Pos) // 0x00000010 +#define HCINTMSK_NAKM HCINTMSK_NAKM_Msk // NAK response received interrupt mask +#define HCINTMSK_ACKM_Pos (5U) +#define HCINTMSK_ACKM_Msk (0x1UL << HCINTMSK_ACKM_Pos) // 0x00000020 +#define HCINTMSK_ACKM HCINTMSK_ACKM_Msk // ACK response received/transmitted interrupt mask +#define HCINTMSK_NYET_Pos (6U) +#define HCINTMSK_NYET_Msk (0x1UL << HCINTMSK_NYET_Pos) // 0x00000040 +#define HCINTMSK_NYET HCINTMSK_NYET_Msk // response received interrupt mask +#define HCINTMSK_TXERRM_Pos (7U) +#define HCINTMSK_TXERRM_Msk (0x1UL << HCINTMSK_TXERRM_Pos) // 0x00000080 +#define HCINTMSK_TXERRM HCINTMSK_TXERRM_Msk // Transaction error mask +#define HCINTMSK_BBERRM_Pos (8U) +#define HCINTMSK_BBERRM_Msk (0x1UL << HCINTMSK_BBERRM_Pos) // 0x00000100 +#define HCINTMSK_BBERRM HCINTMSK_BBERRM_Msk // Babble error mask +#define HCINTMSK_FRMORM_Pos (9U) +#define HCINTMSK_FRMORM_Msk (0x1UL << HCINTMSK_FRMORM_Pos) // 0x00000200 +#define HCINTMSK_FRMORM HCINTMSK_FRMORM_Msk // Frame overrun mask +#define HCINTMSK_DTERRM_Pos (10U) +#define HCINTMSK_DTERRM_Msk (0x1UL << HCINTMSK_DTERRM_Pos) // 0x00000400 +#define HCINTMSK_DTERRM HCINTMSK_DTERRM_Msk // Data toggle error mask + +/******************** Bit definition for DIEPTSIZ register ********************/ + +#define DIEPTSIZ_XFRSIZ_Pos (0U) +#define DIEPTSIZ_XFRSIZ_Msk (0x7FFFFUL << DIEPTSIZ_XFRSIZ_Pos) // 0x0007FFFF +#define DIEPTSIZ_XFRSIZ DIEPTSIZ_XFRSIZ_Msk // Transfer size +#define DIEPTSIZ_PKTCNT_Pos (19U) +#define DIEPTSIZ_PKTCNT_Msk (0x3FFUL << DIEPTSIZ_PKTCNT_Pos) // 0x1FF80000 +#define DIEPTSIZ_PKTCNT DIEPTSIZ_PKTCNT_Msk // Packet count +#define DIEPTSIZ_MULCNT_Pos (29U) +#define DIEPTSIZ_MULCNT_Msk (0x3UL << DIEPTSIZ_MULCNT_Pos) // 0x60000000 +#define DIEPTSIZ_MULCNT DIEPTSIZ_MULCNT_Msk // Packet count + /******************** Bit definition for HCTSIZ register ********************/ +#define HCTSIZ_XFRSIZ_Pos (0U) +#define HCTSIZ_XFRSIZ_Msk (0x7FFFFUL << HCTSIZ_XFRSIZ_Pos) // 0x0007FFFF +#define HCTSIZ_XFRSIZ HCTSIZ_XFRSIZ_Msk // Transfer size +#define HCTSIZ_PKTCNT_Pos (19U) +#define HCTSIZ_PKTCNT_Msk (0x3FFUL << HCTSIZ_PKTCNT_Pos) // 0x1FF80000 +#define HCTSIZ_PKTCNT HCTSIZ_PKTCNT_Msk // Packet count +#define HCTSIZ_DOPING_Pos (31U) +#define HCTSIZ_DOPING_Msk (0x1UL << HCTSIZ_DOPING_Pos) // 0x80000000 +#define HCTSIZ_DOPING HCTSIZ_DOPING_Msk // Do PING +#define HCTSIZ_DPID_Pos (29U) +#define HCTSIZ_DPID_Msk (0x3UL << HCTSIZ_DPID_Pos) // 0x60000000 +#define HCTSIZ_DPID HCTSIZ_DPID_Msk // Data PID +#define HCTSIZ_DPID_0 (0x1UL << HCTSIZ_DPID_Pos) // 0x20000000 +#define HCTSIZ_DPID_1 (0x2UL << HCTSIZ_DPID_Pos) // 0x40000000 + +/******************** Bit definition for DIEPDMA register ********************/ +#define DIEPDMA_DMAADDR_Pos (0U) +#define DIEPDMA_DMAADDR_Msk (0xFFFFFFFFUL << DIEPDMA_DMAADDR_Pos) // 0xFFFFFFFF +#define DIEPDMA_DMAADDR DIEPDMA_DMAADDR_Msk // DMA address + +/******************** Bit definition for HCDMA register ********************/ +#define HCDMA_DMAADDR_Pos (0U) +#define HCDMA_DMAADDR_Msk (0xFFFFFFFFUL << HCDMA_DMAADDR_Pos) // 0xFFFFFFFF +#define HCDMA_DMAADDR HCDMA_DMAADDR_Msk // DMA address + + /******************** Bit definition for DTXFSTS register ********************/ +#define DTXFSTS_INEPTFSAV_Pos (0U) +#define DTXFSTS_INEPTFSAV_Msk (0xFFFFUL << DTXFSTS_INEPTFSAV_Pos) // 0x0000FFFF +#define DTXFSTS_INEPTFSAV DTXFSTS_INEPTFSAV_Msk // IN endpoint TxFIFO space available + + /******************** Bit definition for DIEPTXF register ********************/ +#define DIEPTXF_INEPTXSA_Pos (0U) +#define DIEPTXF_INEPTXSA_Msk (0xFFFFUL << DIEPTXF_INEPTXSA_Pos) // 0x0000FFFF +#define DIEPTXF_INEPTXSA DIEPTXF_INEPTXSA_Msk // IN endpoint FIFOx transmit RAM start address +#define DIEPTXF_INEPTXFD_Pos (16U) +#define DIEPTXF_INEPTXFD_Msk (0xFFFFUL << DIEPTXF_INEPTXFD_Pos) // 0xFFFF0000 +#define DIEPTXF_INEPTXFD DIEPTXF_INEPTXFD_Msk // IN endpoint TxFIFO depth + +/******************** Bit definition for DOEPCTL register ********************/ +#define DOEPCTL_MPSIZ_Pos (0U) +#define DOEPCTL_MPSIZ_Msk (0x7FFUL << DOEPCTL_MPSIZ_Pos) // 0x000007FF +#define DOEPCTL_MPSIZ DOEPCTL_MPSIZ_Msk // Maximum packet size //Bit 1 +#define DOEPCTL_USBAEP_Pos (15U) +#define DOEPCTL_USBAEP_Msk (0x1UL << DOEPCTL_USBAEP_Pos) // 0x00008000 +#define DOEPCTL_USBAEP DOEPCTL_USBAEP_Msk // USB active endpoint +#define DOEPCTL_NAKSTS_Pos (17U) +#define DOEPCTL_NAKSTS_Msk (0x1UL << DOEPCTL_NAKSTS_Pos) // 0x00020000 +#define DOEPCTL_NAKSTS DOEPCTL_NAKSTS_Msk // NAK status +#define DOEPCTL_SD0PID_SEVNFRM_Pos (28U) +#define DOEPCTL_SD0PID_SEVNFRM_Msk (0x1UL << DOEPCTL_SD0PID_SEVNFRM_Pos) // 0x10000000 +#define DOEPCTL_SD0PID_SEVNFRM DOEPCTL_SD0PID_SEVNFRM_Msk // Set DATA0 PID +#define DOEPCTL_SODDFRM_Pos (29U) +#define DOEPCTL_SODDFRM_Msk (0x1UL << DOEPCTL_SODDFRM_Pos) // 0x20000000 +#define DOEPCTL_SODDFRM DOEPCTL_SODDFRM_Msk // Set odd frame +#define DOEPCTL_EPTYP_Pos (18U) +#define DOEPCTL_EPTYP_Msk (0x3UL << DOEPCTL_EPTYP_Pos) // 0x000C0000 +#define DOEPCTL_EPTYP DOEPCTL_EPTYP_Msk // Endpoint type +#define DOEPCTL_EPTYP_0 (0x1UL << DOEPCTL_EPTYP_Pos) // 0x00040000 +#define DOEPCTL_EPTYP_1 (0x2UL << DOEPCTL_EPTYP_Pos) // 0x00080000 +#define DOEPCTL_SNPM_Pos (20U) +#define DOEPCTL_SNPM_Msk (0x1UL << DOEPCTL_SNPM_Pos) // 0x00100000 +#define DOEPCTL_SNPM DOEPCTL_SNPM_Msk // Snoop mode +#define DOEPCTL_STALL_Pos (21U) +#define DOEPCTL_STALL_Msk (0x1UL << DOEPCTL_STALL_Pos) // 0x00200000 +#define DOEPCTL_STALL DOEPCTL_STALL_Msk // STALL handshake +#define DOEPCTL_CNAK_Pos (26U) +#define DOEPCTL_CNAK_Msk (0x1UL << DOEPCTL_CNAK_Pos) // 0x04000000 +#define DOEPCTL_CNAK DOEPCTL_CNAK_Msk // Clear NAK +#define DOEPCTL_SNAK_Pos (27U) +#define DOEPCTL_SNAK_Msk (0x1UL << DOEPCTL_SNAK_Pos) // 0x08000000 +#define DOEPCTL_SNAK DOEPCTL_SNAK_Msk // Set NAK +#define DOEPCTL_EPDIS_Pos (30U) +#define DOEPCTL_EPDIS_Msk (0x1UL << DOEPCTL_EPDIS_Pos) // 0x40000000 +#define DOEPCTL_EPDIS DOEPCTL_EPDIS_Msk // Endpoint disable +#define DOEPCTL_EPENA_Pos (31U) +#define DOEPCTL_EPENA_Msk (0x1UL << DOEPCTL_EPENA_Pos) // 0x80000000 +#define DOEPCTL_EPENA DOEPCTL_EPENA_Msk // Endpoint enable + +/******************** Bit definition for DOEPINT register ********************/ +#define DOEPINT_XFRC_Pos (0U) +#define DOEPINT_XFRC_Msk (0x1UL << DOEPINT_XFRC_Pos) // 0x00000001 +#define DOEPINT_XFRC DOEPINT_XFRC_Msk // Transfer completed interrupt +#define DOEPINT_EPDISD_Pos (1U) +#define DOEPINT_EPDISD_Msk (0x1UL << DOEPINT_EPDISD_Pos) // 0x00000002 +#define DOEPINT_EPDISD DOEPINT_EPDISD_Msk // Endpoint disabled interrupt +#define DOEPINT_AHBERR_Pos (2U) +#define DOEPINT_AHBERR_Msk (0x1UL << DOEPINT_AHBERR_Pos) // 0x00000004 +#define DOEPINT_AHBERR DOEPINT_AHBERR_Msk // AHB Error (AHBErr) during an OUT transaction +#define DOEPINT_STUP_Pos (3U) +#define DOEPINT_STUP_Msk (0x1UL << DOEPINT_STUP_Pos) // 0x00000008 +#define DOEPINT_STUP DOEPINT_STUP_Msk // SETUP phase done +#define DOEPINT_OTEPDIS_Pos (4U) +#define DOEPINT_OTEPDIS_Msk (0x1UL << DOEPINT_OTEPDIS_Pos) // 0x00000010 +#define DOEPINT_OTEPDIS DOEPINT_OTEPDIS_Msk // OUT token received when endpoint disabled +#define DOEPINT_OTEPSPR_Pos (5U) +#define DOEPINT_OTEPSPR_Msk (0x1UL << DOEPINT_OTEPSPR_Pos) // 0x00000020 +#define DOEPINT_OTEPSPR DOEPINT_OTEPSPR_Msk // Status Phase Received For Control Write +#define DOEPINT_B2BSTUP_Pos (6U) +#define DOEPINT_B2BSTUP_Msk (0x1UL << DOEPINT_B2BSTUP_Pos) // 0x00000040 +#define DOEPINT_B2BSTUP DOEPINT_B2BSTUP_Msk // Back-to-back SETUP packets received +#define DOEPINT_OUTPKTERR_Pos (8U) +#define DOEPINT_OUTPKTERR_Msk (0x1UL << DOEPINT_OUTPKTERR_Pos) // 0x00000100 +#define DOEPINT_OUTPKTERR DOEPINT_OUTPKTERR_Msk // OUT packet error +#define DOEPINT_NAK_Pos (13U) +#define DOEPINT_NAK_Msk (0x1UL << DOEPINT_NAK_Pos) // 0x00002000 +#define DOEPINT_NAK DOEPINT_NAK_Msk // NAK Packet is transmitted by the device +#define DOEPINT_NYET_Pos (14U) +#define DOEPINT_NYET_Msk (0x1UL << DOEPINT_NYET_Pos) // 0x00004000 +#define DOEPINT_NYET DOEPINT_NYET_Msk // NYET interrupt +#define DOEPINT_STPKTRX_Pos (15U) +#define DOEPINT_STPKTRX_Msk (0x1UL << DOEPINT_STPKTRX_Pos) // 0x00008000 +#define DOEPINT_STPKTRX DOEPINT_STPKTRX_Msk // Setup Packet Received + +/******************** Bit definition for DOEPTSIZ register ********************/ +#define DOEPTSIZ_XFRSIZ_Pos (0U) +#define DOEPTSIZ_XFRSIZ_Msk (0x7FFFFUL << DOEPTSIZ_XFRSIZ_Pos) // 0x0007FFFF +#define DOEPTSIZ_XFRSIZ DOEPTSIZ_XFRSIZ_Msk // Transfer size +#define DOEPTSIZ_PKTCNT_Pos (19U) +#define DOEPTSIZ_PKTCNT_Msk (0x3FFUL << DOEPTSIZ_PKTCNT_Pos) // 0x1FF80000 +#define DOEPTSIZ_PKTCNT DOEPTSIZ_PKTCNT_Msk // Packet count + +#define DOEPTSIZ_STUPCNT_Pos (29U) +#define DOEPTSIZ_STUPCNT_Msk (0x3UL << DOEPTSIZ_STUPCNT_Pos) // 0x60000000 +#define DOEPTSIZ_STUPCNT DOEPTSIZ_STUPCNT_Msk // SETUP packet count +#define DOEPTSIZ_STUPCNT_0 (0x1UL << DOEPTSIZ_STUPCNT_Pos) // 0x20000000 +#define DOEPTSIZ_STUPCNT_1 (0x2UL << DOEPTSIZ_STUPCNT_Pos) // 0x40000000 + +/******************** Bit definition for PCGCTL register ********************/ +#define PCGCTL_IF_DEV_MODE TU_BIT(31) +#define PCGCTL_P2HD_PRT_SPD_MASK (0x3ul << 29) +#define PCGCTL_P2HD_PRT_SPD_SHIFT 29 +#define PCGCTL_P2HD_DEV_ENUM_SPD_MASK (0x3ul << 27) +#define PCGCTL_P2HD_DEV_ENUM_SPD_SHIFT 27 +#define PCGCTL_MAC_DEV_ADDR_MASK (0x7ful << 20) +#define PCGCTL_MAC_DEV_ADDR_SHIFT 20 +#define PCGCTL_MAX_TERMSEL TU_BIT(19) +#define PCGCTL_MAX_XCVRSELECT_MASK (0x3ul << 17) +#define PCGCTL_MAX_XCVRSELECT_SHIFT 17 +#define PCGCTL_PORT_POWER TU_BIT(16) +#define PCGCTL_PRT_CLK_SEL_MASK (0x3ul << 14) +#define PCGCTL_PRT_CLK_SEL_SHIFT 14 +#define PCGCTL_ESS_REG_RESTORED TU_BIT(13) +#define PCGCTL_EXTND_HIBER_SWITCH TU_BIT(12) +#define PCGCTL_EXTND_HIBER_PWRCLMP TU_BIT(11) +#define PCGCTL_ENBL_EXTND_HIBER TU_BIT(10) +#define PCGCTL_RESTOREMODE TU_BIT(9) +#define PCGCTL_RESETAFTSUSP TU_BIT(8) +#define PCGCTL_DEEP_SLEEP TU_BIT(7) +#define PCGCTL_PHY_IN_SLEEP TU_BIT(6) +#define PCGCTL_ENBL_SLEEP_GATING TU_BIT(5) +#define PCGCTL_RSTPDWNMODULE TU_BIT(3) +#define PCGCTL_PWRCLMP TU_BIT(2) +#define PCGCTL_GATEHCLK TU_BIT(1) +#define PCGCTL_STOPPCLK TU_BIT(0) + +#define PCGCTL1_TIMER (0x3ul << 1) +#define PCGCTL1_GATEEN TU_BIT(0) + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_xmc.h b/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_xmc.h new file mode 100644 index 00000000..63419abf --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/portable/synopsys/dwc2/dwc2_xmc.h @@ -0,0 +1,88 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 Rafael Silva (@perigoso) + * Copyright (c) 2021, Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _DWC2_XMC_H_ +#define _DWC2_XMC_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include "xmc_device.h" + +#define DWC2_EP_MAX 7 + +static const dwc2_controller_t _dwc2_controller[] = +{ + // Note: XMC has some custom control registers before DWC registers + { .reg_base = USB0_BASE, .irqnum = USB0_0_IRQn, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 2048 } +}; + +TU_ATTR_ALWAYS_INLINE +static inline void dwc2_dcd_int_enable(uint8_t rhport) +{ + NVIC_EnableIRQ(_dwc2_controller[rhport].irqnum); +} + +TU_ATTR_ALWAYS_INLINE +static inline void dwc2_dcd_int_disable (uint8_t rhport) +{ + NVIC_DisableIRQ(_dwc2_controller[rhport].irqnum); +} + +static inline void dwc2_remote_wakeup_delay(void) +{ + // try to delay for 1 ms +// uint32_t count = SystemCoreClock / 1000; +// while ( count-- ) __NOP(); +} + +// MCU specific PHY init, called BEFORE core reset +static inline void dwc2_phy_init(dwc2_regs_t * dwc2, uint8_t hs_phy_type) +{ + (void) dwc2; + (void) hs_phy_type; + + // Enable PHY + //USB->ROUTE = USB_ROUTE_PHYPEN; +} + +// MCU specific PHY update, it is called AFTER init() and core reset +static inline void dwc2_phy_update(dwc2_regs_t * dwc2, uint8_t hs_phy_type) +{ + (void) dwc2; + (void) hs_phy_type; + + // XMC Manual: turn around must be 5 (reset & default value) + // dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_TRDT_Msk) | (5u << GUSBCFG_TRDT_Pos); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test-devices/loopback-stm32/lib/tinyusb/tusb.c b/test-devices/loopback-stm32/lib/tinyusb/tusb.c new file mode 100644 index 00000000..0092267a --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/tusb.c @@ -0,0 +1,457 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if CFG_TUH_ENABLED || CFG_TUD_ENABLED + +#include "tusb.h" +#include "common/tusb_private.h" + +#if CFG_TUD_ENABLED +#include "device/usbd_pvt.h" +#endif + +#if CFG_TUH_ENABLED +#include "host/usbh_pvt.h" +#endif + +//--------------------------------------------------------------------+ +// Public API +//--------------------------------------------------------------------+ + +bool tusb_init(void) { + #if CFG_TUD_ENABLED && defined(TUD_OPT_RHPORT) + // init device stack CFG_TUSB_RHPORTx_MODE must be defined + TU_ASSERT ( tud_init(TUD_OPT_RHPORT) ); + #endif + + #if CFG_TUH_ENABLED && defined(TUH_OPT_RHPORT) + // init host stack CFG_TUSB_RHPORTx_MODE must be defined + TU_ASSERT( tuh_init(TUH_OPT_RHPORT) ); + #endif + + return true; +} + +bool tusb_inited(void) { + bool ret = false; + + #if CFG_TUD_ENABLED + ret = ret || tud_inited(); + #endif + + #if CFG_TUH_ENABLED + ret = ret || tuh_inited(); + #endif + + return ret; +} + +//--------------------------------------------------------------------+ +// Descriptor helper +//--------------------------------------------------------------------+ + +uint8_t const* tu_desc_find(uint8_t const* desc, uint8_t const* end, uint8_t byte1) { + while (desc + 1 < end) { + if (desc[1] == byte1) return desc; + desc += desc[DESC_OFFSET_LEN]; + } + return NULL; +} + +uint8_t const* tu_desc_find2(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2) { + while (desc + 2 < end) { + if (desc[1] == byte1 && desc[2] == byte2) return desc; + desc += desc[DESC_OFFSET_LEN]; + } + return NULL; +} + +uint8_t const* tu_desc_find3(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2, uint8_t byte3) { + while (desc + 3 < end) { + if (desc[1] == byte1 && desc[2] == byte2 && desc[3] == byte3) return desc; + desc += desc[DESC_OFFSET_LEN]; + } + return NULL; +} + +//--------------------------------------------------------------------+ +// Endpoint Helper for both Host and Device stack +//--------------------------------------------------------------------+ + +bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex) { + (void) mutex; + + // pre-check to help reducing mutex lock + TU_VERIFY((ep_state->busy == 0) && (ep_state->claimed == 0)); + (void) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER); + + // can only claim the endpoint if it is not busy and not claimed yet. + bool const available = (ep_state->busy == 0) && (ep_state->claimed == 0); + if (available) { + ep_state->claimed = 1; + } + + (void) osal_mutex_unlock(mutex); + return available; +} + +bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex) { + (void) mutex; + (void) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER); + + // can only release the endpoint if it is claimed and not busy + bool const ret = (ep_state->claimed == 1) && (ep_state->busy == 0); + if (ret) { + ep_state->claimed = 0; + } + + (void) osal_mutex_unlock(mutex); + return ret; +} + +bool tu_edpt_validate(tusb_desc_endpoint_t const* desc_ep, tusb_speed_t speed) { + uint16_t const max_packet_size = tu_edpt_packet_size(desc_ep); + TU_LOG2(" Open EP %02X with Size = %u\r\n", desc_ep->bEndpointAddress, max_packet_size); + + switch (desc_ep->bmAttributes.xfer) { + case TUSB_XFER_ISOCHRONOUS: { + uint16_t const spec_size = (speed == TUSB_SPEED_HIGH ? 1024 : 1023); + TU_ASSERT(max_packet_size <= spec_size); + break; + } + + case TUSB_XFER_BULK: + if (speed == TUSB_SPEED_HIGH) { + // Bulk highspeed must be EXACTLY 512 + TU_ASSERT(max_packet_size == 512); + } else { + // TODO Bulk fullspeed can only be 8, 16, 32, 64 + TU_ASSERT(max_packet_size <= 64); + } + break; + + case TUSB_XFER_INTERRUPT: { + uint16_t const spec_size = (speed == TUSB_SPEED_HIGH ? 1024 : 64); + TU_ASSERT(max_packet_size <= spec_size); + break; + } + + default: + return false; + } + + return true; +} + +void tu_edpt_bind_driver(uint8_t ep2drv[][2], tusb_desc_interface_t const* desc_itf, uint16_t desc_len, + uint8_t driver_id) { + uint8_t const* p_desc = (uint8_t const*) desc_itf; + uint8_t const* desc_end = p_desc + desc_len; + + while (p_desc < desc_end) { + if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) { + uint8_t const ep_addr = ((tusb_desc_endpoint_t const*) p_desc)->bEndpointAddress; + TU_LOG(2, " Bind EP %02x to driver id %u\r\n", ep_addr, driver_id); + ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)] = driver_id; + } + p_desc = tu_desc_next(p_desc); + } +} + +uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const* desc_itf, uint8_t itf_count, uint16_t max_len) { + uint8_t const* p_desc = (uint8_t const*) desc_itf; + uint16_t len = 0; + + while (itf_count--) { + // Next on interface desc + len += tu_desc_len(desc_itf); + p_desc = tu_desc_next(p_desc); + + while (len < max_len) { + // return on IAD regardless of itf count + if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE_ASSOCIATION) { + return len; + } + if ((tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) && + ((tusb_desc_interface_t const*) p_desc)->bAlternateSetting == 0) { + break; + } + + len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } + } + + return len; +} + +//--------------------------------------------------------------------+ +// Endpoint Stream Helper for both Host and Device stack +//--------------------------------------------------------------------+ + +bool tu_edpt_stream_init(tu_edpt_stream_t* s, bool is_host, bool is_tx, bool overwritable, + void* ff_buf, uint16_t ff_bufsize, uint8_t* ep_buf, uint16_t ep_bufsize) { + osal_mutex_t new_mutex = osal_mutex_create(&s->ff_mutexdef); + (void) new_mutex; + (void) is_tx; + + s->is_host = is_host; + tu_fifo_config(&s->ff, ff_buf, ff_bufsize, 1, overwritable); + tu_fifo_config_mutex(&s->ff, is_tx ? new_mutex : NULL, is_tx ? NULL : new_mutex); + + s->ep_buf = ep_buf; + s->ep_bufsize = ep_bufsize; + + return true; +} + +bool tu_edpt_stream_deinit(tu_edpt_stream_t* s) { + (void) s; + #if OSAL_MUTEX_REQUIRED + if (s->ff.mutex_wr) osal_mutex_delete(s->ff.mutex_wr); + if (s->ff.mutex_rd) osal_mutex_delete(s->ff.mutex_rd); + #endif + return true; +} + +TU_ATTR_ALWAYS_INLINE static inline +bool stream_claim(tu_edpt_stream_t* s) { + if (s->is_host) { + #if CFG_TUH_ENABLED + return usbh_edpt_claim(s->daddr, s->ep_addr); + #endif + } else { + #if CFG_TUD_ENABLED + return usbd_edpt_claim(s->rhport, s->ep_addr); + #endif + } + return false; +} + +TU_ATTR_ALWAYS_INLINE static inline +bool stream_xfer(tu_edpt_stream_t* s, uint16_t count) { + if (s->is_host) { + #if CFG_TUH_ENABLED + return usbh_edpt_xfer(s->daddr, s->ep_addr, count ? s->ep_buf : NULL, count); + #endif + } else { + #if CFG_TUD_ENABLED + return usbd_edpt_xfer(s->rhport, s->ep_addr, count ? s->ep_buf : NULL, count); + #endif + } + return false; +} + +TU_ATTR_ALWAYS_INLINE static inline +bool stream_release(tu_edpt_stream_t* s) { + if (s->is_host) { + #if CFG_TUH_ENABLED + return usbh_edpt_release(s->daddr, s->ep_addr); + #endif + } else { + #if CFG_TUD_ENABLED + return usbd_edpt_release(s->rhport, s->ep_addr); + #endif + } + return false; +} + +//--------------------------------------------------------------------+ +// Stream Write +//--------------------------------------------------------------------+ +bool tu_edpt_stream_write_zlp_if_needed(tu_edpt_stream_t* s, uint32_t last_xferred_bytes) { + // ZLP condition: no pending data, last transferred bytes is multiple of packet size + TU_VERIFY(!tu_fifo_count(&s->ff) && last_xferred_bytes && (0 == (last_xferred_bytes & (s->ep_packetsize - 1)))); + TU_VERIFY(stream_claim(s)); + TU_ASSERT(stream_xfer(s, 0)); + return true; +} + +uint32_t tu_edpt_stream_write_xfer(tu_edpt_stream_t* s) { + // skip if no data + TU_VERIFY(tu_fifo_count(&s->ff), 0); + + // Claim the endpoint + TU_VERIFY(stream_claim(s), 0); + + // Pull data from FIFO -> EP buf + uint16_t const count = tu_fifo_read_n(&s->ff, s->ep_buf, s->ep_bufsize); + + if (count) { + TU_ASSERT(stream_xfer(s, count), 0); + return count; + } else { + // Release endpoint since we don't make any transfer + // Note: data is dropped if terminal is not connected + stream_release(s); + return 0; + } +} + +uint32_t tu_edpt_stream_write(tu_edpt_stream_t* s, void const* buffer, uint32_t bufsize) { + TU_VERIFY(bufsize); // TODO support ZLP + uint16_t ret = tu_fifo_write_n(&s->ff, buffer, (uint16_t) bufsize); + + // flush if fifo has more than packet size or + // in rare case: fifo depth is configured too small (which never reach packet size) + if ((tu_fifo_count(&s->ff) >= s->ep_packetsize) || (tu_fifo_depth(&s->ff) < s->ep_packetsize)) { + tu_edpt_stream_write_xfer(s); + } + + return ret; +} + +//--------------------------------------------------------------------+ +// Stream Read +//--------------------------------------------------------------------+ +uint32_t tu_edpt_stream_read_xfer(tu_edpt_stream_t* s) { + uint16_t available = tu_fifo_remaining(&s->ff); + + // Prepare for incoming data but only allow what we can store in the ring buffer. + // TODO Actually we can still carry out the transfer, keeping count of received bytes + // and slowly move it to the FIFO when read(). + // This pre-check reduces endpoint claiming + TU_VERIFY(available >= s->ep_packetsize); + + // claim endpoint + TU_VERIFY(stream_claim(s), 0); + + // get available again since fifo can be changed before endpoint is claimed + available = tu_fifo_remaining(&s->ff); + + if (available >= s->ep_packetsize) { + // multiple of packet size limit by ep bufsize + uint16_t count = (uint16_t) (available & ~(s->ep_packetsize - 1)); + count = tu_min16(count, s->ep_bufsize); + + TU_ASSERT(stream_xfer(s, count), 0); + return count; + } else { + // Release endpoint since we don't make any transfer + stream_release(s); + return 0; + } +} + +uint32_t tu_edpt_stream_read(tu_edpt_stream_t* s, void* buffer, uint32_t bufsize) { + uint32_t num_read = tu_fifo_read_n(&s->ff, buffer, (uint16_t) bufsize); + tu_edpt_stream_read_xfer(s); + return num_read; +} + +//--------------------------------------------------------------------+ +// Debug +//--------------------------------------------------------------------+ + +#if CFG_TUSB_DEBUG +#include + +#if CFG_TUSB_DEBUG >= CFG_TUH_LOG_LEVEL || CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL +char const* const tu_str_speed[] = {"Full", "Low", "High"}; +char const* const tu_str_std_request[] = { + "Get Status", + "Clear Feature", + "Reserved", + "Set Feature", + "Reserved", + "Set Address", + "Get Descriptor", + "Set Descriptor", + "Get Configuration", + "Set Configuration", + "Get Interface", + "Set Interface", + "Synch Frame" +}; + +char const* const tu_str_xfer_result[] = { + "OK", "FAILED", "STALLED", "TIMEOUT" +}; +#endif + +static void dump_str_line(uint8_t const* buf, uint16_t count) { + tu_printf(" |"); + // each line is 16 bytes + for (uint16_t i = 0; i < count; i++) { + const char ch = buf[i]; + tu_printf("%c", isprint(ch) ? ch : '.'); + } + tu_printf("|\r\n"); +} + +/* Print out memory contents + * - buf : buffer + * - count : number of item + * - indent: prefix spaces on every line + */ +void tu_print_mem(void const* buf, uint32_t count, uint8_t indent) { + uint8_t const size = 1; // fixed 1 byte for now + if (!buf || !count) { + tu_printf("NULL\r\n"); + return; + } + + uint8_t const* buf8 = (uint8_t const*) buf; + char format[] = "%00X"; + format[2] += (uint8_t) (2 * size); // 1 byte = 2 hex digits + const uint8_t item_per_line = 16 / size; + + for (unsigned int i = 0; i < count; i++) { + unsigned int value = 0; + + if (i % item_per_line == 0) { + // Print Ascii + if (i != 0) dump_str_line(buf8 - 16, 16); + for (uint8_t s = 0; s < indent; s++) tu_printf(" "); + // print offset or absolute address + tu_printf("%04X: ", 16 * i / item_per_line); + } + + tu_memcpy_s(&value, sizeof(value), buf8, size); + buf8 += size; + + tu_printf(" "); + tu_printf(format, value); + } + + // fill up last row to 16 for printing ascii + const uint32_t remain = count % 16; + uint8_t nback = (uint8_t) (remain ? remain : 16); + if (remain) { + for (uint32_t i = 0; i < 16 - remain; i++) { + tu_printf(" "); + for (int j = 0; j < 2 * size; j++) tu_printf(" "); + } + } + + dump_str_line(buf8 - nback, nback); +} + +#endif + +#endif // host or device enabled diff --git a/test-devices/loopback-stm32/lib/tinyusb/tusb.h b/test-devices/loopback-stm32/lib/tinyusb/tusb.h new file mode 100644 index 00000000..4f69a141 --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/tusb.h @@ -0,0 +1,148 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_H_ +#define _TUSB_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// INCLUDE +//--------------------------------------------------------------------+ +#include "common/tusb_common.h" +#include "osal/osal.h" +#include "common/tusb_fifo.h" + +//------------- TypeC -------------// +#if CFG_TUC_ENABLED + #include "typec/usbc.h" +#endif + +//------------- HOST -------------// +#if CFG_TUH_ENABLED + #include "host/usbh.h" + + #if CFG_TUH_HID + #include "class/hid/hid_host.h" + #endif + + #if CFG_TUH_MSC + #include "class/msc/msc_host.h" + #endif + + #if CFG_TUH_CDC + #include "class/cdc/cdc_host.h" + #endif + + #if CFG_TUH_VENDOR + #include "class/vendor/vendor_host.h" + #endif +#else + #ifndef tuh_int_handler + #define tuh_int_handler(...) + #endif +#endif + +//------------- DEVICE -------------// +#if CFG_TUD_ENABLED + #include "device/usbd.h" + + #if CFG_TUD_HID + #include "class/hid/hid_device.h" + #endif + + #if CFG_TUD_CDC + #include "class/cdc/cdc_device.h" + #endif + + #if CFG_TUD_MSC + #include "class/msc/msc_device.h" + #endif + + #if CFG_TUD_AUDIO + #include "class/audio/audio_device.h" + #endif + + #if CFG_TUD_VIDEO + #include "class/video/video_device.h" + #endif + + #if CFG_TUD_MIDI + #include "class/midi/midi_device.h" + #endif + + #if CFG_TUD_VENDOR + #include "class/vendor/vendor_device.h" + #endif + + #if CFG_TUD_USBTMC + #include "class/usbtmc/usbtmc_device.h" + #endif + + #if CFG_TUD_DFU_RUNTIME + #include "class/dfu/dfu_rt_device.h" + #endif + + #if CFG_TUD_DFU + #include "class/dfu/dfu_device.h" + #endif + + #if CFG_TUD_ECM_RNDIS || CFG_TUD_NCM + #include "class/net/net_device.h" + #endif + + #if CFG_TUD_BTH + #include "class/bth/bth_device.h" + #endif +#else + #ifndef tud_int_handler + #define tud_int_handler(...) + #endif +#endif + + +//--------------------------------------------------------------------+ +// APPLICATION API +//--------------------------------------------------------------------+ + +// Initialize device/host stack +// Note: when using with RTOS, this should be called after scheduler/kernel is started. +// Otherwise it could cause kernel issue since USB IRQ handler does use RTOS queue API. +bool tusb_init(void); + +// Check if stack is initialized +bool tusb_inited(void); + +// TODO +// bool tusb_teardown(void); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_H_ */ diff --git a/test-devices/loopback-stm32/lib/tinyusb/tusb_option.h b/test-devices/loopback-stm32/lib/tinyusb/tusb_option.h new file mode 100644 index 00000000..3ead20ee --- /dev/null +++ b/test-devices/loopback-stm32/lib/tinyusb/tusb_option.h @@ -0,0 +1,558 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_OPTION_H_ +#define _TUSB_OPTION_H_ + +#include "common/tusb_compiler.h" + +// Version is release as major.minor.revision eg 1.0.0. though there could be notable APIs before a new release. +// For notable API changes within a release, we increase the build number. +#define TUSB_VERSION_MAJOR 0 +#define TUSB_VERSION_MINOR 16 +#define TUSB_VERSION_REVISION 0 +#define TUSB_VERSION_BUILD 3 + +#define TUSB_VERSION_NUMBER (TUSB_VERSION_MAJOR << 24 | TUSB_VERSION_MINOR << 16 | TUSB_VERSION_REVISION << 8 | TUSB_VERSION_BUILD) +#define TUSB_VERSION_STRING TU_STRING(TUSB_VERSION_MAJOR) "." TU_STRING(TUSB_VERSION_MINOR) "." TU_STRING(TUSB_VERSION_REVISION) + +//--------------------------------------------------------------------+ +// Supported MCUs +// CFG_TUSB_MCU must be defined to one of following value +//--------------------------------------------------------------------+ + +#define OPT_MCU_NONE 0 + +// LPC +#define OPT_MCU_LPC11UXX 1 ///< NXP LPC11Uxx +#define OPT_MCU_LPC13XX 2 ///< NXP LPC13xx +#define OPT_MCU_LPC15XX 3 ///< NXP LPC15xx +#define OPT_MCU_LPC175X_6X 4 ///< NXP LPC175x, LPC176x +#define OPT_MCU_LPC177X_8X 5 ///< NXP LPC177x, LPC178x +#define OPT_MCU_LPC18XX 6 ///< NXP LPC18xx +#define OPT_MCU_LPC40XX 7 ///< NXP LPC40xx +#define OPT_MCU_LPC43XX 8 ///< NXP LPC43xx +#define OPT_MCU_LPC51UXX 9 ///< NXP LPC51U6x +#define OPT_MCU_LPC54 10 ///< NXP LPC54 +#define OPT_MCU_LPC55 11 ///< NXP LPC55 +// legacy naming +#define OPT_MCU_LPC54XXX OPT_MCU_LPC54 +#define OPT_MCU_LPC55XX OPT_MCU_LPC55 + +// NRF +#define OPT_MCU_NRF5X 100 ///< Nordic nRF5x series + +// SAM +#define OPT_MCU_SAMD21 200 ///< MicroChip SAMD21 +#define OPT_MCU_SAMD51 201 ///< MicroChip SAMD51 +#define OPT_MCU_SAMG 202 ///< MicroChip SAMDG series +#define OPT_MCU_SAME5X 203 ///< MicroChip SAM E5x +#define OPT_MCU_SAMD11 204 ///< MicroChip SAMD11 +#define OPT_MCU_SAML22 205 ///< MicroChip SAML22 +#define OPT_MCU_SAML21 206 ///< MicroChip SAML21 +#define OPT_MCU_SAMX7X 207 ///< MicroChip SAME70, S70, V70, V71 family + +// STM32 +#define OPT_MCU_STM32F0 300 ///< ST F0 +#define OPT_MCU_STM32F1 301 ///< ST F1 +#define OPT_MCU_STM32F2 302 ///< ST F2 +#define OPT_MCU_STM32F3 303 ///< ST F3 +#define OPT_MCU_STM32F4 304 ///< ST F4 +#define OPT_MCU_STM32F7 305 ///< ST F7 +#define OPT_MCU_STM32H7 306 ///< ST H7 +#define OPT_MCU_STM32L1 308 ///< ST L1 +#define OPT_MCU_STM32L0 307 ///< ST L0 +#define OPT_MCU_STM32L4 309 ///< ST L4 +#define OPT_MCU_STM32G0 310 ///< ST G0 +#define OPT_MCU_STM32G4 311 ///< ST G4 +#define OPT_MCU_STM32WB 312 ///< ST WB +#define OPT_MCU_STM32U5 313 ///< ST U5 +#define OPT_MCU_STM32L5 314 ///< ST L5 +#define OPT_MCU_STM32H5 315 ///< ST H5 + +// Sony +#define OPT_MCU_CXD56 400 ///< SONY CXD56 + +// TI +#define OPT_MCU_MSP430x5xx 500 ///< TI MSP430x5xx +#define OPT_MCU_MSP432E4 510 ///< TI MSP432E4xx +#define OPT_MCU_TM4C123 511 ///< TI Tiva-C 123x +#define OPT_MCU_TM4C129 512 ///< TI Tiva-C 129x + +// ValentyUSB eptri +#define OPT_MCU_VALENTYUSB_EPTRI 600 ///< Fomu eptri config + +// NXP iMX RT +#define OPT_MCU_MIMXRT1XXX 700 ///< NXP iMX RT1xxx Series +#define OPT_MCU_MIMXRT10XX OPT_MCU_MIMXRT1XXX ///< RT10xx +#define OPT_MCU_MIMXRT11XX OPT_MCU_MIMXRT1XXX ///< RT11xx + +// Nuvoton +#define OPT_MCU_NUC121 800 +#define OPT_MCU_NUC126 801 +#define OPT_MCU_NUC120 802 +#define OPT_MCU_NUC505 803 + +// Espressif +#define OPT_MCU_ESP32S2 900 ///< Espressif ESP32-S2 +#define OPT_MCU_ESP32S3 901 ///< Espressif ESP32-S3 +#define OPT_MCU_ESP32 902 ///< Espressif ESP32 (for host max3421e) +#define OPT_MCU_ESP32C3 903 ///< Espressif ESP32-C3 +#define OPT_MCU_ESP32C6 904 ///< Espressif ESP32-C6 +#define TUP_MCU_ESPRESSIF (CFG_TUSB_MCU >= 900 && CFG_TUSB_MCU < 1000) // check if Espressif MCU + +// Dialog +#define OPT_MCU_DA1469X 1000 ///< Dialog Semiconductor DA1469x + +// Raspberry Pi +#define OPT_MCU_RP2040 1100 ///< Raspberry Pi RP2040 + +// NXP Kinetis +#define OPT_MCU_KINETIS_KL 1200 ///< NXP KL series +#define OPT_MCU_KINETIS_K32L 1201 ///< NXP K32L series +#define OPT_MCU_KINETIS_K32 1201 ///< Alias to K32L +#define OPT_MCU_KINETIS_K 1202 ///< NXP K series + +#define OPT_MCU_MKL25ZXX 1200 ///< Alias to KL (obsolete) +#define OPT_MCU_K32L2BXX 1201 ///< Alias to K32 (obsolete) + +// Silabs +#define OPT_MCU_EFM32GG 1300 ///< Silabs EFM32GG + +// Renesas RX +#define OPT_MCU_RX63X 1400 ///< Renesas RX63N/631 +#define OPT_MCU_RX65X 1401 ///< Renesas RX65N/RX651 +#define OPT_MCU_RX72N 1402 ///< Renesas RX72N +#define OPT_MCU_RAXXX 1403 ///< Renesas RAxxx families + +// Mind Motion +#define OPT_MCU_MM32F327X 1500 ///< Mind Motion MM32F327 + +// GigaDevice +#define OPT_MCU_GD32VF103 1600 ///< GigaDevice GD32VF103 + +// Broadcom +#define OPT_MCU_BCM2711 1700 ///< Broadcom BCM2711 +#define OPT_MCU_BCM2835 1701 ///< Broadcom BCM2835 +#define OPT_MCU_BCM2837 1702 ///< Broadcom BCM2837 + +// Infineon +#define OPT_MCU_XMC4000 1800 ///< Infineon XMC4000 + +// PIC +#define OPT_MCU_PIC32MZ 1900 ///< MicroChip PIC32MZ family +#define OPT_MCU_PIC32MM 1901 ///< MicroChip PIC32MM family +#define OPT_MCU_PIC32MX 1902 ///< MicroChip PIC32MX family +#define OPT_MCU_PIC32MK 1903 ///< MicroChip PIC32MK family +#define OPT_MCU_PIC24 1910 ///< MicroChip PIC24 family +#define OPT_MCU_DSPIC33 1911 ///< MicroChip DSPIC33 family + +// BridgeTek +#define OPT_MCU_FT90X 2000 ///< BridgeTek FT90x +#define OPT_MCU_FT93X 2001 ///< BridgeTek FT93x + +// Allwinner +#define OPT_MCU_F1C100S 2100 ///< Allwinner F1C100s family + +// WCH +#define OPT_MCU_CH32V307 2200 ///< WCH CH32V307 +#define OPT_MCU_CH32F20X 2210 ///< WCH CH32F20x + + +// NXP LPC MCX +#define OPT_MCU_MCXN9 2300 ///< NXP MCX N9 Series +#define OPT_MCU_MCXA15 2301 ///< NXP MCX A15 Series + +// Check if configured MCU is one of listed +// Apply _TU_CHECK_MCU with || as separator to list of input +#define _TU_CHECK_MCU(_m) (CFG_TUSB_MCU == _m) +#define TU_CHECK_MCU(...) (TU_ARGS_APPLY(_TU_CHECK_MCU, ||, __VA_ARGS__)) + +//--------------------------------------------------------------------+ +// Supported OS +//--------------------------------------------------------------------+ + +#define OPT_OS_NONE 1 ///< No RTOS +#define OPT_OS_FREERTOS 2 ///< FreeRTOS +#define OPT_OS_MYNEWT 3 ///< Mynewt OS +#define OPT_OS_CUSTOM 4 ///< Custom OS is implemented by application +#define OPT_OS_PICO 5 ///< Raspberry Pi Pico SDK +#define OPT_OS_RTTHREAD 6 ///< RT-Thread +#define OPT_OS_RTX4 7 ///< Keil RTX 4 + +// Allow to use command line to change the config name/location +#ifdef CFG_TUSB_CONFIG_FILE + #include CFG_TUSB_CONFIG_FILE +#else + #include "tusb_config.h" +#endif + +#include "common/tusb_mcu.h" + +//-------------------------------------------------------------------- +// RootHub Mode Configuration +// CFG_TUSB_RHPORTx_MODE contains operation mode and speed for that port +//-------------------------------------------------------------------- + +// Low byte is operational mode +#define OPT_MODE_NONE 0x0000 ///< Disabled +#define OPT_MODE_DEVICE 0x0001 ///< Device Mode +#define OPT_MODE_HOST 0x0002 ///< Host Mode + +// High byte is max operational speed (corresponding to tusb_speed_t) +#define OPT_MODE_DEFAULT_SPEED 0x0000 ///< Default (max) speed supported by MCU +#define OPT_MODE_LOW_SPEED 0x0100 ///< Low Speed +#define OPT_MODE_FULL_SPEED 0x0200 ///< Full Speed +#define OPT_MODE_HIGH_SPEED 0x0400 ///< High Speed +#define OPT_MODE_SPEED_MASK 0xff00 + +//------------- Roothub as Device -------------// + +#if defined(CFG_TUSB_RHPORT0_MODE) && ((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_DEVICE) + #define TUD_RHPORT_MODE (CFG_TUSB_RHPORT0_MODE) + #define TUD_OPT_RHPORT 0 +#elif defined(CFG_TUSB_RHPORT1_MODE) && ((CFG_TUSB_RHPORT1_MODE) & OPT_MODE_DEVICE) + #define TUD_RHPORT_MODE (CFG_TUSB_RHPORT1_MODE) + #define TUD_OPT_RHPORT 1 +#else + #define TUD_RHPORT_MODE OPT_MODE_NONE +#endif + +#ifndef CFG_TUD_ENABLED + // fallback to use CFG_TUSB_RHPORTx_MODE + #define CFG_TUD_ENABLED (TUD_RHPORT_MODE & OPT_MODE_DEVICE) +#endif + +#ifndef CFG_TUD_MAX_SPEED + // fallback to use CFG_TUSB_RHPORTx_MODE + #define CFG_TUD_MAX_SPEED (TUD_RHPORT_MODE & OPT_MODE_SPEED_MASK) +#endif + +// For backward compatible +#define TUSB_OPT_DEVICE_ENABLED CFG_TUD_ENABLED + +// highspeed support indicator +#define TUD_OPT_HIGH_SPEED (CFG_TUD_MAX_SPEED ? (CFG_TUD_MAX_SPEED & OPT_MODE_HIGH_SPEED) : TUP_RHPORT_HIGHSPEED) + +//------------- Roothub as Host -------------// + +#if defined(CFG_TUSB_RHPORT0_MODE) && ((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_HOST) + #define TUH_RHPORT_MODE (CFG_TUSB_RHPORT0_MODE) + #define TUH_OPT_RHPORT 0 +#elif defined(CFG_TUSB_RHPORT1_MODE) && ((CFG_TUSB_RHPORT1_MODE) & OPT_MODE_HOST) + #define TUH_RHPORT_MODE (CFG_TUSB_RHPORT1_MODE) + #define TUH_OPT_RHPORT 1 +#else + #define TUH_RHPORT_MODE OPT_MODE_NONE +#endif + +#ifndef CFG_TUH_ENABLED + // fallback to use CFG_TUSB_RHPORTx_MODE + #define CFG_TUH_ENABLED (TUH_RHPORT_MODE & OPT_MODE_HOST) +#endif + +#ifndef CFG_TUH_MAX_SPEED + // fallback to use CFG_TUSB_RHPORTx_MODE + #define CFG_TUH_MAX_SPEED (TUH_RHPORT_MODE & OPT_MODE_SPEED_MASK) +#endif + +// For backward compatible +#define TUSB_OPT_HOST_ENABLED CFG_TUH_ENABLED + +// highspeed support indicator +#define TUH_OPT_HIGH_SPEED (CFG_TUH_MAX_SPEED ? (CFG_TUH_MAX_SPEED & OPT_MODE_HIGH_SPEED) : TUP_RHPORT_HIGHSPEED) + + +//--------------------------------------------------------------------+ +// TODO move later +//--------------------------------------------------------------------+ + +// TUP_MCU_STRICT_ALIGN will overwrite TUP_ARCH_STRICT_ALIGN. +// In case TUP_MCU_STRICT_ALIGN = 1 and TUP_ARCH_STRICT_ALIGN =0, we will not reply on compiler +// to generate unaligned access code. +// LPC_IP3511 Highspeed cannot access unaligned memory on USB_RAM +#if TUD_OPT_HIGH_SPEED && TU_CHECK_MCU(OPT_MCU_LPC54XXX, OPT_MCU_LPC55XX) + #define TUP_MCU_STRICT_ALIGN 1 +#else + #define TUP_MCU_STRICT_ALIGN 0 +#endif + + +//--------------------------------------------------------------------+ +// Common Options (Default) +//--------------------------------------------------------------------+ + +// Debug enable to print out error message +#ifndef CFG_TUSB_DEBUG + #define CFG_TUSB_DEBUG 0 +#endif + +// Level where CFG_TUSB_DEBUG must be at least for USBH is logged +#ifndef CFG_TUH_LOG_LEVEL + #define CFG_TUH_LOG_LEVEL 2 +#endif + +// Level where CFG_TUSB_DEBUG must be at least for USBD is logged +#ifndef CFG_TUD_LOG_LEVEL + #define CFG_TUD_LOG_LEVEL 2 +#endif + +// Memory section for placing buffer used for usb transferring. If MEM_SECTION is different for +// host and device use: CFG_TUD_MEM_SECTION, CFG_TUH_MEM_SECTION instead +#ifndef CFG_TUSB_MEM_SECTION + #define CFG_TUSB_MEM_SECTION +#endif + +// Alignment requirement of buffer used for usb transferring. if MEM_ALIGN is different for +// host and device controller use: CFG_TUD_MEM_ALIGN, CFG_TUH_MEM_ALIGN instead +#ifndef CFG_TUSB_MEM_ALIGN + #define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4) +#endif + +// OS selection +#ifndef CFG_TUSB_OS + #define CFG_TUSB_OS OPT_OS_NONE +#endif + +#ifndef CFG_TUSB_OS_INC_PATH + #define CFG_TUSB_OS_INC_PATH +#endif + +//-------------------------------------------------------------------- +// Device Options (Default) +//-------------------------------------------------------------------- + +// Attribute to place data in accessible RAM for device controller (default: CFG_TUSB_MEM_SECTION) +#ifndef CFG_TUD_MEM_SECTION + #define CFG_TUD_MEM_SECTION CFG_TUSB_MEM_SECTION +#endif + +// Attribute to align memory for device controller (default: CFG_TUSB_MEM_ALIGN) +#ifndef CFG_TUD_MEM_ALIGN + #define CFG_TUD_MEM_ALIGN CFG_TUSB_MEM_ALIGN +#endif + +#ifndef CFG_TUD_ENDPOINT0_SIZE + #define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +#ifndef CFG_TUD_INTERFACE_MAX + #define CFG_TUD_INTERFACE_MAX 16 +#endif + +//------------- Device Class Driver -------------// +#ifndef CFG_TUD_BTH + #define CFG_TUD_BTH 0 +#endif + +#if CFG_TUD_BTH && !defined(CFG_TUD_BTH_ISO_ALT_COUNT) +#error CFG_TUD_BTH_ISO_ALT_COUNT must be defined to tell Bluetooth driver the number of ISO endpoints to use +#endif + +#ifndef CFG_TUD_CDC + #define CFG_TUD_CDC 0 +#endif + +#ifndef CFG_TUD_MSC + #define CFG_TUD_MSC 0 +#endif + +#ifndef CFG_TUD_HID + #define CFG_TUD_HID 0 +#endif + +#ifndef CFG_TUD_AUDIO + #define CFG_TUD_AUDIO 0 +#endif + +#ifndef CFG_TUD_VIDEO + #define CFG_TUD_VIDEO 0 +#endif + +#ifndef CFG_TUD_MIDI + #define CFG_TUD_MIDI 0 +#endif + +#ifndef CFG_TUD_VENDOR + #define CFG_TUD_VENDOR 0 +#endif + +#ifndef CFG_TUD_USBTMC + #define CFG_TUD_USBTMC 0 +#endif + +#ifndef CFG_TUD_DFU_RUNTIME + #define CFG_TUD_DFU_RUNTIME 0 +#endif + +#ifndef CFG_TUD_DFU + #define CFG_TUD_DFU 0 +#endif + +#ifndef CFG_TUD_ECM_RNDIS + #ifdef CFG_TUD_NET + #warning "CFG_TUD_NET is renamed to CFG_TUD_ECM_RNDIS" + #define CFG_TUD_ECM_RNDIS CFG_TUD_NET + #else + #define CFG_TUD_ECM_RNDIS 0 + #endif +#endif + +#ifndef CFG_TUD_NCM + #define CFG_TUD_NCM 0 +#endif + +//-------------------------------------------------------------------- +// Host Options (Default) +//-------------------------------------------------------------------- +#if CFG_TUH_ENABLED + #ifndef CFG_TUH_DEVICE_MAX + #define CFG_TUH_DEVICE_MAX 1 + #endif + + #ifndef CFG_TUH_ENUMERATION_BUFSIZE + #define CFG_TUH_ENUMERATION_BUFSIZE 256 + #endif +#endif // CFG_TUH_ENABLED + +// Attribute to place data in accessible RAM for host controller (default: CFG_TUSB_MEM_SECTION) +#ifndef CFG_TUH_MEM_SECTION + #define CFG_TUH_MEM_SECTION CFG_TUSB_MEM_SECTION +#endif + +// Attribute to align memory for host controller +#ifndef CFG_TUH_MEM_ALIGN + #define CFG_TUH_MEM_ALIGN CFG_TUSB_MEM_ALIGN +#endif + +//------------- CLASS -------------// + +#ifndef CFG_TUH_HUB + #define CFG_TUH_HUB 0 +#endif + +#ifndef CFG_TUH_CDC + #define CFG_TUH_CDC 0 +#endif + +#ifndef CFG_TUH_CDC_FTDI + // FTDI is not part of CDC class, only to re-use CDC driver API + #define CFG_TUH_CDC_FTDI 0 +#endif + +#ifndef CFG_TUH_CDC_FTDI_VID_PID_LIST + // List of product IDs that can use the FTDI CDC driver. 0x0403 is FTDI's VID + #define CFG_TUH_CDC_FTDI_VID_PID_LIST \ + {0x0403, 0x6001}, {0x0403, 0x6006}, {0x0403, 0x6010}, {0x0403, 0x6011}, \ + {0x0403, 0x6014}, {0x0403, 0x6015}, {0x0403, 0x8372}, {0x0403, 0xFBFA}, \ + {0x0403, 0xCD18} +#endif + +#ifndef CFG_TUH_CDC_CP210X + // CP210X is not part of CDC class, only to re-use CDC driver API + #define CFG_TUH_CDC_CP210X 0 +#endif + +#ifndef CFG_TUH_CDC_CP210X_VID_PID_LIST + // List of product IDs that can use the CP210X CDC driver. 0x10C4 is Silicon Labs' VID + #define CFG_TUH_CDC_CP210X_VID_PID_LIST \ + {0x10C4, 0xEA60}, {0x10C4, 0xEA70} +#endif + +#ifndef CFG_TUH_CDC_CH34X + // CH34X is not part of CDC class, only to re-use CDC driver API + #define CFG_TUH_CDC_CH34X 0 +#endif + +#ifndef CFG_TUH_CDC_CH34X_VID_PID_LIST + // List of product IDs that can use the CH34X CDC driver + #define CFG_TUH_CDC_CH34X_VID_PID_LIST \ + { 0x1a86, 0x5523 }, /* ch341 chip */ \ + { 0x1a86, 0x7522 }, /* ch340k chip */ \ + { 0x1a86, 0x7523 }, /* ch340 chip */ \ + { 0x1a86, 0xe523 }, /* ch330 chip */ \ + { 0x4348, 0x5523 }, /* ch340 custom chip */ \ + { 0x2184, 0x0057 }, /* overtaken from Linux Kernel driver /drivers/usb/serial/ch341.c */ \ + { 0x9986, 0x7523 } /* overtaken from Linux Kernel driver /drivers/usb/serial/ch341.c */ +#endif + +#ifndef CFG_TUH_HID + #define CFG_TUH_HID 0 +#endif + +#ifndef CFG_TUH_MIDI + #define CFG_TUH_MIDI 0 +#endif + +#ifndef CFG_TUH_MSC + #define CFG_TUH_MSC 0 +#endif + +#ifndef CFG_TUH_VENDOR + #define CFG_TUH_VENDOR 0 +#endif + +#ifndef CFG_TUH_API_EDPT_XFER + #define CFG_TUH_API_EDPT_XFER 0 +#endif + +// Enable PIO-USB software host controller +#ifndef CFG_TUH_RPI_PIO_USB + #define CFG_TUH_RPI_PIO_USB 0 +#endif + +#ifndef CFG_TUD_RPI_PIO_USB + #define CFG_TUD_RPI_PIO_USB 0 +#endif + +// MAX3421 Host controller option +#ifndef CFG_TUH_MAX3421 + #define CFG_TUH_MAX3421 0 +#endif + +//--------------------------------------------------------------------+ +// TypeC Options (Default) +//--------------------------------------------------------------------+ + +#ifndef CFG_TUC_ENABLED +#define CFG_TUC_ENABLED 0 + +#define tuc_int_handler(_p) +#endif + +//------------------------------------------------------------------ +// Configuration Validation +//------------------------------------------------------------------ +#if CFG_TUD_ENDPOINT0_SIZE > 64 + #error Control Endpoint Max Packet Size cannot be larger than 64 +#endif + +// To avoid GCC compiler warnings when -pedantic option is used (strict ISO C) +typedef int make_iso_compilers_happy; + +#endif /* _TUSB_OPTION_H_ */ + +/** @} */ diff --git a/test-devices/loopback-stm32/platformio.ini b/test-devices/loopback-stm32/platformio.ini index d586996e..5f2375ab 100644 --- a/test-devices/loopback-stm32/platformio.ini +++ b/test-devices/loopback-stm32/platformio.ini @@ -1,4 +1,40 @@ -[env:loopback-stm32] +[common] +tinyusb_flags = + -D CFG_VENDOR_CUSTOM=1 + -D CFG_TUSB_RHPORT1_MODE=OPT_MODE_NONE platform = ststm32 +framework = cmsis +debug_tool = stlink + +[env:blackpill-f401cc] +extends = common +board = blackpill_f401cc +build_flags = + ${common.tinyusb_flags} + -D CFG_TUSB_MCU=OPT_MCU_STM32F4 + -D HSE_VALUE=25000000 + +[env:blackpill-f411ce] +extends = common +board = blackpill_f411ce +build_flags = + ${common.tinyusb_flags} + -D CFG_TUSB_MCU=OPT_MCU_STM32F4 + -D HSE_VALUE=25000000 + +[env:bluepill-f103c8] +extends = common board = bluepill_f103c8 -framework = libopencm3 +build_flags = + ${common.tinyusb_flags} + -D CFG_TUSB_MCU=OPT_MCU_STM32F1 + +[env:disco_f723ie] +extends = common +board = disco_f723ie +build_flags = + ${common.tinyusb_flags} + -D CFG_TUSB_MCU=OPT_MCU_STM32F7 + -D HSE_VALUE=25000000 + -D BOARD_TUD_RHPORT=1 + -D BOARD_TUD_MAX_SPEED=OPT_MODE_HIGH_SPEED diff --git a/test-devices/loopback-stm32/save_firmware.sh b/test-devices/loopback-stm32/save_firmware.sh new file mode 100755 index 00000000..f7247427 --- /dev/null +++ b/test-devices/loopback-stm32/save_firmware.sh @@ -0,0 +1,7 @@ +#!/bin/sh +rm -rf .pio +pio run +cp .pio/build/bluepill-f103c8/firmware.bin bin/bluepill-f103c8.bin +cp .pio/build/blackpill-f401cc/firmware.bin bin/blackpill-f401cc.bin +cp .pio/build/blackpill-f411ce/firmware.bin bin/blackpill-f411ce.bin +cp .pio/build/disco_f723ie/firmware.bin bin/disco_f723ie.bin diff --git a/test-devices/loopback-stm32/src/board.h b/test-devices/loopback-stm32/src/board.h new file mode 100644 index 00000000..8d073409 --- /dev/null +++ b/test-devices/loopback-stm32/src/board.h @@ -0,0 +1,40 @@ +// +// Java Does USB +// Loopback device for testing +// +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Board specific functions (HAL) +// + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +// Initialize the board +void board_init(void); + +// Set the LED on or off +void board_led_write(bool on); + +// Return the number of milliseconds since a time in the past +uint32_t board_millis(void); + +// Enter sleep or stop mode and wake up on USB resume +void board_sleep(void); + +// USB serial number +extern char board_serial_num[13]; + + +#ifdef __cplusplus +} +#endif diff --git a/test-devices/loopback-stm32/src/board_f1.c b/test-devices/loopback-stm32/src/board_f1.c new file mode 100644 index 00000000..d0690d88 --- /dev/null +++ b/test-devices/loopback-stm32/src/board_f1.c @@ -0,0 +1,253 @@ +// +// Java Does USB +// Loopback device for testing +// +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Board specific code for STM32F1 family +// + +#if defined(STM32F1) + +#include +#include "stm32f1xx.h" +#include "device/usbd.h" + +#define EXTI_USBWakeUp_Line EXTI_IMR_IM18 + + +extern uint32_t SystemCoreClock; +void SystemCoreClockUpdate(void); + + +static inline uint32_t get_reg(__I uint32_t* reg, uint32_t mask) { + return *reg & mask; +} + +static inline void set_reg(__IO uint32_t* reg, uint32_t value, uint32_t mask) { + *reg = (*reg & ~mask) | (value & mask); +} + + +// --- additional RCC constants + +#define RCC_CFGR_PLLSRC_HSI (0 << RCC_CFGR_PLLSRC_Pos) +#define RCC_CFGR_PLLSRC_HSE (1 << RCC_CFGR_PLLSRC_Pos) + +// --- additional SysTick constants + +#define SysTick_CTRL_CLKSOURCE_AHB_DIV8 (0 << SysTick_CTRL_CLKSOURCE_Pos) +#define SysTick_CTRL_CLKSOURCE_AHB (1 << SysTick_CTRL_CLKSOURCE_Pos) + +// --- additional GPIO constants + +#define GPIO_CNF_INPUT_ANALOG 0 +#define GPIO_CNF_INPUT_FLOAT 1 +#define GPIO_CNF_INPUT_PUPD 2 +#define GPIO_CNF_OUTPUT_PUSH_PULL 0 +#define GPIO_CNF_OUTPUT_OPEN_DRAIN 1 +#define GPIO_CNF_OUTPUT_ALT_PUSH_PULL 2 +#define GPIO_CNG_OUTPUT_ALT_OPEN_DRAIN 3 + +#define GPIO_MODE_INPUT 0 +#define GPIO_MODE_OUTPUT_10_MHZ 1 +#define GPIO_MODE_OUTPUT_2_MHZ 2 +#define GPIO_MODE_OUTPUT_50_MHZ 3 + + +static inline void rcc_wait_for_osc_ready(uint32_t rcc_cr_clk_rdy) { + while (get_reg(&RCC->CR, rcc_cr_clk_rdy) == 0) + ; +} + + +static void gpio_set_mode(GPIO_TypeDef* gpioport, int gpio, uint8_t mode, uint8_t cnf) { + + int offset; + __IO uint32_t* reg; + if (gpio < 8) { + offset = 4 * gpio; + reg = &gpioport->CRL; + } else { + offset = 4 * (gpio - 8); + reg = &gpioport->CRH; + } + + set_reg(reg, ((cnf << 2) | mode) << offset, 0xf << offset); +} + +static inline void gpio_set(GPIO_TypeDef* gpioport, int gpio) { + gpioport->BSRR = 1 << gpio; +} + +static inline void gpio_clear(GPIO_TypeDef* gpioport, int gpio) { + gpioport->BSRR = 1 << (gpio + 16); +} + + +static void rcc_clock_setup_in_hse_8mhz_out_72mhz(void) { + + // Enable internal high-speed oscillator + set_reg(&RCC->CR, RCC_CR_HSION, RCC_CR_HSION_Msk); + rcc_wait_for_osc_ready(RCC_CR_HSIRDY); + + // Select HSI as SYSCLK source + set_reg(&RCC->CFGR, RCC_CFGR_SW_HSI, RCC_CFGR_SW_Msk); + + // Enable external high-speed oscillator 8MHz + set_reg(&RCC->CR, RCC_CR_HSEON, RCC_CR_HSEON_Msk); + rcc_wait_for_osc_ready(RCC_CR_HSERDY); + set_reg(&RCC->CFGR, RCC_CFGR_SW_HSE, RCC_CFGR_SW_Msk); + + // Set prescalers for AHB, ADC, APB1, APB2 + set_reg(&RCC->CFGR, RCC_CFGR_HPRE_DIV1 | RCC_CFGR_ADCPRE_DIV8 | RCC_CFGR_PPRE1_DIV2 | RCC_CFGR_PPRE2_DIV1, + RCC_CFGR_HPRE_Msk | RCC_CFGR_ADCPRE_Msk | RCC_CFGR_PPRE1_Msk | RCC_CFGR_PPRE2_Msk); + + // System clock of 72 MHz requires 2 wait states + set_reg(&FLASH->ACR, FLASH_ACR_LATENCY_2, FLASH_ACR_LATENCY_Msk); + + // PLL multiplier 9 (for 72 MHz), HSE as PLL source, no clock predevision + set_reg(&RCC->CFGR, RCC_CFGR_PLLMULL9 | RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLXTPRE_HSE, + RCC_CFGR_PLLMULL_Msk | RCC_CFGR_PLLSRC_Msk | RCC_CFGR_PLLXTPRE_Msk); + + // Enable PLL oscillator and wait for it to stabilize + set_reg(&RCC->CR, RCC_CR_PLLON, RCC_CR_PLLON_Msk); + rcc_wait_for_osc_ready(RCC_CR_PLLRDY); + + // Select PLL as SYSCLK source + set_reg(&RCC->CFGR, RCC_CFGR_SW_PLL, RCC_CFGR_SW_Msk); + + // Update the SystemCoreClock variable used by TinyUSB + SystemCoreClockUpdate(); +} + +static volatile uint32_t millis_count; + +static void systick_init(void) { + + // Initialize SysTick + set_reg(&SysTick->CTRL, SysTick_CTRL_CLKSOURCE_AHB_DIV8, SysTick_CTRL_CLKSOURCE_Msk); + SysTick->LOAD = SystemCoreClock / 8 / 1000 - 1; + + // Enable and start + set_reg(&SysTick->CTRL, SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk, + SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk); +} + + +// --- Serial number --- + +char board_serial_num[13]; + +const static char HEX_DIGITS[] = "0123456789ABCDEF"; + +void put_hex(uint32_t value, char *buf, int len) { + for (int idx = 0; idx < len; idx++) { + buf[idx] = HEX_DIGITS[value >> 28]; + value = value << 4; + } +} + +void usb_init_serial_num() { + __I uint32_t* unique_id =(__I uint32_t*) UID_BASE; + uint32_t id0 = unique_id[0]; + uint32_t id1 = unique_id[1]; + uint32_t id2 = unique_id[2]; + + id0 += id2; + + put_hex(id0, board_serial_num, 8); + put_hex(id1, board_serial_num + 8, 4); + board_serial_num[12] = 0; +} + + +// --- Exported board functions + +void board_init(void) { + + rcc_clock_setup_in_hse_8mhz_out_72mhz(); + systick_init(); + + // clock for GPIOA (USB pins) + RCC->APB2ENR |= RCC_APB2ENR_IOPAEN_Msk; + // clock for GPIOB (LED) + RCC->APB2ENR |= RCC_APB2ENR_IOPBEN_Msk; + // clock for USB + RCC->APB1ENR |= RCC_APB1ENR_USBEN_Msk; + + // LED + gpio_set_mode(GPIOB, 12, GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSH_PULL); + + usb_init_serial_num(); + + // Wake up event is only available as interrupt, not as an event. + // See product errata sheet + set_reg(&EXTI->RTSR, EXTI_USBWakeUp_Line, EXTI_USBWakeUp_Line); + set_reg(&EXTI->IMR, EXTI_USBWakeUp_Line, EXTI_USBWakeUp_Line); + NVIC_EnableIRQ(USBWakeUp_IRQn); +} + +uint32_t board_millis(void) { + return millis_count; +} + +void board_led_write(bool on) { + if (on) + gpio_clear(GPIOB, 12); + else + gpio_set(GPIOB, 12); +} + +void board_sleep(void) { + + // turn off LED + board_led_write(false); + + // pause systick interrupts + set_reg(&SysTick->CTRL, 0, SysTick_CTRL_TICKINT_Msk); + + // enter Stop mode when the CPU enters deep sleep + set_reg(&PWR->CR, 0, PWR_CR_PDDS_Msk | PWR_CR_LPDS_Msk); + + set_reg(&SCB->SCR, SCB_SCR_SLEEPDEEP_Msk, SCB_SCR_SLEEPDEEP_Msk); + + // sleep until an interrupt occurs + __WFI(); + + // reset SLEEPDEEP bit + set_reg(&SCB->SCR, 0, SCB_SCR_SLEEPDEEP_Msk); + + // after wakeup, re-enable PLL as clock source + rcc_clock_setup_in_hse_8mhz_out_72mhz(); + + // resume systick interrupts + set_reg(&SysTick->CTRL, SysTick_CTRL_TICKINT_Msk, SysTick_CTRL_TICKINT_Msk); + + // turn on LED + board_led_write(true); +} + + +// --- Interrupt handlers --- + +void SysTick_Handler (void) { + millis_count++; +} + +void USBWakeUp_IRQHandler(void) { + // clear interrupt + EXTI->PR = EXTI_USBWakeUp_Line; +} + +void USB_HP_IRQHandler(void) { + tud_int_handler(0); +} + +void USB_LP_IRQHandler(void) { + tud_int_handler(0); +} + +#endif diff --git a/test-devices/loopback-stm32/src/board_f4.c b/test-devices/loopback-stm32/src/board_f4.c new file mode 100644 index 00000000..dfb887e7 --- /dev/null +++ b/test-devices/loopback-stm32/src/board_f4.c @@ -0,0 +1,336 @@ +// +// Java Does USB +// Loopback device for testing +// +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Board specific code for STM32F4 family +// + +#if defined(STM32F4xx) + +#include +#include "stm32f4xx.h" +#include "device/usbd.h" + +#define EXTI_USBWakeUp_Line EXTI_IMR_IM18 + + +extern uint32_t SystemCoreClock; +void SystemCoreClockUpdate(void); + + +static inline uint32_t get_reg(__I uint32_t* reg, uint32_t mask) { + return *reg & mask; +} + +static inline void set_reg(__IO uint32_t* reg, uint32_t value, uint32_t mask) { + *reg = (*reg & ~mask) | (value & mask); +} + + +// --- additional PWR constants + +#define PWR_CR_VOS_SCALE3 (1 << PWR_CR_VOS_Pos) +#define PWR_CR_VOS_SCALE2 (2 << PWR_CR_VOS_Pos) +#define PWR_CR_VOS_SCALE1 (3 << PWR_CR_VOS_Pos) + + +// --- additional RCC constants + +typedef struct rcc_clock_setup { + uint8_t pllm; + uint16_t plln; + uint8_t pllp; + uint8_t pllq; + uint32_t pll_source; + uint32_t flash_config; + uint32_t hpre; + uint32_t ppre1; + uint32_t ppre2; + uint32_t voltage_scale; +} rcc_clock_setup_t; + +const rcc_clock_setup_t clock_setup_hse_value_out_84mhz_3v3 = { + .pllm = HSE_VALUE / 1000000, + .plln = 336, + .pllp = 4, + .pllq = 7, + .pll_source = RCC_PLLCFGR_PLLSRC_HSE, + .hpre = RCC_CFGR_HPRE_DIV1, + .ppre1 = RCC_CFGR_PPRE1_DIV2, + .ppre2 = RCC_CFGR_PPRE2_DIV1, + .voltage_scale = PWR_CR_VOS_SCALE1, + .flash_config = FLASH_ACR_DCEN | FLASH_ACR_ICEN | FLASH_ACR_LATENCY_2WS +}; + +// --- additional SysTick constants + +#define SysTick_CTRL_CLKSOURCE_AHB_DIV8 (0 << SysTick_CTRL_CLKSOURCE_Pos) +#define SysTick_CTRL_CLKSOURCE_AHB (1 << SysTick_CTRL_CLKSOURCE_Pos) + +// --- additional GPIO constants + +#define GPIO_PUPD_NO_PULL 0 +#define GPIO_PUPD_PULL_UP 1 +#define GPIO_PUPD_PULL_DOWN 2 + +#define GPIO_MODE_INPUT 0 +#define GPIO_MODE_OUTPUT 1 +#define GPIO_MODE_ALT 2 +#define GPIO_MODE_ANALOG 3 + +#define GPIO_OSPEED_LOW 0 +#define GPIO_OSPEED_MEDIUM 1 +#define GPIO_OSPEED_FAST 2 +#define GPIO_OSPEED_HIGH 3 + + +// --- additional USB register +#define PCGCCTL ((volatile uint32_t *)((uint32_t)USB_OTG_FS + USB_OTG_PCGCCTL_BASE)) + + + +static inline void rcc_wait_for_osc_ready(uint32_t rcc_cr_clk_rdy) { + while (get_reg(&RCC->CR, rcc_cr_clk_rdy) == 0) + ; +} + +static void gpio_mode_setup(GPIO_TypeDef* gpioport, int gpio, uint8_t mode, uint8_t pull_up_down) { + + int offset = gpio * 2; + set_reg(&gpioport->PUPDR, pull_up_down << offset, 3 << offset); + set_reg(&gpioport->MODER, mode << offset, 3 << offset); +} + +void gpio_set_af(GPIO_TypeDef* gpioport, int gpio, uint8_t alt_func_num) { + + int offset = 4 * gpio; + __IO uint32_t* reg; + if (offset < 32) { + reg = gpioport->AFR; + } else { + reg = gpioport->AFR + 1; + offset -= 32; + } + + set_reg(reg, alt_func_num << offset, 0xf << offset); +} + +static inline void gpio_set_ospeed(GPIO_TypeDef* gpioport, int gpio, uint8_t ospeed) { + int offset = gpio * 2; + set_reg(&gpioport->OSPEEDR, ospeed << offset, 3 << offset); +} + +static inline void gpio_set(GPIO_TypeDef* gpioport, int gpio) { + gpioport->BSRR = 1 << gpio; +} + +static inline void gpio_clear(GPIO_TypeDef* gpioport, int gpio) { + gpioport->BSRR = 1 << (gpio + 16); +} + + +static void rcc_clock_setup_pll(const rcc_clock_setup_t* setup) { + + // Enable internal high-speed oscillator (HSI) + set_reg(&RCC->CR, RCC_CR_HSION, RCC_CR_HSION_Msk); + rcc_wait_for_osc_ready(RCC_CR_HSIRDY); + + // Select HSI as SYSCLK source + set_reg(&RCC->CFGR, RCC_CFGR_SW_HSI, RCC_CFGR_SW_Msk); + + // Enable external high-speed oscillator (HSE) + if (setup->pll_source == RCC_PLLCFGR_PLLSRC_HSE) { + set_reg(&RCC->CR, RCC_CR_HSEON, RCC_CR_HSEON_Msk); + rcc_wait_for_osc_ready(RCC_CR_HSERDY); + } + + // Set the VOS scale mode + set_reg(&RCC->APB1ENR, RCC_APB1ENR_PWREN, RCC_APB1ENR_PWREN_Msk); + set_reg(&PWR->CR, setup->voltage_scale, PWR_CR_VOS_Msk); + + // Set prescalers for AHB, APB1, APB2 + set_reg(&RCC->CFGR, setup->hpre | setup->ppre1 | setup->ppre2, + RCC_CFGR_HPRE_Msk | RCC_CFGR_PPRE1_Msk | RCC_CFGR_PPRE2_Msk); + + // Disable PLL oscillator before changing its configuration + set_reg(&RCC->CR, 0, RCC_CR_PLLON_Msk); + + // Configure the PLL oscillator + int pllp_val = (setup->pllp >> 1) - 1; + RCC->PLLCFGR = setup->pll_source + | (setup->pllm << RCC_PLLCFGR_PLLM_Pos) + | (setup->plln << RCC_PLLCFGR_PLLN_Pos) + | (pllp_val << RCC_PLLCFGR_PLLP_Pos) + | (setup->pllq << RCC_PLLCFGR_PLLQ_Pos); + + // Enable PLL oscillator and wait for it to stabilize + set_reg(&RCC->CR, RCC_CR_PLLON, RCC_CR_PLLON_Msk); + rcc_wait_for_osc_ready(RCC_CR_PLLRDY); + + // Configure flash settings + set_reg(&FLASH->ACR, setup->flash_config, FLASH_ACR_DCEN_Msk | FLASH_ACR_ICEN_Msk | FLASH_ACR_LATENCY_Msk); + + // Select PLL as SYSCLK source + set_reg(&RCC->CFGR, RCC_CFGR_SW_PLL, RCC_CFGR_SW_Msk); + + // Wait for PLL clock to be selected + while (get_reg(&RCC->CFGR, RCC_CFGR_SWS_Msk) != RCC_CFGR_SWS_PLL) + ; + + // Disable internal high-speed oscillator + if (setup->pll_source == RCC_PLLCFGR_PLLSRC_HSE) + set_reg(&RCC->CR, 0, RCC_CR_HSION_Msk); + + // Update the SystemCoreClock variable used by TinyUSB + SystemCoreClockUpdate(); +} + +static volatile uint32_t millis_count; + +static void systick_init(void) { + + // Initialize SysTick + SysTick->CTRL = (SysTick->CTRL & ~SysTick_CTRL_CLKSOURCE_Msk) | SysTick_CTRL_CLKSOURCE_AHB_DIV8; + SysTick->LOAD = SystemCoreClock / 8 / 1000 - 1; + + // Enable and start + SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; + SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; +} + + +// --- Serial number --- + +char board_serial_num[13]; + +const static char HEX_DIGITS[] = "0123456789ABCDEF"; + +static void put_hex(uint32_t value, char *buf, int len) { + for (int idx = 0; idx < len; idx++) { + buf[idx] = HEX_DIGITS[value >> 28]; + value = value << 4; + } +} + +static void usb_init_serial_num() { + __I uint32_t* unique_id =(__I uint32_t*) UID_BASE; + uint32_t id0 = unique_id[0]; + uint32_t id1 = unique_id[1]; + uint32_t id2 = unique_id[2]; + + id0 += id2; + + put_hex(id0, board_serial_num, 8); + put_hex(id1, board_serial_num + 8, 4); + board_serial_num[12] = 0; +} + + +// --- Exported board functions + +void board_init(void) { + + rcc_clock_setup_pll(&clock_setup_hse_value_out_84mhz_3v3); + systick_init(); + + // clock for GPIOA (USB pins) + set_reg(&RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN, RCC_AHB1ENR_GPIOAEN_Msk); + + // Configure USB D+/D- pins + gpio_mode_setup(GPIOA, 11, GPIO_MODE_ALT, GPIO_PUPD_NO_PULL); + gpio_set_af(GPIOA, 11, 10); + gpio_set_ospeed(GPIOA, 11, GPIO_OSPEED_HIGH); + gpio_mode_setup(GPIOA, 12, GPIO_MODE_ALT, GPIO_PUPD_NO_PULL); + gpio_set_af(GPIOA, 12, 10); + gpio_set_ospeed(GPIOA, 12, GPIO_OSPEED_HIGH); + + // clock for USB + set_reg(&RCC->AHB2ENR, RCC_AHB2ENR_OTGFSEN, RCC_AHB2ENR_OTGFSEN_Msk); + + // Disable VBUS sense + set_reg(&USB_OTG_FS->GCCFG, USB_OTG_GCCFG_NOVBUSSENS, + USB_OTG_GCCFG_NOVBUSSENS_Msk | USB_OTG_GCCFG_VBUSASEN_Msk | USB_OTG_GCCFG_VBUSBSEN_Msk); + + // clock for GPIOC (LED) + set_reg(&RCC->AHB1ENR, RCC_AHB1ENR_GPIOCEN, RCC_AHB1ENR_GPIOCEN_Msk); + + // LED pin + gpio_mode_setup(GPIOC, 13, GPIO_MODE_OUTPUT, GPIO_PUPD_NO_PULL); + + usb_init_serial_num(); + + // enable USB wakeup interrupt + EXTI->PR = EXTI_USBWakeUp_Line; + EXTI->RTSR |= EXTI_USBWakeUp_Line; + EXTI->IMR |= EXTI_USBWakeUp_Line; + NVIC_SetPriority(OTG_FS_WKUP_IRQn, 0); + NVIC_EnableIRQ(OTG_FS_WKUP_IRQn); +} + +uint32_t board_millis(void) { + return millis_count; +} + +void board_led_write(bool on) { + if (on) + gpio_clear(GPIOC, 13); + else + gpio_set(GPIOC, 13); +} + +void board_sleep(void) { + + // turn off LED + board_led_write(false); + + // stop PCLK to USB + set_reg(PCGCCTL, USB_OTG_PCGCCTL_STOPCLK, USB_OTG_PCGCCTL_STOPCLK_Msk); + + // pause systick interrupts + set_reg(&SysTick->CTRL, 0, SysTick_CTRL_TICKINT_Msk); + + // enter stop mode when the CPU enters deep sleep + set_reg(&PWR->CR, 0, PWR_CR_PDDS_Msk | PWR_CR_LPDS_Msk); + + // use deep sleep mode + set_reg(&SCB->SCR, SCB_SCR_SLEEPDEEP_Msk, SCB_SCR_SLEEPDEEP_Msk); + + __WFI(); + + // reset to regular sleep mode + set_reg(&SCB->SCR, 0, SCB_SCR_SLEEPDEEP_Msk); + + // after wakeup, re-enable PLL as clock source + rcc_clock_setup_pll(&clock_setup_hse_value_out_84mhz_3v3); + + // resume systick interrupts + set_reg(&SysTick->CTRL, SysTick_CTRL_TICKINT_Msk, SysTick_CTRL_TICKINT_Msk); + + // restart PCLK to USB + set_reg(PCGCCTL, 0, USB_OTG_PCGCCTL_STOPCLK_Msk); + + // turn on LED + board_led_write(true); +} + + +// --- Interrupt handlers --- + +void SysTick_Handler (void) { + millis_count++; +} + +void OTG_FS_IRQHandler(void) { + tud_int_handler(0); +} + +void OTG_FS_WKUP_IRQHandler(void) { + // clear interrupt + EXTI->PR = EXTI_USBWakeUp_Line; +} + +#endif diff --git a/test-devices/loopback-stm32/src/board_f7.c b/test-devices/loopback-stm32/src/board_f7.c new file mode 100644 index 00000000..34d40bf5 --- /dev/null +++ b/test-devices/loopback-stm32/src/board_f7.c @@ -0,0 +1,313 @@ +// +// Java Does USB +// Loopback device for testing +// +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Board specific code for STM32F7 family +// + +#if defined(STM32F723xx) + +#include +#include "stm32f7xx.h" +#include "device/usbd.h" + +extern uint32_t SystemCoreClock; +void SystemCoreClockUpdate(void); + + +static inline uint32_t get_reg(__I uint32_t* reg, uint32_t mask) { + return *reg & mask; +} + +static inline void set_reg(__IO uint32_t* reg, uint32_t value, uint32_t mask) { + *reg = (*reg & ~mask) | (value & mask); +} + + +// --- additional PWR constants + +#define PWR_CR1_VOS_SCALE3 (1 << PWR_CR1_VOS_Pos) +#define PWR_CR1_VOS_SCALE2 (2 << PWR_CR1_VOS_Pos) +#define PWR_CR1_VOS_SCALE1 (3 << PWR_CR1_VOS_Pos) + + +// --- additional RCC constants + +typedef struct rcc_clock_setup { + uint8_t pllm; + uint16_t plln; + uint8_t pllp; + uint8_t pllq; + uint32_t hpre; + uint32_t ppre1; + uint32_t ppre2; + uint32_t voltage_scale; + uint8_t overdrive; + uint8_t flash_wait_states; +} rcc_clock_setup_t; + +const rcc_clock_setup_t clock_setup_hse_216mhz_3v3 = { + .pllm = HSE_VALUE / 1000000, + .plln = 432, + .pllp = 2, + .pllq = 9, + .hpre = RCC_CFGR_HPRE_DIV1, + .ppre1 = RCC_CFGR_PPRE1_DIV4, + .ppre2 = RCC_CFGR_PPRE2_DIV2, + .voltage_scale = PWR_CR1_VOS_SCALE1, + .overdrive = 1, + .flash_wait_states = 7 +}; + +// --- additional SysTick constants + +#define SysTick_CTRL_CLKSOURCE_AHB_DIV8 (0 << SysTick_CTRL_CLKSOURCE_Pos) +#define SysTick_CTRL_CLKSOURCE_AHB (1 << SysTick_CTRL_CLKSOURCE_Pos) + +// --- additional GPIO constants + +#define GPIO_PUPD_NO_PULL 0 +#define GPIO_PUPD_PULL_UP 1 +#define GPIO_PUPD_PULL_DOWN 2 + +#define GPIO_MODE_INPUT 0 +#define GPIO_MODE_OUTPUT 1 +#define GPIO_MODE_ALT 2 +#define GPIO_MODE_ANALOG 3 + +#define GPIO_OSPEED_LOW 0 +#define GPIO_OSPEED_MEDIUM 1 +#define GPIO_OSPEED_FAST 2 +#define GPIO_OSPEED_HIGH 3 + + +static inline void rcc_wait_for_osc_ready(uint32_t rcc_cr_clk_rdy) { + while (get_reg(&RCC->CR, rcc_cr_clk_rdy) == 0) + ; +} + +static void gpio_mode_setup(GPIO_TypeDef* gpioport, int gpio, uint8_t mode, uint8_t pull_up_down) { + + int offset = gpio * 2; + set_reg(&gpioport->PUPDR, pull_up_down << offset, 3 << offset); + set_reg(&gpioport->MODER, mode << offset, 3 << offset); +} + +void gpio_set_af(GPIO_TypeDef* gpioport, int gpio, uint8_t alt_func_num) { + + int offset = 4 * gpio; + __IO uint32_t* reg; + if (offset < 32) { + reg = gpioport->AFR; + } else { + reg = gpioport->AFR + 1; + offset -= 32; + } + + set_reg(reg, alt_func_num << offset, 0xf << offset); +} + +static inline void gpio_set_ospeed(GPIO_TypeDef* gpioport, int gpio, uint8_t ospeed) { + int offset = gpio * 2; + set_reg(&gpioport->OSPEEDR, ospeed << offset, 3 << offset); +} + +static inline void gpio_set(GPIO_TypeDef* gpioport, int gpio) { + gpioport->BSRR = 1 << gpio; +} + +static inline void gpio_clear(GPIO_TypeDef* gpioport, int gpio) { + gpioport->BSRR = 1 << (gpio + 16); +} + + +static void rcc_clock_setup_hse(const rcc_clock_setup_t* setup) { + + // Enable internal high-speed oscillator (HSI) + set_reg(&RCC->CR, RCC_CR_HSION, RCC_CR_HSION_Msk); + rcc_wait_for_osc_ready(RCC_CR_HSIRDY); + + // Select HSI as SYSCLK source + set_reg(&RCC->CFGR, RCC_CFGR_SW_HSI, RCC_CFGR_SW_Msk); + + // Enable external high-speed oscillator (HSE) + set_reg(&RCC->CR, RCC_CR_HSEON, RCC_CR_HSEON_Msk); + rcc_wait_for_osc_ready(RCC_CR_HSERDY); + + // Set voltage scaling + set_reg(&RCC->APB1ENR, RCC_APB1ENR_PWREN, RCC_APB1ENR_PWREN_Msk); + set_reg(&PWR->CR1, setup->voltage_scale, PWR_CR1_VOS_Msk); + + // Overdrive + if (setup->overdrive) { + set_reg(&PWR->CR1, PWR_CR1_ODEN, PWR_CR1_ODEN_Msk); + while (get_reg(&PWR->CSR1, PWR_CSR1_ODRDY_Msk) == 0); + set_reg(&PWR->CR1, PWR_CR1_ODSWEN, PWR_CR1_ODSWEN_Msk); + while (get_reg(&PWR->CSR1, PWR_CSR1_ODSWRDY_Msk) == 0); + } + + // Set prescalers for AHB, APB1, APB2 + set_reg(&RCC->CFGR, setup->hpre | setup->ppre1 | setup->ppre2, + RCC_CFGR_HPRE_Msk | RCC_CFGR_PPRE1_Msk | RCC_CFGR_PPRE2_Msk); + + // Disable PLL oscillator before changing its configuration + set_reg(&RCC->CR, 0, RCC_CR_PLLON_Msk); + + // Configure the PLL oscillator + int pllp_val = (setup->pllp >> 1) - 1; + RCC->PLLCFGR = RCC_PLLCFGR_PLLSRC_HSE + | (setup->pllm << RCC_PLLCFGR_PLLM_Pos) + | (setup->plln << RCC_PLLCFGR_PLLN_Pos) + | (pllp_val << RCC_PLLCFGR_PLLP_Pos) + | (setup->pllq << RCC_PLLCFGR_PLLQ_Pos); + + // Enable PLL oscillator and wait for it to stabilize + set_reg(&RCC->CR, RCC_CR_PLLON, RCC_CR_PLLON_Msk); + rcc_wait_for_osc_ready(RCC_CR_PLLRDY); + + // Configure flash settings + set_reg(&FLASH->ACR, (setup->flash_wait_states << FLASH_ACR_LATENCY_Pos) | FLASH_ACR_ARTEN | FLASH_ACR_PRFTEN, + FLASH_ACR_ARTEN_Msk | FLASH_ACR_PRFTEN_Msk | FLASH_ACR_LATENCY_Msk); + + // Select PLL as SYSCLK source + set_reg(&RCC->CFGR, RCC_CFGR_SW_PLL, RCC_CFGR_SW_Msk); + + // Wait for PLL clock to be selected + while (get_reg(&RCC->CFGR, RCC_CFGR_SWS_Msk) != RCC_CFGR_SWS_PLL) + ; + + // Disable internal high-speed oscillator + set_reg(&RCC->CR, 0, RCC_CR_HSION_Msk); + + // Update the SystemCoreClock variable used by TinyUSB + SystemCoreClockUpdate(); +} + +static volatile uint32_t millis_count; + +static void systick_init(void) { + + // Initialize SysTick + SysTick->CTRL = (SysTick->CTRL & ~SysTick_CTRL_CLKSOURCE_Msk) | SysTick_CTRL_CLKSOURCE_AHB_DIV8; + SysTick->LOAD = SystemCoreClock / 8 / 1000 - 1; + + // Enable and start + SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; + SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; +} + + +// --- Serial number --- + +char board_serial_num[13]; + +const static char HEX_DIGITS[] = "0123456789ABCDEF"; + +static void put_hex(uint32_t value, char *buf, int len) { + for (int idx = 0; idx < len; idx++) { + buf[idx] = HEX_DIGITS[value >> 28]; + value = value << 4; + } +} + +static void usb_init_serial_num() { + __I uint32_t* unique_id =(__I uint32_t*) UID_BASE; + uint32_t id0 = unique_id[0]; + uint32_t id1 = unique_id[1]; + uint32_t id2 = unique_id[2]; + + id0 += id2; + + put_hex(id0, board_serial_num, 8); + put_hex(id1, board_serial_num + 8, 4); + board_serial_num[12] = 0; +} + + +// --- Exported board functions + +void board_init(void) { + + // Enable CPU instruction cache + SCB_EnableICache(); + // Enable CPU data cache + SCB_EnableDCache(); + // Enable ART accelerator + //set_reg(&FLASH->ACR, FLASH_ACR_ARTEN, FLASH_ACR_ARTEN_Msk); + // Enable instruction prefetch + //set_reg(&FLASH->ACR, FLASH_ACR_PRFTEN, FLASH_ACR_PRFTEN_Msk); + + rcc_clock_setup_hse(&clock_setup_hse_216mhz_3v3); + systick_init(); + + // clock for GPIOB (USB pins) + set_reg(&RCC->AHB1ENR, RCC_AHB1ENR_GPIOBEN, RCC_AHB1ENR_GPIOBEN_Msk); + + // Configure USB D+/D- pins + gpio_mode_setup(GPIOB, 14, GPIO_MODE_ALT, GPIO_PUPD_NO_PULL); + gpio_set_af(GPIOB, 14, 10); + gpio_set_ospeed(GPIOB, 14, GPIO_OSPEED_HIGH); + gpio_mode_setup(GPIOB, 15, GPIO_MODE_ALT, GPIO_PUPD_NO_PULL); + gpio_set_af(GPIOB, 15, 10); + gpio_set_ospeed(GPIOB, 15, GPIO_OSPEED_HIGH); + + set_reg(&RCC->APB2ENR, RCC_APB2ENR_OTGPHYCEN, RCC_APB2ENR_OTGPHYCEN_Msk); + set_reg(&RCC->AHB1ENR, RCC_AHB1ENR_OTGHSULPIEN, RCC_AHB1ENR_OTGHSULPIEN_Msk); + set_reg(&RCC->AHB1ENR, RCC_AHB1ENR_OTGHSEN, RCC_AHB1ENR_OTGHSEN_Msk); + +#if 0 + // Configure hardware VBUS sense (using PB13) + set_reg(&USB_OTG_HS->GCCFG, USB_OTG_GCCFG_VBDEN, USB_OTG_GCCFG_VBDEN_Msk); + gpio_mode_setup(GPIOB, 13, GPIO_MODE_INPUT, GPIO_PUPD_NO_PULL); + +#else + // Configure no VBUS sense + set_reg(&USB_OTG_HS->GCCFG, 0, USB_OTG_GCCFG_VBDEN_Msk); + set_reg(&USB_OTG_HS->GOTGCTL, USB_OTG_GOTGCTL_BVALOEN, USB_OTG_GOTGCTL_BVALOEN_Msk); + set_reg(&USB_OTG_HS->GOTGCTL, USB_OTG_GOTGCTL_BVALOVAL, USB_OTG_GOTGCTL_BVALOVAL_Msk); +#endif + + // Force device mode + set_reg(&USB_OTG_HS->GUSBCFG, 0, USB_OTG_GUSBCFG_FHMOD_Msk); + set_reg(&USB_OTG_HS->GUSBCFG, USB_OTG_GUSBCFG_FDMOD, USB_OTG_GUSBCFG_FDMOD_Msk); + + // clock for GPIOB (LED) + set_reg(&RCC->AHB1ENR, RCC_AHB1ENR_GPIOBEN, RCC_AHB1ENR_GPIOBEN_Msk); + + // LED pin + gpio_mode_setup(GPIOB, 1, GPIO_MODE_OUTPUT, GPIO_PUPD_NO_PULL); + + usb_init_serial_num(); +} + +void board_sleep(void) { + // not implemented yet +} + +uint32_t board_millis(void) { + return millis_count; +} + +void board_led_write(bool on) { + if (on) + gpio_set(GPIOB, 1); + else + gpio_clear(GPIOB, 1); +} + + +// --- Interrupt handlers --- + +void SysTick_Handler (void) { + millis_count++; +} + +void OTG_HS_IRQHandler(void) { + tud_int_handler(1); +} + +#endif diff --git a/test-devices/loopback-stm32/src/common.cpp b/test-devices/loopback-stm32/src/common.cpp deleted file mode 100644 index 5102b6bd..00000000 --- a/test-devices/loopback-stm32/src/common.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// -// Java Does USB -// Loopback device for testing -// -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// -// Commmon functions -// - -#include "common.h" - -#include - -static volatile uint32_t millis_count; - -uint32_t millis() { return millis_count; } - -void delay(uint32_t ms) { - int32_t target_time = millis_count + ms; - while (target_time - (int32_t)millis_count > 0) - ; -} - -void systick_init() { - // Initialize SysTick - systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8); - systick_set_reload(rcc_ahb_frequency / 8 / 1000 - 1); - - // Enable and start - systick_interrupt_enable(); - systick_counter_enable(); -} - -// System tick timer interrupt handler -extern "C" void sys_tick_handler() { millis_count++; } diff --git a/test-devices/loopback-stm32/src/main.c b/test-devices/loopback-stm32/src/main.c new file mode 100644 index 00000000..d826197e --- /dev/null +++ b/test-devices/loopback-stm32/src/main.c @@ -0,0 +1,336 @@ +// +// Java Does USB +// Loopback device for testing +// +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Board specific function (HAL) +// + +#include +#include +#include + +#include "board.h" +#include "tusb.h" +#include "usb_descriptors.h" +#include "vendor_custom.h" + +#if BOARD_TUD_MAX_SPEED == OPT_MODE_HIGH_SPEED + #define BUFFER_SIZE 16384 +#else + #define BUFFER_SIZE 2048 +#endif + +// FIFO buffer for loopback data +tu_fifo_t loopback_fifo; +uint8_t loopback_buffer[BUFFER_SIZE] __attribute__ ((aligned(4))); +bool delay_loopback_reset = false; + +uint16_t bulk_packet_size = 64; +const int num_rx_packets = 2; +const int num_tx_packets = 4; + +// buffer for echoed packet +uint8_t echo_buffer[16]; +int echo_buffer_len; +int num_echos; + + +static bool is_blinking = true; +static uint32_t led_on_until = 0; +static uint32_t blink_toogle_at = 0; +static bool is_blink_on = true; + +static inline bool has_expired(uint32_t deadline, uint32_t now) { + return (int32_t)(now - deadline) >= 0; +} + +static void led_busy(void); +static void led_blinking_task(void); +static void loopback_init(void); +static void loopback_check_rx(void); +static void loopback_check_tx(void); +static void echo_update_state(void); +static void reset_buffers(void); + + +int main(void) { + + board_init(); + loopback_init(); + + // init device stack + tud_init(BOARD_TUD_RHPORT); + + while (1) { + tud_task(); + led_blinking_task(); + } + + return 0; +} + +// reset device in predictable state +void reset_buffers(void) { + if (cust_vendor_is_transmitting(EP_LOOPBACK_TX)) { + delay_loopback_reset = true; + } else { + tu_fifo_clear(&loopback_fifo); + } + + num_echos = 0; +} + +// --- Loopback + +void loopback_init(void) { + tu_fifo_config(&loopback_fifo, loopback_buffer, sizeof(loopback_buffer), 1, false); +} + +// Check if the next transmission should be started +void loopback_check_tx(void) { + + if (delay_loopback_reset) { + tu_fifo_clear(&loopback_fifo); + delay_loopback_reset = false; + } + + uint16_t n = tu_fifo_count(&loopback_fifo); + + if (n > 0 && !cust_vendor_is_transmitting(EP_LOOPBACK_TX)) { + uint16_t max_size = num_tx_packets * bulk_packet_size; + if (n > max_size) + n = max_size; + + cust_vendor_start_transmit_fifo(EP_LOOPBACK_TX, &loopback_fifo, n); + led_busy(); + } +} + +// Check if receiving should be started again +void loopback_check_rx(void) { + + uint16_t n = tu_fifo_remaining(&loopback_fifo); + if (n >= num_rx_packets * bulk_packet_size && !cust_vendor_is_receiving(EP_LOOPBACK_RX)) + cust_vendor_prepare_recv_fifo(EP_LOOPBACK_RX, &loopback_fifo, num_rx_packets * bulk_packet_size); +} + + +// --- Echo + +void echo_update_state(void) { + if (num_echos > 0) { + cust_vendor_start_transmit(EP_ECHO_TX, echo_buffer, echo_buffer_len); + led_busy(); + } else { + cust_vendor_prepare_recv(EP_ECHO_RX, echo_buffer, sizeof(echo_buffer)); + } +} + + +// --- Vendor class callbacks + +// Invoked when new data has been received +void cust_vendor_rx_cb(uint8_t ep_addr, uint32_t recv_bytes) { + if (ep_addr == EP_LOOPBACK_RX) { + loopback_check_rx(); + loopback_check_tx(); + + } else if (ep_addr == EP_ECHO_RX) { + num_echos = 2; + echo_buffer_len = recv_bytes; + echo_update_state(); + } + led_busy(); +} + +// Invoked when last tx transfer finished +void cust_vendor_tx_cb(uint8_t ep_addr, uint32_t sent_bytes) { + if (ep_addr == EP_LOOPBACK_TX) { + loopback_check_tx(); + loopback_check_rx(); + + // check ZLP + if (sent_bytes > 0 + && (sent_bytes & (bulk_packet_size - 1)) == 0 + && !cust_vendor_is_transmitting(ep_addr)) { + cust_vendor_start_transmit(EP_LOOPBACK_TX, NULL, 0); + led_busy(); + } + + } else if (ep_addr == EP_ECHO_TX) { + num_echos--; + echo_update_state(); + } +} + +// Invoked when interface has been opened +void cust_vendor_intf_open_cb(uint8_t intf) { + bulk_packet_size = cust_vendor_packet_size(EP_LOOPBACK_RX); + loopback_check_rx(); + echo_update_state(); + led_busy(); +} + +// Invoked when an alternate interface has been selected +void cust_vendor_alt_intf_selected_cb(uint8_t intf, uint8_t alt) { + reset_buffers(); + bulk_packet_size = cust_vendor_packet_size(EP_LOOPBACK_RX); + loopback_check_rx(); + if (alt == 0) + echo_update_state(); + led_busy(); +} + +void cust_vendor_halt_cleared_cb(uint8_t ep_addr) { + switch (ep_addr) { + case EP_LOOPBACK_RX: + loopback_check_rx(); + break; + case EP_LOOPBACK_TX: + loopback_check_tx(); + break; + case EP_ECHO_RX: + if (num_echos == 0) + echo_update_state(); + break; + case EP_ECHO_TX: + if (num_echos > 0) + echo_update_state(); + break; + default: + break; + } + led_busy(); +} + + +// --- Control messages (see README) + +#define REQUEST_SAVE_VALUE 0x01 +#define REQUEST_SAVE_DATA 0x02 +#define REQUEST_SEND_DATA 0x03 +#define REQUEST_RESET_BUFFERS 0x04 +#define REQUEST_GET_INTF_NUM 0x05 + +static uint32_t saved_value = 0; + +bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) { + if (stage != CONTROL_STAGE_SETUP) + return true; // nothing to do + + if (request->bmRequestType_bit.type != TUSB_REQ_TYPE_VENDOR) + return false; // stall unknown request + + switch (request->bRequest) { + + case REQUEST_SAVE_VALUE: + if (request->bmRequestType_bit.direction == TUSB_DIR_OUT && request->wLength == 0) { + // save value from wValue + saved_value = request->wValue; + led_busy(); + return tud_control_status(rhport, request); + } + break; + + case REQUEST_SAVE_DATA: + if (request->bmRequestType_bit.direction == TUSB_DIR_OUT && request->wLength == 4) { + // receive into `saved_value` + led_busy(); + return tud_control_xfer(rhport, request, &saved_value, 4); + } + break; + + case REQUEST_SEND_DATA: + if (request->bmRequestType_bit.direction == TUSB_DIR_IN && request->wLength == 4) { + // transmit from `saved_value` + led_busy(); + return tud_control_xfer(rhport, request, &saved_value, 4); + } + break; + + case REQUEST_RESET_BUFFERS: + if (request->bmRequestType_bit.direction == TUSB_DIR_OUT && request->wLength == 0) { + reset_buffers(); + led_busy(); + return tud_control_status(rhport, request); + } + break; + + case REQUEST_GET_INTF_NUM: + if (request->bmRequestType_bit.direction == TUSB_DIR_IN && request->wLength == 1) { + uint8_t intf_num = request->wIndex & 0xff; + if (intf_num < 4) { + led_busy(); + // return inteface number + return tud_control_xfer(rhport, request, &intf_num, 1); + } + } + break; + + // Microsoft WCID descriptor (for automatic WinUSB installation) + case WCID_VENDOR_CODE: + if (request->bmRequestType_bit.direction == TUSB_DIR_IN && request->wIndex == 0x0004) { + led_busy(); + // transmit WCID feature descriptor + int len = sizeof(wcid_feature_desc); + if (len >= request->wLength) + len = request->wLength; + return tud_control_xfer(rhport, request, (void*) wcid_feature_desc, len); + } + + default: + break; + } + + // stall unknown request + return false; +} + + +// --- Device callbacks + +// Register additional driver +usbd_class_driver_t const* usbd_app_driver_get_cb(uint8_t* driver_count) { + *driver_count = 1; + return &cust_vendor_driver; +} + + +// Invoked when device is mounted +void tud_mount_cb(void) { + is_blinking = false; +} + +// Invoked when usb bus is suspended +// remote_wakeup_en: if host allow us to perform remote wakeup +// Within 7ms, device must draw an average of current less than 2.5 mA from bus +void tud_suspend_cb(bool remote_wakeup_en) { + (void) remote_wakeup_en; + board_sleep(); +} + + +// --- LED blinking --- + +void led_busy(void) { + led_on_until = board_millis() + 100; + board_led_write(true); +} + +void led_blinking_task(void) { + uint32_t now = board_millis(); + if (is_blinking) { + if (has_expired(blink_toogle_at, now)) { + is_blink_on = !is_blink_on; + blink_toogle_at = now + 250; + } + board_led_write(is_blink_on && (now & 7) == 0); + + } else if (has_expired(led_on_until, now)) { + board_led_write((now & 3) == 0); + } +} diff --git a/test-devices/loopback-stm32/src/main.cpp b/test-devices/loopback-stm32/src/main.cpp deleted file mode 100644 index 8d049e4a..00000000 --- a/test-devices/loopback-stm32/src/main.cpp +++ /dev/null @@ -1,233 +0,0 @@ -// -// Java Does USB -// Loopback device for testing -// -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// -// Main program -// - -#include -#include -#include -#include - -#include - -#include "circ_buf.h" -#include "common.h" -#include "usb_descriptor.h" -#include "wcid.h" - -static void on_usb_set_config(usbd_device *usbd_dev, uint16_t wValue); -static usbd_request_return_codes on_usb_control_request(usbd_device *usbd_dev, usb_setup_data *req, uint8_t **buf, uint16_t *len, usbd_control_complete_callback *complete); -static void on_usb_loopback_received(usbd_device *usbd_dev, uint8_t ep); -static void on_usb_loopback_transmitted(usbd_device *usbd_dev, uint8_t ep); -static void on_usb_echo_received(usbd_device *usbd_dev, uint8_t ep); -static void on_usb_echo_transmitted(usbd_device *usbd_dev, uint8_t ep); -static void check_buffers(); - -// USB device instance -static usbd_device *usb_device; - -// buffer for control requests -static uint8_t usbd_control_buffer[256]; - -// Circular buffer for data -static circ_buf<1024> buffer; - -// Minimum free space in circular buffer for requesting more packets -static constexpr int MIN_FREE_SPACE = 2 * BULK_MAX_PACKET_SIZE; - -// indicates if loopback data is being transmitted -static bool is_loopback_tx = false; - -// indicates if the loopback RX endpoint is forced to NAK to prevent receiving further data -static bool is_loopback_rx_nak = false; - -// value that can be saved and retrieved with control requests -static uint32_t saved_value; - -// echo message -static char echo_msg[INTR_MAX_PACKET_SIZE]; - -// echo message length -static int echo_msg_len; - -// number of echos left to transmit (if > 1, RX endpointed is NAKed) -static int num_echos_left; - - -void init() { - // Enable required clocks - rcc_clock_setup_in_hse_8mhz_out_72mhz(); - rcc_periph_clock_enable(RCC_GPIOA); - rcc_periph_clock_enable(RCC_GPIOC); - rcc_periph_clock_enable(RCC_AFIO); - rcc_periph_clock_enable(RCC_SPI1); - rcc_periph_clock_enable(RCC_USB); - - // Initialize systick services - systick_init(); -} - -void usb_init() { - // reset USB peripheral - rcc_periph_reset_pulse(RST_USB); - - // Pull USB D+ (A12) low for 80ms to trigger device reenumeration - gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO12); - gpio_clear(GPIOA, GPIO12); - delay(80); - - usb_init_serial_num(); - - // create USB device - usb_device = usbd_init(&st_usbfs_v1_usb_driver, &usb_device_desc, usb_config_descs, usb_desc_strings, - sizeof(usb_desc_strings) / sizeof(usb_desc_strings[0]), usbd_control_buffer, - sizeof(usbd_control_buffer)); - - // Set callback for config calls - usbd_register_set_config_callback(usb_device, on_usb_set_config); - register_wcid_desc(usb_device); -} - -// Called when the host connects to the device and selects a configuration -void on_usb_set_config(usbd_device *usbd_dev, __attribute__((unused)) uint16_t wValue) { - register_wcid_desc(usbd_dev); - usbd_register_control_callback(usbd_dev, - USB_REQ_TYPE_VENDOR | USB_REQ_TYPE_INTERFACE, - USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, - on_usb_control_request); - usbd_ep_setup(usbd_dev, EP_LOOPBACK_RX, USB_ENDPOINT_ATTR_BULK, BULK_MAX_PACKET_SIZE, on_usb_loopback_received); - usbd_ep_setup(usbd_dev, EP_LOOPBACK_TX, USB_ENDPOINT_ATTR_BULK, BULK_MAX_PACKET_SIZE, on_usb_loopback_transmitted); - usbd_ep_setup(usbd_dev, EP_ECHO_RX, USB_ENDPOINT_ATTR_INTERRUPT, INTR_MAX_PACKET_SIZE, on_usb_echo_received); - usbd_ep_setup(usbd_dev, EP_ECHO_TX, USB_ENDPOINT_ATTR_INTERRUPT, INTR_MAX_PACKET_SIZE, on_usb_echo_transmitted); - - buffer.reset(); - is_loopback_tx = false; - is_loopback_rx_nak = false; - num_echos_left = 0; - echo_msg_len = 0; - saved_value = 0; -} - -// Called when loopback data has been received -void on_usb_loopback_received(usbd_device *usbd_dev, uint8_t ep) { - // Retrieve USB data (has side effect of setting endpoint to VALID) - uint8_t packet[BULK_MAX_PACKET_SIZE] __attribute__((aligned(4))); - int len = usbd_ep_read_packet(usbd_dev, ep, packet, sizeof(packet)); - - // copy data into circular buffer - buffer.add_data(packet, len); - -} - -// Called when loopback data has been transmitted -void on_usb_loopback_transmitted(__attribute__((unused)) usbd_device *usbd_dev, __attribute__((unused)) uint8_t ep) { - is_loopback_tx = false; -} - -void check_buffers() { - - // If RX is stopped and there is sufficient space in the buffer, resume it - if (is_loopback_rx_nak) { - if (buffer.avail_size() >= MIN_FREE_SPACE) { - usbd_ep_nak_set(usb_device, EP_LOOPBACK_RX, 0); - is_loopback_rx_nak = false; - } - - // If RX is enabled but the space in the buffer is low, stop it - } else { - // check if there is space for less than 2 packets - if (buffer.avail_size() < MIN_FREE_SPACE) { - // set endpoint from VALID to NAK - usbd_ep_nak_set(usb_device, EP_LOOPBACK_RX, 1); - is_loopback_rx_nak = true; - } - } - - // If no data is being transmitted and there is data in the buffer, transmit a packet - if (!is_loopback_tx && buffer.data_size() >= 0) { - uint8_t packet[BULK_MAX_PACKET_SIZE] __attribute__((aligned(4))); - int len = buffer.get_data(packet, BULK_MAX_PACKET_SIZE); - usbd_ep_write_packet(usb_device, EP_LOOPBACK_TX, packet, len); - is_loopback_tx = true; - } -} - -// Called when echo data has been received -void on_usb_echo_received(usbd_device *usbd_dev,uint8_t ep) { - // Retrieve USB data (has side effect of setting endpoint to VALID) - echo_msg_len = usbd_ep_read_packet(usbd_dev, EP_ECHO_RX, echo_msg, sizeof(echo_msg)); - usbd_ep_nak_set(usbd_dev, ep, 1); - - usbd_ep_write_packet(usbd_dev, EP_ECHO_TX, echo_msg, echo_msg_len); - num_echos_left = 2; -} - -// Called when echo data has been transmitted -void on_usb_echo_transmitted(__attribute__((unused)) usbd_device *usbd_dev, __attribute__((unused)) uint8_t ep) { - num_echos_left--; - if (num_echos_left > 0) { - usbd_ep_write_packet(usbd_dev, ep, echo_msg, echo_msg_len); - } else { - usbd_ep_nak_set(usbd_dev, EP_ECHO_RX, 0); - } -} - - -usbd_request_return_codes on_usb_control_request( - __attribute__((unused)) usbd_device *usbd_dev, usb_setup_data *req, - uint8_t **buf, uint16_t *len, __attribute__((unused)) usbd_control_complete_callback *complete) { - - switch (req->bRequest) { - case 1: - if (req->wIndex == 0 && req->wLength == 0) { - saved_value = req->wValue; - return USBD_REQ_HANDLED; - } else { - return USBD_REQ_NOTSUPP; - } - break; - - case 2: - if (req->wIndex == 0 && req->wLength == 4) { - uint32_t* value = reinterpret_cast(*buf); - saved_value = *value; - return USBD_REQ_HANDLED; - } else { - return USBD_REQ_NOTSUPP; - } - break; - - case 3: - if (req->wIndex == 0) { - uint8_t* value = reinterpret_cast(&saved_value); - *len = std::min(*len, (uint16_t) 4); - memcpy(*buf, value, *len); - return USBD_REQ_HANDLED; - } else { - return USBD_REQ_NOTSUPP; - } - break; - - default: - ; // fall through - } - - return USBD_REQ_NEXT_CALLBACK; -} - - -int main() { - init(); - usb_init(); - - while (true) { - usbd_poll(usb_device); - check_buffers(); - } -} diff --git a/test-devices/loopback-stm32/src/usb_descriptor.cpp b/test-devices/loopback-stm32/src/usb_descriptor.cpp deleted file mode 100644 index 7c26184c..00000000 --- a/test-devices/loopback-stm32/src/usb_descriptor.cpp +++ /dev/null @@ -1,158 +0,0 @@ -// -// Java Does USB -// Loopback device for testing -// -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// -// USB descriptor -// - -#include "usb_descriptor.h" - -#include - -static void put_hex(uint32_t value, char *buf, int len); - -#define USB_VID 0xcafe // Vendor ID -#define USB_PID 0xceaf // Product ID -#define USB_DEVICE_REL 0x0071 // release 0.7.1 - -// Interface index -#define INTF_LOOPBACK 0 - -static char serial_num[13]; - -const char *const usb_desc_strings[] = { - "JavaDoesUSB", // USB Manufacturer - "Loopback", // USB Product - serial_num, // Serial number -}; - -enum usb_strings_index { // Index of USB strings. Must sync with above, starts from 1. - USB_STRINGS_MANUFACTURER_ID = 1, - USB_STRINGS_PRODUCT_ID, - USB_STRINGS_SERIAL_NUMBER_ID, -}; - -static const struct usb_endpoint_descriptor comm_endpoint_descs[] = { - { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_LOOPBACK_RX, - .bmAttributes = USB_ENDPOINT_ATTR_BULK, - .wMaxPacketSize = BULK_MAX_PACKET_SIZE, - .bInterval = 0, - .extra = nullptr, - .extralen = 0, - }, - { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_LOOPBACK_TX, - .bmAttributes = USB_ENDPOINT_ATTR_BULK, - .wMaxPacketSize = BULK_MAX_PACKET_SIZE, - .bInterval = 0, - .extra = nullptr, - .extralen = 0, - }, - { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_ECHO_RX, - .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, - .wMaxPacketSize = INTR_MAX_PACKET_SIZE, - .bInterval = 16, - .extra = nullptr, - .extralen = 0, - }, - { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_ECHO_TX, - .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, - .wMaxPacketSize = INTR_MAX_PACKET_SIZE, - .bInterval = 16, - .extra = nullptr, - .extralen = 0, - }, -}; - -static const struct usb_interface_descriptor comm_if_descs[] = { - { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = INTF_LOOPBACK, - .bAlternateSetting = 0, - .bNumEndpoints = sizeof(comm_endpoint_descs) / sizeof(comm_endpoint_descs[0]), - .bInterfaceClass = USB_CLASS_VENDOR, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, // vendor specific - .iInterface = 0, - .endpoint = comm_endpoint_descs, - .extra = nullptr, - .extralen = 0, - }, -}; - -static const struct usb_interface usb_interfaces[] = { - { - .cur_altsetting = nullptr, - .num_altsetting = sizeof(comm_if_descs) / sizeof(comm_if_descs[0]), - .iface_assoc = nullptr, - .altsetting = comm_if_descs, - }, -}; - -const struct usb_config_descriptor usb_config_descs[] = { - { - .bLength = USB_DT_CONFIGURATION_SIZE, - .bDescriptorType = USB_DT_CONFIGURATION, - .wTotalLength = 0, - .bNumInterfaces = sizeof(usb_interfaces) / sizeof(usb_interfaces[0]), - .bConfigurationValue = 1, - .iConfiguration = 0, - .bmAttributes = 0x80, // bus-powered, i.e. it draws power from USB bus - .bMaxPower = 0xfa, // 500 mA - .interface = usb_interfaces, - }, -}; - -const struct usb_device_descriptor usb_device_desc = { - .bLength = USB_DT_DEVICE_SIZE, - .bDescriptorType = USB_DT_DEVICE, - .bcdUSB = 0x0200, // USB version 2.00 - .bDeviceClass = USB_CLASS_VENDOR, - .bDeviceSubClass = 0, - .bDeviceProtocol = 0, // no class specific protocol - .bMaxPacketSize0 = BULK_MAX_PACKET_SIZE, - .idVendor = USB_VID, - .idProduct = USB_PID, - .bcdDevice = USB_DEVICE_REL, - .iManufacturer = USB_STRINGS_MANUFACTURER_ID, - .iProduct = USB_STRINGS_PRODUCT_ID, - .iSerialNumber = USB_STRINGS_SERIAL_NUMBER_ID, - .bNumConfigurations = sizeof(usb_config_descs) / sizeof(usb_config_descs[0]), -}; - -void usb_init_serial_num() { - uint32_t id0 = DESIG_UNIQUE_ID0; - uint32_t id1 = DESIG_UNIQUE_ID1; - uint32_t id2 = DESIG_UNIQUE_ID2; - - id0 += id2; - - put_hex(id0, serial_num, 8); - put_hex(id1, serial_num + 8, 4); - serial_num[12] = 0; -} - -const static char HEX_DIGITS[] = "0123456789ABCDEF"; - -void put_hex(uint32_t value, char *buf, int len) { - for (int idx = 0; idx < len; idx++) { - buf[idx] = HEX_DIGITS[value >> 28]; - value = value << 4; - } -} diff --git a/test-devices/loopback-stm32/src/usb_descriptors.c b/test-devices/loopback-stm32/src/usb_descriptors.c new file mode 100644 index 00000000..9aa5028e --- /dev/null +++ b/test-devices/loopback-stm32/src/usb_descriptors.c @@ -0,0 +1,225 @@ +// +// Java Does USB +// Loopback device for testing +// +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Board specific function (HAL) +// + +#include "usb_descriptors.h" + +#include "board.h" +#include "tusb.h" +#include "vendor_custom.h" + + +// --- Device Descriptor --- + +tusb_desc_device_t const desc_device = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + + .bDeviceClass = TUSB_CLASS_VENDOR_SPECIFIC, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = 0xCAFE, + .idProduct = 0xCEAF, + .bcdDevice = 0x0074, // version 0.7.4 + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01 +}; + +// Invoked when a GET DEVICE DESCRIPTOR request is received. +// Return a pointer to the descriptor. +uint8_t const* tud_descriptor_device_cb(void) { + return (uint8_t const*)&desc_device; +} + + +// --- Configuration Descriptor --- + +enum { + INTF_LOOPBACK = 0, + INTF_NUM_TOTAL +}; + +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + 9 + 4 * 7 + 9 + 2 * 7) + +uint8_t const desc_fs_configuration[] = { + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, INTF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x80, 500), + // Loopback interface (alternate 0) + CUSTOM_VENDOR_INTERFACE(0, 4), + // Loopback endpoint OUT + CUSTOM_VENDOR_BULK_ENDPOINT(EP_LOOPBACK_RX, 64), + // Loopback endpoint IN + CUSTOM_VENDOR_BULK_ENDPOINT(EP_LOOPBACK_TX, 64), + // Echo endpoint OUT + CUSTOM_VENDOR_INTERRUPT_ENDPOINT(EP_ECHO_RX, INTR_MAX_PACKET_SIZE, 16), + // Echo endpoint IN + CUSTOM_VENDOR_INTERRUPT_ENDPOINT(EP_ECHO_TX, INTR_MAX_PACKET_SIZE, 16), + // Loopback interface (alternate 1) + CUSTOM_VENDOR_INTERFACE_ALT(0, 1, 2), + // Loopback endpoint OUT + CUSTOM_VENDOR_BULK_ENDPOINT(EP_LOOPBACK_RX, 64), + // Loopback endpoint IN + CUSTOM_VENDOR_BULK_ENDPOINT(EP_LOOPBACK_TX, 64) +}; + +#if TUD_OPT_HIGH_SPEED + +uint8_t const desc_hs_configuration[] = { + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, INTF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 500), + // Loopback interface (alternate 0) + CUSTOM_VENDOR_INTERFACE(0, 4), + // Loopback endpoint OUT + CUSTOM_VENDOR_BULK_ENDPOINT(EP_LOOPBACK_RX, 512), + // Loopback endpoint IN + CUSTOM_VENDOR_BULK_ENDPOINT(EP_LOOPBACK_TX, 512), + // Echo endpoint OUT + CUSTOM_VENDOR_INTERRUPT_ENDPOINT(EP_ECHO_RX, INTR_MAX_PACKET_SIZE, 8), + // Echo endpoint IN + CUSTOM_VENDOR_INTERRUPT_ENDPOINT(EP_ECHO_TX, INTR_MAX_PACKET_SIZE, 8), + // Loopback interface (alternate 1) + CUSTOM_VENDOR_INTERFACE_ALT(0, 1, 2), + // Loopback endpoint OUT + CUSTOM_VENDOR_BULK_ENDPOINT(EP_LOOPBACK_RX, 512), + // Loopback endpoint IN + CUSTOM_VENDOR_BULK_ENDPOINT(EP_LOOPBACK_TX, 512) +}; + +// device qualifier (same values as device descriptor where possible) +tusb_desc_device_qualifier_t const desc_device_qualifier = +{ + .bLength = sizeof(tusb_desc_device_qualifier_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + + .bDeviceClass = TUSB_CLASS_VENDOR_SPECIFIC, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .bNumConfigurations = 0x01, + .bReserved = 0x00 +}; + +// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete. +// device_qualifier descriptor describes information about a high-speed capable device that would +// change if the device were operating at the other speed. If not highspeed capable stall this request. +uint8_t const* tud_descriptor_device_qualifier_cb(void) +{ + return (uint8_t const*) &desc_device_qualifier; +} + +// Invoked when received GET OTHER SPEED CONFIGURATION DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa +uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) +{ + (void) index; // for multiple configurations + + // if link speed is high return fullspeed config, and vice versa + return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration; +} + +#endif + + +// Invoked when a GET CONFIGURATION DESCRIPTOR request is recieved. +// Return a pointer to descriptor. +// Descriptor contents must exist long enough for transfer to complete +uint8_t const* tud_descriptor_configuration_cb(uint8_t configuration_index) { + (void)configuration_index; + +#if TUD_OPT_HIGH_SPEED + // Although we are highspeed, host may be fullspeed. + return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration; +#else + return desc_fs_configuration; +#endif +} + + +// --- String Descriptors --- + +// table with strings +const char* const string_table[] = { + 0, // 0 - supported languages (see below) + "JavaDoesUSB", // 1 - manufacturer + "Loopback", // 2 - product + board_serial_num // 3 - serial number +}; + +// Microsoft WCID (Microsoft OS 1.0 Descriptors) string descriptor (for string index 0xee) +// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors +static const uint8_t msft_sig_desc[] = { + 0x12, // length = 18 bytes + TUSB_DESC_STRING, // descriptor type string + 'M', 0, 'S', 0, 'F', 0, 'T', 0, // 'M', 'S', 'F', 'T' + '1', 0, '0', 0, '0', 0, // '1', '0', '0' + WCID_VENDOR_CODE, // vendor code + 0 // padding +}; + + +static uint16_t str_desc_buf[32]; + +// Invoked when a GET STRING DESCRIPTOR request is received. +// Return pointer to string descriptor. +uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) { + (void)langid; + + int str_len; + + if (index == 0) { + str_desc_buf[1] = 0x0409; // US English + str_len = 1; + + } else if (index == 0xee) { + return (const uint16_t*) msft_sig_desc; + + } else { + if (index >= TU_ARRAY_SIZE(string_table)) + return NULL; + + const char* str = string_table[index]; + str_len = strlen(str); + + // Convert ASCII to UTF-16 + for (uint8_t i = 0; i < str_len; i++) + str_desc_buf[1 + i] = str[i]; + } + + // first byte is length (including header), second byte is string type + str_desc_buf[0] = (uint16_t)((2 * str_len + 2) | (TUSB_DESC_STRING << 8)); + + return str_desc_buf; +} + +// --- Microsoft WCID feature descriptor --- + +const uint8_t wcid_feature_desc[] = { + 0x28, 0x00, 0x00, 0x00, // length = 40 bytes + 0x00, 0x01, // version 1.0 (in BCD) + 0x04, 0x00, // compatibility descriptor index 0x0004 + 0x01, // number of sections + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved (7 bytes) + 0x00, // interface number 0 + 0x01, // reserved + 0x57, 0x49, 0x4E, 0x55, 0x53, 0x42, 0x00, 0x00, // Compatible ID "WINUSB\0\0" + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Subcompatible ID (unused) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // reserved 6 bytes +}; diff --git a/test-devices/loopback-stm32/include/usb_descriptor.h b/test-devices/loopback-stm32/src/usb_descriptors.h similarity index 54% rename from test-devices/loopback-stm32/include/usb_descriptor.h rename to test-devices/loopback-stm32/src/usb_descriptors.h index d200756a..ad7af02b 100644 --- a/test-devices/loopback-stm32/include/usb_descriptor.h +++ b/test-devices/loopback-stm32/src/usb_descriptors.h @@ -11,10 +11,9 @@ #pragma once -#include +#include #define INTR_MAX_PACKET_SIZE 16 -#define BULK_MAX_PACKET_SIZE 64 // Endpoints #define EP_LOOPBACK_RX 0x01 @@ -22,11 +21,9 @@ #define EP_ECHO_RX 0x03 #define EP_ECHO_TX 0x83 -// USB descriptor string table -extern const char *const usb_desc_strings[4]; -// USB device descriptor -extern const struct usb_device_descriptor usb_device_desc; -// USB device configurations -extern const struct usb_config_descriptor usb_config_descs[]; +#define WCID_VENDOR_CODE 0x37 + +const uint8_t wcid_feature_desc[40]; + void usb_init_serial_num(); diff --git a/test-devices/loopback-stm32/src/vendor_custom.c b/test-devices/loopback-stm32/src/vendor_custom.c new file mode 100644 index 00000000..cebf47b7 --- /dev/null +++ b/test-devices/loopback-stm32/src/vendor_custom.c @@ -0,0 +1,246 @@ +// +// Java Does USB +// Loopback device for testing +// +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// USB driver for interfaces with vendor specific class. +// The interface can have any number of Bulk and Interrupt endpoints. +// +// Alternate interfaces are supported. +// + +#include "tusb_option.h" + +#if (CFG_TUD_ENABLED && CFG_VENDOR_CUSTOM) + +#include "device/usbd.h" +#include "vendor_custom.h" + +void dcd_edpt_close_all(uint8_t rhport); + + +static void cv_init(void); +static void cv_reset(uint8_t rhport); +static uint16_t cv_open(uint8_t rhport, tusb_desc_interface_t const* desc_intf, uint16_t max_len); +static bool cv_control_xfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); +static bool cv_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); +static uint16_t setup_endpoints(uint8_t rhport, tusb_desc_interface_t const* desc_intf, uint16_t max_len, uint8_t alt_num); +static void close_endpoints(); + +const usbd_class_driver_t cust_vendor_driver = { + .init = cv_init, + .reset = cv_reset, + .open = cv_open, + .control_xfer_cb = cv_control_xfer, + .xfer_cb = cv_xfer_cb, + .sof = NULL +}; + +// open endpoints +static uint8_t cv_eps_open[8]; +static uint16_t cv_eps_packet_size[8]; +static int cv_num_eps_open; + +// current alternate setting +static uint8_t cv_alternate_setting; + +// interface descriptor (length covers all alternate interfaces and endpoints) +static tusb_desc_interface_t const * cv_intf_desc; +static uint16_t cv_intf_desc_len; + + +void cv_init(void) { + // nothing to do +} + +void cv_reset(uint8_t rhport) { + // nothing to do +} + +// Open interface if the descriptor matches this class +uint16_t cv_open(uint8_t rhport, tusb_desc_interface_t const * desc_intf, uint16_t max_len) { + + cv_num_eps_open = 0; + int ret = setup_endpoints(rhport, desc_intf, max_len, 0); + + if (ret != 0) + cust_vendor_intf_open_cb(desc_intf->bInterfaceNumber); + + return ret; +} + +// Setup endpoints for the given alternate interface. +// Endpoints details are extracted from the configuration descriptor +// returns: number of processed bytes +uint16_t setup_endpoints(uint8_t rhport, tusb_desc_interface_t const* desc_intf, uint16_t max_len, uint8_t alt_num) { + + uint8_t const * p_desc = (uint8_t const *) desc_intf; + uint8_t const * p_desc_end = p_desc + max_len; + + // process the interface descriptor including all alternate interface descriptors + while (p_desc < p_desc_end) { + + // check for interface descriptor with class "vendor specific" + tusb_desc_interface_t const * desc_if = (tusb_desc_interface_t const *) p_desc; + if (desc_if->bDescriptorType != TUSB_DESC_INTERFACE || desc_if->bInterfaceClass != TUSB_CLASS_VENDOR_SPECIFIC + || desc_if->bInterfaceNumber != desc_intf->bInterfaceNumber) + break; + + uint8_t curr_alt_num = desc_if->bAlternateSetting; + if (curr_alt_num == alt_num) { + // desired alternate interface found + close_endpoints(); + cv_alternate_setting = alt_num; + } + + p_desc = tu_desc_next(p_desc); + + // iterate endpoints + while (p_desc < p_desc_end) { + tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc; + if (desc_ep->bDescriptorType != TUSB_DESC_ENDPOINT) + break; + + // open endpoint if it is for selected alternate setting + if (curr_alt_num == alt_num) { + TU_ASSERT(usbd_edpt_open(rhport, desc_ep)); + + // remember open endpoints + cv_eps_open[cv_num_eps_open] = desc_ep->bEndpointAddress; + cv_eps_packet_size[cv_num_eps_open] = desc_ep->wMaxPacketSize; + cv_num_eps_open += 1; + } + + p_desc = tu_desc_next(p_desc); + } + } + + uint16_t processed_bytes = p_desc - (uint8_t const *) desc_intf; + + if (processed_bytes > 0) { + // remember interface descriptor + cv_intf_desc = desc_intf; + cv_intf_desc_len = processed_bytes; + } + + return processed_bytes; +} + +void close_endpoints() { + + uint8_t const rhport = BOARD_TUD_RHPORT; + + // close in reverse order + while (cv_num_eps_open > 0) { + cv_num_eps_open -= 1; + usbd_edpt_close(rhport, cv_eps_open[cv_num_eps_open]); + } + + dcd_edpt_close_all(rhport); +} + +bool cv_control_xfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) { + TU_VERIFY(TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type); + + if (request->bRequest == TUSB_REQ_GET_INTERFACE) { + if (stage == CONTROL_STAGE_SETUP) { + tud_control_xfer(rhport, request, &cv_alternate_setting, 1); + } + return true; // indicate that request has been handled + + } else if (request->bRequest == TUSB_REQ_SET_INTERFACE) { + if (stage == CONTROL_STAGE_SETUP) { + uint8_t alt_num = request->wValue; + setup_endpoints(rhport, cv_intf_desc, cv_intf_desc_len, alt_num); + if (cust_vendor_alt_intf_selected_cb != NULL) + cust_vendor_alt_intf_selected_cb((uint8_t) request->wIndex, alt_num); + tud_control_status(rhport, request); + } + return true; // indicate that request has been handled + + } else if (request->bRequest == TUSB_REQ_CLEAR_FEATURE + && request->wValue == TUSB_REQ_FEATURE_EDPT_HALT + && request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_ENDPOINT + && cust_vendor_halt_cleared_cb != NULL) { + uint8_t const ep_addr = tu_u16_low(request->wIndex); + cust_vendor_halt_cleared_cb(ep_addr); + return true; // ignored by caller + } + + return false; +} + +bool cv_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { + + if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { + cust_vendor_tx_cb(ep_addr, xferred_bytes); + + } else { + cust_vendor_rx_cb(ep_addr, xferred_bytes); + } + + return true; +} + +void cust_vendor_prepare_recv(uint8_t ep_addr, void* buf, uint32_t buf_len) { + + uint8_t const rhport = BOARD_TUD_RHPORT; + + TU_ASSERT(!usbd_edpt_busy(rhport, ep_addr), ); + + usbd_edpt_xfer(rhport, ep_addr, buf, buf_len); +} + +void cust_vendor_prepare_recv_fifo(uint8_t ep_addr, tu_fifo_t * fifo, uint32_t buf_len) { + uint8_t const rhport = BOARD_TUD_RHPORT; + + TU_ASSERT(!usbd_edpt_busy(rhport, ep_addr), ); + + usbd_edpt_xfer_fifo(rhport, ep_addr, fifo, buf_len); +} + + +void cust_vendor_start_transmit(uint8_t ep_addr, void const * data, uint32_t data_len) { + + uint8_t const rhport = BOARD_TUD_RHPORT; + + TU_ASSERT(!usbd_edpt_busy(rhport, ep_addr), ); + + usbd_edpt_xfer(rhport, ep_addr, (void*) data, data_len); +} + +void cust_vendor_start_transmit_fifo(uint8_t ep_addr, tu_fifo_t * fifo, uint32_t data_len) { + + uint8_t const rhport = BOARD_TUD_RHPORT; + + TU_ASSERT(!usbd_edpt_busy(rhport, ep_addr), ); + + usbd_edpt_xfer_fifo(rhport, ep_addr, (void*) fifo, data_len); +} + + +bool cust_vendor_is_receiving(uint8_t ep_addr) { + + uint8_t const rhport = BOARD_TUD_RHPORT; + + return usbd_edpt_busy(rhport, ep_addr); +} + +bool cust_vendor_is_transmitting(uint8_t ep_addr) { + + uint8_t const rhport = BOARD_TUD_RHPORT; + + return usbd_edpt_busy(rhport, ep_addr); +} + +uint16_t cust_vendor_packet_size(uint8_t ep_addr) { + for (int i = 0; i < cv_num_eps_open; i++) + if (cv_eps_open[i] == ep_addr) + return cv_eps_packet_size[i]; + return 1; +} + +#endif diff --git a/test-devices/loopback-stm32/src/vendor_custom.h b/test-devices/loopback-stm32/src/vendor_custom.h new file mode 100644 index 00000000..c80abf0a --- /dev/null +++ b/test-devices/loopback-stm32/src/vendor_custom.h @@ -0,0 +1,176 @@ +// +// Java Does USB +// Loopback device for testing +// +// Copyright (c) 2022 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// USB driver for interfaces with vendor specific class. +// The interface can have any number of bulk and interrupt endpoints. +// + +#pragma once + +#include "common/tusb_common.h" +#include "device/usbd_pvt.h" + +// --- Macro to create USB configuration descriptor + +// Interface descriptor: interface number, number of endponts +#define CUSTOM_VENDOR_INTERFACE(_itfnum, _numeps) \ + /* Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, _numeps, TUSB_CLASS_VENDOR_SPECIFIC, 0x00, 0x00, 0 + +// Interface descriptor: interface number, number of endponts +#define CUSTOM_VENDOR_INTERFACE_ALT(_itfnum, _altnum, _numeps) \ + /* Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, _altnum, _numeps, TUSB_CLASS_VENDOR_SPECIFIC, 0x00, 0x00, 0 + +// Bulk endpoint descriptor: endpoint address, packet size +#define CUSTOM_VENDOR_BULK_ENDPOINT(_epaddr, _packetsize) \ + /* Endpoint */\ + 7, TUSB_DESC_ENDPOINT, _epaddr, TUSB_XFER_BULK, U16_TO_U8S_LE(_packetsize), 0 + +// Interrupt endpoint descriptor: endpoint address, packet size, interval +#define CUSTOM_VENDOR_INTERRUPT_ENDPOINT(_epaddr, _packetsize, _interval) \ + /* Endpoint */\ + 7, TUSB_DESC_ENDPOINT, _epaddr, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_packetsize), _interval + + +// --- Application API + +/** + * @brief Prepares to recieve data on an OUT endpoint. + * + * The buffer must stay valid until the `cust_vendor_rx_cb()` callback + * reports that data has been received. + * + * If the OUT endpoint is already receiving data, this function does nothing. + * + * @param ep_addr endpoint address (1 to 127) + * @param buf pointer to buffer for received data + * @param buf_len length of buffer + */ +void cust_vendor_prepare_recv(uint8_t ep_addr, void* buf, uint32_t buf_len); + +/** + * @brief Prepares to recieve data on an OUT endpoint using a FIFO buffer. + * + * The FIFO buffer must have sufficient space to receive the data. + * It's write pointer will updated before the `cust_vendor_rx_cb()` callback is called. + * + * If the OUT endpoint is already receiving data, this function does nothing. + * + * @param ep_addr endpoint address (1 to 127) + * @param fifo pointer to FIFO buffer + * @param buf_len length of buffer + */ +void cust_vendor_prepare_recv_fifo(uint8_t ep_addr, tu_fifo_t * fifo, uint32_t buf_len); + +/** + * @brief Gets if the endpoint is busy receiving data. + * + * @param ep_addr endpoint address (1 to 127) + * @return true if the endpoint is busy receiving + * @return false it the endpoint is idle + */ +bool cust_vendor_is_receiving(uint8_t ep_addr); + +/** + * @brief Transmits data on an IN endpoint. + * + * The data must stay valid until the `cust_vendor_tx_cb()` callback + * reports that the data has been transmitted. + * + * If the IN endpoint is already transmitting data, this function does nothing. + * + * @param ep_addr endpoint address (129 to 255) + * @param data pointer to data to transmit + * @param data_len number of bytes to transmit + */ +void cust_vendor_start_transmit(uint8_t ep_addr, void const * data, uint32_t data_len); + +/** + * @brief Transmits data on an IN endpoint using a FIFO buffer. + * + * The data in the FIFO buffer must stay valid until the `cust_vendor_tx_cb()` callback. + * Before the callback, FIFO read pointer is updated. + * + * If the IN endpoint is already transmitting data, this function does nothing. + * + * @param ep_addr endpoint address (129 to 255) + * @param fifo pointer to FIFO buffer + * @param data_len number of bytes to transmit + */ +void cust_vendor_start_transmit_fifo(uint8_t ep_addr, tu_fifo_t * fifo, uint32_t data_len); + +/** + * @brief Gets if the endpoint is busy transmitting data. + * + * @param ep_addr endpoint address (129 to 255) + * @return true if the endpoint is busy transmitting + * @return false it the endpoint is idle + */ +bool cust_vendor_is_transmitting(uint8_t ep_addr); + +/** + * @brief Gets the endpoint packet size. + * + * @param ep_addr endpoint address (1 to 255) + * @return int packet size, in bytes + */ +uint16_t cust_vendor_packet_size(uint8_t ep_addr); + + + +// --- Application Callback API + +/** + * @brief Invoked when new data has been received on an OUT endpoint. + * + * @param ep_addr endpoint address (1 to 127) + * @param recv_bytes number of received bytes + */ +TU_ATTR_WEAK void cust_vendor_rx_cb(uint8_t ep_addr, uint32_t recv_bytes); + +/** + * @brief Invoked when data has been transmitted on an IN endpoint. + * + * @param ep_addr endpoint address (129 to 255) + * @param sent_bytes number of sent bytes + */ +TU_ATTR_WEAK void cust_vendor_tx_cb(uint8_t ep_addr, uint32_t sent_bytes); + +/** + * @brief Invoked when an interface of this class has been opened. + * + * This function is called as part of a SET CONFIGURATION control request. + * + * @param intf interface number + */ +TU_ATTR_WEAK void cust_vendor_intf_open_cb(uint8_t intf); + +/** + * @brief Invoked when an alternate interface has been selected. + * + * This function is called as part of a SET INTERFACE control request. + * + * @param intf interface number + * @param alt alternate interface number + */ +TU_ATTR_WEAK void cust_vendor_alt_intf_selected_cb(uint8_t intf, uint8_t alt); + +/** + * @brief Invoked when an endpoint's halt condition has been cleared. + * + * This function is called as part of a SET FEATURE control request. + * + * @param ep_addr endpoint address + */ +TU_ATTR_WEAK void cust_vendor_halt_cleared_cb(uint8_t ep_addr); + + +// --- Driver to be registered in usbd_app_driver_get_cb() + +const usbd_class_driver_t cust_vendor_driver; diff --git a/test-devices/loopback-stm32/src/wcid.cpp b/test-devices/loopback-stm32/src/wcid.cpp deleted file mode 100644 index 2c284f36..00000000 --- a/test-devices/loopback-stm32/src/wcid.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// -// Java Does USB -// Loopback device for testing -// -// Copyright (c) 2022 Manuel Bleichenbacher -// Licensed under MIT License -// https://opensource.org/licenses/MIT -// -// Microsoft WCID descriptors -// - -#include - -#include - -static usbd_request_return_codes msft_string_desc(usbd_device *usbd_dev, usb_setup_data *req, uint8_t **buf, uint16_t *len, usbd_control_complete_callback *complete); -static usbd_request_return_codes msft_feature_desc(usbd_device *usbd_dev, usb_setup_data *req, uint8_t **buf, uint16_t *len, usbd_control_complete_callback *complete); - -// Registers additional control request handlers to implement -// See https://github.com/pbatard/libwdi/wiki/WCID-Devices -void register_wcid_desc(usbd_device *usb_dev) { - usbd_register_control_callback(usb_dev, - USB_REQ_TYPE_STANDARD | USB_REQ_TYPE_DEVICE, USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, - msft_string_desc); - usbd_register_control_callback(usb_dev, - USB_REQ_TYPE_VENDOR, USB_REQ_TYPE_TYPE, - msft_feature_desc); -} - -#define WCID_VENDOR_CODE 0x37 - -// Microsoft WCID string descriptor (string index 0xee) -static const uint8_t msft_sig_desc[] = { - 0x12, // length = 18 bytes - USB_DT_STRING, // descriptor type string - 'M', 0, 'S', 0, 'F', 0, 'T', 0, // 'M', 'S', 'F', 'T' - '1', 0, '0', 0, '0', 0, // '1', '0', '0' - WCID_VENDOR_CODE, // vendor code - 0 // padding -}; - -// Microsoft WCID feature descriptor (index 0x0004) -static const uint8_t wcid_feature_desc[] = { - 0x28, 0x00, 0x00, 0x00, // length = 40 bytes - 0x00, 0x01, // version 1.0 (in BCD) - 0x04, 0x00, // compatibility descriptor index 0x0004 - 0x01, // number of sections - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved (7 bytes) - 0x00, // interface number 0 - 0x01, // reserved - 0x57, 0x49, 0x4E, 0x55, 0x53, 0x42, 0x00, 0x00, // Compatible ID "WINUSB\0\0" - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Subcompatible ID (unused) - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // reserved 6 bytes -}; - -usbd_request_return_codes msft_string_desc(__attribute__((unused)) usbd_device *usbd_dev, usb_setup_data *req, - uint8_t **buf, uint16_t *len, - __attribute__((unused)) usbd_control_complete_callback *complete) { - // 0x03: descriptor type string - // 0xee: Microsoft WCID string index - if (req->bRequest == USB_REQ_GET_DESCRIPTOR && req->wValue == 0x03ee) { - *buf = const_cast(reinterpret_cast(&msft_sig_desc)); - *len = std::min(*len, (uint16_t)msft_sig_desc[0]); - return USBD_REQ_HANDLED; - } - - return USBD_REQ_NEXT_CALLBACK; -} - -usbd_request_return_codes msft_feature_desc(__attribute__((unused)) usbd_device *usbd_dev, usb_setup_data *req, - uint8_t **buf, uint16_t *len, - __attribute__((unused)) usbd_control_complete_callback *complete) { - // 0x0004: Microsoft WCID index for feature descriptor - if (req->bRequest == WCID_VENDOR_CODE && req->wIndex == 0x0004) { - *buf = const_cast(reinterpret_cast(&wcid_feature_desc)); - *len = std::min(*len, (uint16_t)wcid_feature_desc[0]); - return USBD_REQ_HANDLED; - } - - return USBD_REQ_NEXT_CALLBACK; -} diff --git a/test-graalvm/README.md b/test-graalvm/README.md new file mode 100644 index 00000000..ffe37b77 --- /dev/null +++ b/test-graalvm/README.md @@ -0,0 +1,26 @@ +# Application for Testing the GraalVM Configuration + +## Collect Reachability Data + +Reachability data can be collected by running the unit test +of the _java-does-usb_ project: + +```shell +cd java-does-usb +export JAVA_TOOL_OPTIONS="-agentlib:native-image-agent=config-output-dir=metadata-{pid}-{datetime}/" +mvn test +``` + + +## Building + +```shell +mvn -Pnative package +``` + + +## Running + +```shell +./target/test_graalvm +``` diff --git a/test-graalvm/config/linux/reachability-metadata.json b/test-graalvm/config/linux/reachability-metadata.json new file mode 100644 index 00000000..e1746c04 --- /dev/null +++ b/test-graalvm/config/linux/reachability-metadata.json @@ -0,0 +1,111 @@ +{ + "foreign": { + "downcalls": [ + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint", + "jlong", + "void*" + ], + "options": { + "captureCallState": true, + "firstVariadicArg": 2 + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint", + "void*", + "jint", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "void*", + "parameterTypes": [] + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "jint" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint", + "jint", + "jint", + "void*" + ], + "options": { + "captureCallState": true + } + } + ] + } +} \ No newline at end of file diff --git a/test-graalvm/config/macos/reachability-metadata.json b/test-graalvm/config/macos/reachability-metadata.json new file mode 100644 index 00000000..113fb587 --- /dev/null +++ b/test-graalvm/config/macos/reachability-metadata.json @@ -0,0 +1,318 @@ +{ + "foreign": { + "upcalls": [ + { + "returnType": "void", + "parameterTypes": [ + "void*", + "jint", + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "jint", + "void*", + "void*" + ] + }, + { + "returnType": "void", + "parameterTypes": [ + "void*", + "jint" + ] + } + ], + "downcalls": [ + { + "returnType": "void", + "parameterTypes": [ + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "void", + "parameterTypes": [ + "void*", + "struct(jlong,jlong)", + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "void*", + "jlong" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jint", + "void*", + "jdouble", + "jdouble", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*", + "void*", + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint" + ] + }, + { + "returnType": "jlong", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint", + "void*", + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "jint", + "void*", + "void*", + "jint" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "jint", + "void*", + "void*" + ] + }, + { + "returnType": "jbyte", + "parameterTypes": [ + "void*", + "jlong", + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "struct(jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte)" + ] + }, + { + "returnType": "void", + "parameterTypes": [] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte", + "void*", + "jint", + "jint", + "jint", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte", + "void*", + "void*", + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "jlong", + "parameterTypes": [] + }, + { + "returnType": "void", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint", + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "jint" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "void*", + "void*", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte", + "void*", + "jint", + "void*", + "void*" + ] + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "struct(jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte)", + "void*" + ] + }, + { + "returnType": "struct(jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte,jbyte)", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "void", + "parameterTypes": [ + "void*", + "jint" + ] + } + ] + }, + "reflection": [ + { + "type": "net.codecrete.usb.macos.gen.corefoundation.CFMessagePortCreateLocal$callout$Function", + "methods": [ + { + "name": "apply", + "parameterTypes": [ + "java.lang.foreign.MemorySegment", + "int", + "java.lang.foreign.MemorySegment", + "java.lang.foreign.MemorySegment" + ] + } + ] + }, + { + "type": "net.codecrete.usb.macos.gen.iokit.IOServiceAddMatchingNotification$callback$Function", + "methods": [ + { + "name": "apply", + "parameterTypes": [ + "java.lang.foreign.MemorySegment", + "int" + ] + } + ] + } + ] +} diff --git a/test-graalvm/config/windows/reachability-metadata.json b/test-graalvm/config/windows/reachability-metadata.json new file mode 100644 index 00000000..fe1ba6cc --- /dev/null +++ b/test-graalvm/config/windows/reachability-metadata.json @@ -0,0 +1,348 @@ +{ + "foreign": { + "upcalls": [ + { + "returnType": "jlong", + "parameterTypes": [ + "void*", + "jint", + "jlong", + "jlong" + ] + } + ], + "downcalls": [ + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "void*", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jlong", + "void*", + "void*", + "void*", + "void*", + "jint", + "void*", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte", + "jint", + "jint", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jlong", + "parameterTypes": [ + "void*", + "jint", + "jlong", + "jlong" + ] + }, + { + "returnType": "jlong", + "parameterTypes": [ + "void*", + "void*", + "void*", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "void*", + "jlong", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jlong", + "void*", + "void*", + "jint", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jlong", + "jint", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jlong" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "struct(jbyte,jbyte,jshort,jshort,jshort)", + "void*", + "jint", + "void*", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jlong", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*", + "jint", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jint", + "void*", + "jint", + "void*", + "jint", + "void*", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [ + "jint", + "void*", + "void*", + "jint", + "jint", + "jint", + "jint", + "jint", + "void*", + "void*", + "void*", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "void*", + "parameterTypes": [ + "void*", + "jint", + "jint", + "void*", + "jint", + "jint", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*", + "void*", + "void*", + "jint" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jshort", + "parameterTypes": [ + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jlong", + "void*", + "void*", + "jint", + "void*", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "jbyte", + "void*", + "jint", + "void*", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "jint", + "void*", + "jint", + "jint", + "void*", + "jint", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jlong", + "parameterTypes": [ + "void*", + "void*" + ], + "options": { + "captureCallState": true + } + }, + { + "returnType": "jint", + "parameterTypes": [ + "void*", + "void*", + "jint", + "void*" + ], + "options": { + "captureCallState": true + } + } + ] + }, + "reflection": [ + { + "type": "windows.win32.ui.windowsandmessaging.WNDPROC$Function", + "methods": [ + { + "name": "invoke", + "parameterTypes": [ + "java.lang.foreign.MemorySegment", + "int", + "long", + "long" + ] + } + ] + } + ] +} diff --git a/test-graalvm/pom.xml b/test-graalvm/pom.xml new file mode 100644 index 00000000..cfaf0bbf --- /dev/null +++ b/test-graalvm/pom.xml @@ -0,0 +1,84 @@ + + 4.0.0 + + net.codecrete.usb.examples + test_graalvm + jar + 1.0-SNAPSHOT + test_graalvm + https://www.github.com/manuelbl/java-does-usb + + + 25 + 25 + UTF-8 + 0.11.0 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.14.0 + + true + + + + org.apache.maven.plugins + maven-jar-plugin + 3.4.2 + + + + net.codecrete.usb.examples.App + true + + + + + + + + + + net.codecrete.usb + java-does-usb + 1.3.0 + + + junit + junit + 3.8.1 + test + + + + + + native + + + + org.graalvm.buildtools + native-maven-plugin + ${native.maven.plugin.version} + true + + + build-native + + compile-no-fork + + package + + + + + + + + + diff --git a/test-graalvm/src/main/java/net/codecrete/usb/examples/App.java b/test-graalvm/src/main/java/net/codecrete/usb/examples/App.java new file mode 100644 index 00000000..18dd1636 --- /dev/null +++ b/test-graalvm/src/main/java/net/codecrete/usb/examples/App.java @@ -0,0 +1,324 @@ +// +// Java Does USB +// Copyright (c) 2025 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.examples; + +import net.codecrete.usb.Usb; +import net.codecrete.usb.UsbDevice; +import net.codecrete.usb.UsbException; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import static java.time.Duration.ofSeconds; + +/** + * Test for robustness when USB devices is unplugged during operation. + * + *

+ * Requires use of test device. + *

+ */ +@SuppressWarnings({"java:S106", "java:S2189"}) +public class App { + private static final Map activeDevices = new HashMap<>(); + + public static void main(String[] args) throws IOException { + System.out.println("Plug and unplug test device multiple times."); + System.out.println("Hit ENTER to exit."); + + Usb.setOnDeviceConnected(App::onPluggedDevice); + Usb.setOnDeviceDisconnected(App::onUnpluggedDevice); + Usb.getDevices().forEach(App::onPluggedDevice); + + //noinspection ResultOfMethodCallIgnored + System.in.read(); + } + + private static void onPluggedDevice(UsbDevice device) { + var config = DeviceConfig.getConfig(device); + if (config.isEmpty()) + return; + + var worker = new DeviceWorker(device, config.get()); + activeDevices.put(device, worker); + worker.start(); + } + + private static void onUnpluggedDevice(UsbDevice device) { + var config = DeviceConfig.getConfig(device); + if (config.isEmpty()) + return; + + var worker = activeDevices.remove(device); + worker.setDisconnectTime(System.currentTimeMillis()); + worker.join(); + + // test handling of disconnected devices + new Thread(() -> { + sleep(2000); + try { + device.open(); + System.err.println("Device should not be openable after disconnect"); + } catch (UsbException e) { + if (!e.getMessage().contains("disconnected")) + System.err.println("Unexpected error: " + e.getMessage()); + } + }).start(); + } + + @SuppressWarnings({"SameParameterValue", "java:S2925"}) + private static void sleep(long millis) { + try { + Thread.sleep(millis); + + } catch (InterruptedException _) { + Thread.currentThread().interrupt(); + } + } + + static class DeviceWorker { + + private final UsbDevice device; + private final DeviceConfig config; + + private final int seed; + + private long disconnectTime; + + private final Map workTracking = new HashMap<>(); + + DeviceWorker(UsbDevice device, DeviceConfig config) { + this.device = device; + this.config = config; + this.seed = (int) System.currentTimeMillis(); + } + + void start() { + System.out.println("Device connected"); + + device.open(); + device.claimInterface(config.interfaceNumber()); + + // start loopback sender and receiver + startThread((seed & 1) != 0 ? this::sendLoopbackDataStream : this::sendLoopbackData); + startThread((seed & 2) != 0 ? this::receiveLoopbackDataStream : this::receiveLoopbackData); + + // start echo sender and receiver + if (config.endpointEchoOut() > 0) { + startThread(this::sendEcho); + startThread(this::receiveEcho); + } + } + + private void startThread(Runnable action) { + var thread = new Thread(() -> runAction(action)); + var work = new Work(); + workTracking.put(thread, work); + thread.start(); + } + + void join() { + // wait for threads to finish + for (var thread : workTracking.keySet()) { + try { + boolean terminated = thread.join(ofSeconds(5)); + if (!terminated) + System.err.printf("Thread \"%s\" failed to join within 5s%n", thread.getName()); + + } catch (InterruptedException _) { + Thread.currentThread().interrupt(); + } + } + + device.close(); + + // check achieved work + for (var e : workTracking.entrySet()) { + var work = e.getValue(); + var expectedWork = work.expectedWorkPerSec * 0.001 * (work.finishTime - work.startTime); + if (work.actualWork < expectedWork) + System.err.printf("Thread \"%s\" achieved insufficient work executed. Expected: %.0f, achieved: %d%n", + e.getKey().getName(), expectedWork, work.actualWork); + } + + // check that the threads haven't terminated early + for (var e : workTracking.entrySet()) { + long duration = Math.abs(e.getValue().finishTime - disconnectTime); + if (duration > 500) + System.err.printf("Thread \"%s\" has likely crashed early%n", e.getKey().getName()); + } + + System.out.println("Device disconnected"); + } + + void setDisconnectTime(long time) { + disconnectTime = time; + } + + private synchronized void logFinish() { + var work = workTracking.get(Thread.currentThread()); + work.finishTime = System.currentTimeMillis(); + } + + private void logStart(String operation, long expectedWorkPerSec) { + Thread.currentThread().setName(operation); + var work = workTracking.get(Thread.currentThread()); + work.startTime = System.currentTimeMillis(); + work.expectedWorkPerSec = expectedWorkPerSec; + } + + private void logWork(long amount) { + var work = workTracking.get(Thread.currentThread()); + work.actualWork += amount; + } + + private void runAction(Runnable action) { + try { + action.run(); + } catch (UsbException _) { + logFinish(); + } + } + + private void sendLoopbackData() { + logStart("sending loopback data", 300_000); + var prng = new PRNG(); + var data = new byte[5000]; + //noinspection InfiniteLoopStatement + while (true) { + prng.fill(data); + device.transferOut(config.endpointLoopbackOut(), data, 1000); + logWork(data.length); + } + } + + private void receiveLoopbackData() { + logStart("receiving loopback data", 300_000); + var prng = new PRNG(); + //noinspection InfiniteLoopStatement + while (true) { + byte[] data = device.transferIn(config.endpointLoopbackIn()); + int index = prng.verify(data); + if (index >= 0) + throw new CommunicationException("invalid data received"); + logWork(data.length); + } + } + + private void sendLoopbackDataStream() { + logStart("sending loopback data with output stream", 300_000); + var prng = new PRNG(); + var data = new byte[5000]; + try (var os = device.openOutputStream(config.endpointLoopbackOut())) { + //noinspection InfiniteLoopStatement + while (true) { + prng.fill(data); + os.write(data); + logWork(data.length); + } + } catch (IOException e) { + throw new CommunicationException(e); + } + } + + private void receiveLoopbackDataStream() { + logStart("receiving loopback data with input stream", 300_000); + var prng = new PRNG(); + try (var is = device.openInputStream(config.endpointLoopbackIn())) { + //noinspection InfiniteLoopStatement + while (true) { + var data = new byte[2000]; + int n = is.read(data); + int index = prng.verify(data, n); + if (index >= 0) + throw new CommunicationException("invalid data received"); + logWork(n); + } + } catch (IOException e) { + throw new CommunicationException(e); + } + } + + private void sendEcho() { + logStart("sending echo", 7); + var data = new byte[]{0x03, 0x45, 0x73, (byte) 0xb3, (byte) 0x9f, 0x3f, 0x00, 0x6a}; + //noinspection InfiniteLoopStatement + while (true) { + device.transferOut(config.endpointEchoOut(), data); + logWork(1); + sleep(100); + } + } + + private void receiveEcho() { + logStart("receiving echo", 14); + //noinspection InfiniteLoopStatement + while (true) { + device.transferIn(config.endpointEchoIn()); + logWork(1); + } + } + } + + static class Work { + long startTime; + long expectedWorkPerSec; + long actualWork; + long finishTime; + } + + /** + * Pseudo random number generator + */ + static class PRNG { + private int state; + private int nBytes; + private int bits; + + int next() { + int x = state; + x ^= x << 13; + x ^= x >>> 17; + x ^= x << 5; + state = x; + return x; + } + + void fill(byte[] data) { + int len = data.length; + for (int i = 0; i < len; i++) { + if (nBytes == 0) { + bits = next(); + nBytes = 4; + } + data[i] = (byte) bits; + bits >>>= 8; + nBytes--; + } + } + + int verify(byte[] data) { + return verify(data, data.length); + } + + int verify(byte[] data, int len) { + for (int i = 0; i < len; i++) { + if (nBytes == 0) { + bits = next(); + nBytes = 4; + } + if (data[i] != (byte) bits) + return i; + bits >>>= 8; + nBytes--; + } + return -1; + } + } +} diff --git a/test-graalvm/src/main/java/net/codecrete/usb/examples/CommunicationException.java b/test-graalvm/src/main/java/net/codecrete/usb/examples/CommunicationException.java new file mode 100644 index 00000000..d8660f7f --- /dev/null +++ b/test-graalvm/src/main/java/net/codecrete/usb/examples/CommunicationException.java @@ -0,0 +1,21 @@ +// +// Java Does USB +// Copyright (c) 2025 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// + +package net.codecrete.usb.examples; + +import java.io.IOException; + +public class CommunicationException extends RuntimeException { + + public CommunicationException(String message) { + super(message); + } + + public CommunicationException(IOException cause) { + super("Error in communication with USB device", cause); + } +} diff --git a/test-graalvm/src/main/java/net/codecrete/usb/examples/DeviceConfig.java b/test-graalvm/src/main/java/net/codecrete/usb/examples/DeviceConfig.java new file mode 100644 index 00000000..05bf8bdd --- /dev/null +++ b/test-graalvm/src/main/java/net/codecrete/usb/examples/DeviceConfig.java @@ -0,0 +1,68 @@ +// +// Java Does USB +// Copyright (c) 2025 Manuel Bleichenbacher +// Licensed under MIT License +// https://opensource.org/licenses/MIT +// +// Configuration information about test device +// + +package net.codecrete.usb.examples; + +import net.codecrete.usb.UsbDevice; + +import java.util.Optional; +import java.util.stream.Stream; + +/** + * Test device configuration + * @param vid vendor ID + * @param pid product ID + * @param isComposite indicates if this is the composite test device + * @param interfaceNumber interface number for loopback and echo endpoints + * @param endpointLoopbackOut loopback OUT endpoint number + * @param endpointLoopbackIn loopback IN endpoint number + * @param endpointEchoOut echo OUT endpoint number + * @param endpointEchoIn echo IN endpoint number + */ +public record DeviceConfig(int vid, int pid, + boolean isComposite, + int interfaceNumber, + int endpointLoopbackOut, int endpointLoopbackIn, + int endpointEchoOut, int endpointEchoIn +) { + + private static final DeviceConfig LOOPBACK_DEVICE = new DeviceConfig( + 0xcafe, + 0xceaf, + false, + 0, + 1, + 2, + 3, + 3 + ); + + private static final DeviceConfig COMPOSITE_DEVICE = new DeviceConfig( + 0xcafe, + 0xcea0, + true, + 3, + 1, + 2, + -1, + -1 + ); + + + /** + * Gets the configuration fo the specified USB device. + * @param device USB device + * @return configuration, or empty if the USB device is not a test device + */ + public static Optional getConfig(UsbDevice device) { + return Stream.of(LOOPBACK_DEVICE, COMPOSITE_DEVICE) + .filter(config -> device.getVendorId() == config.vid() && device.getProductId() == config.pid()) + .findFirst(); + } +} diff --git a/test-graalvm/src/main/resources/META-INF/native-image/net.codecrete.usb.examples/test_graalvm/native-image.properties b/test-graalvm/src/main/resources/META-INF/native-image/net.codecrete.usb.examples/test_graalvm/native-image.properties new file mode 100644 index 00000000..4e4f3471 --- /dev/null +++ b/test-graalvm/src/main/resources/META-INF/native-image/net.codecrete.usb.examples/test_graalvm/native-image.properties @@ -0,0 +1 @@ +Args = --enable-native-access=ALL-UNNAMED -H:ConfigurationFileDirectories=config/macos