Squashed 'third_party/allwpilib/' changes from e4b91005cf..83f1860047

83f1860047 [wpilib] Add/update documentation to PneumaticBase and subclasses (NFC) (#4881)
9872e676d8 [commands] Make Subsystem destructor virtual (#4892)
25db20e49d [hal] Fix segfault in various HAL functions (#4891)
b0c6724eed [glass] Add hamburger menu icon to titlebars (#4874)
f0fa8205ac Add missing compiler flags and fix warnings (#4889)
42fc4cb6bc [wpiutil] SafeThread: Provide start/stop hooks (#4880)
cc166c98d2 [templates] Add Command-based skeleton template (#4861)
3f51f10ad3 [build] Update to 2023v3 image (#4886)
1562eae74a [ntcore] Refactor meta-topic decoding from glass (#4809)
b632b288a3 Fix usages of std::min and std::max to be windows safe (#4887)
c11bd2720f [wpilibc] Add internal function to reset Shuffleboard instance (#4884)
f1151d375f [ntcore] Add method to get server time offset (#4847)
fe1b62647f [hal,wpilib] Update documentation for getComments (NFC) (#4879)
c49a45abbd [build] Fix examples linking in incorrect jni library (#4873)
bc3d01a721 [build] Add platform check to doxygen plugin (#4862)
bc473240ae Add Jetbrains Fleet folder to .gitignore (#4872)
2121bd5fb8 [wpimath] Remove RKF45 (#4870)
835f8470d6 [build] Fix roborio cross-compiler on arm hosts (#4864)
6cfe5de00d [ntcore] Don't deadlock server on early destroy (#4863)
2ac41f3edc [hal, wpilib] Add RobotController.getComments()  (#4463)
26bdbf3d41 Java optimization and formatting fixes (#4857)
92149efa11 Spelling and grammar cleanups (#4849)
176fddeb4c [commands] Add functions to HID classes to allow use of axes as BooleanEvents/Triggers (#4762)
87a34af367 [templates] Add bindings to command-based template (#4838)
4534e75787 [examples] Remove redundant MotorControl example (#4837)
1cbebaa2f7 [commands] Remove final semicolon from test macro definition (#4859)
6efb9ee405 [commands] Add constructor for SwerveControllerCommand that takes a HolonomicDriveController (#4785)
1e7fcd5637 [cscore] Change run loop functions to not be mac specific (#4854)
1f940e2b60 [apriltag] Add C++ wrappers, rewrite Java/JNI to match (#4842)
a6d127aedf [build] Add missing task dependency in wpilibjExamples (#4852)
b893b3d6d3 [cscore] Add support for USB cameras on macOS (#4846)
1696a490fa [glass] Add support for alternate NT ports (#4848)
40a22d69bc [glass] Add support for alternate NT ports (#4848)
e84dbfede0 [wpilib] GenericHID: Add rumble both option (#4843)
8aa9dbfa90 [examples] Link apriltag package in examples build.gradle (#4845)
eda2fa8a17 [build] Update Spotless (#4840)
d20594db0d Fix typos (#4839)
dd8ecfdd54 [commands] Fix typo in waitUntil docs (NFC) (#4841)
17ceebfff4 [apriltag] Clean up apriltag JNI (#4823)
8b74ab389d [examples] RapidReactCommandBot: Fix array indices (#4833)
1aad3489c2 [sim] Implement PD total current and power (#4830)
2744991771 [wpimath] Fix docs in SwerveModulePosition (#4825)
ffbf6a1fa2 [commands] Disable regularly failing unit test (#4824)
fbabd0ef15 [commands] Enhance Command Sendable implementations (#4822)
7713f68772 [hal] Use atomic rather then mutex for DS Data updates (#4787)
701995d6cc [examples] Update Command-based starter project (#4778)
bf7068ac27 [wpilibc] Add missing PPS implementation for C++ (#4821)
aae0f52ca6 [ntcore] NetworkTable: fix visibility of get/set value (#4820)
ee02fb7ba7 [hal] Add support for Pulse-Per-Second signal (#4819)
518916ba02 [wpilib] Fix DS mode thread event being manual reset accidentally (#4818)
3997c6635b [hal] Update to new image, use new TCP notify callback and new duty cycle API (#4774)
cc8675a4e5 [examples] Add comment on how to view elevator sim (NFC) (#4482)
fb2c170b6e [ntcore] Simplify local startup (#4803)
7ba8a9ee1f [wpimath] ProfiledPIDController: Add to SendableRegistry (#4656)
c569d8e523 [wpilib] Joystick.getMagnitude(): use hypot() function (#4816)
2a5e89fa97 [apriltag] Improve description of pose coordinates (NFC) (#4810)
cc003c6c38 [apriltag] Fix AprilTagFieldLayout JSON name (#4814)
5522916123 [commands] CommandXBoxController bumper documentation fix (NFC) (#4815)
967b30de3a [glass] Fix NT view UpdateClients() bug (#4808)
3270d4fc86 [wpimath] Rewrite pose estimator docs (#4807)
be39678447 [apriltag] Add test to ensure apriltagjni loads (#4805)
61c75deb2a [commands] Test no-op behavior of scheduling a scheduled command (#4806)
a865f48e96 [ntcore] Pass pub/sub options as a unified PubSubOptions struct (#4794)
f66a667321 [commands] Fix incorrect Trigger docs (NFC) (#4792)
f8d4e9866e [ntcore] Clean up ntcore_test.h (#4804)
7e84ea891f [wpimath] Fix ComputerVisionUtil transform example in parameter docs (NFC) (#4800)
da3ec1be10 [wpimath] Change terminology for ArmFeedforward gravity gain (NFC) (#4791)
944dd7265d [wpilibc] Add C++ Notifier error handling, update java notifier error message (#4795)
6948cea67a [wpiutil] Fix MemoryBuffer initialization (#4797)
a31459bce6 [wpiutil] Fix UnescapeCString overflow when inputSize < 2 (#4796)
4a0ad6b48c [wpimath] Rotation2d: Add reference to angleModulus in docs (NFC) (#4786)
e6552d272e [ntcore] Remove table multi-subscriber (#4789)
bde383f763 [hal] Replace const char* with std::string_view in Driver Station sim functions (#4532)
5a52b51443 [hal] Add RobotController.getSerialNumber() (#4783)
69a66ec5ec [wpilib] Fix multiple motor safety issues (#4784)
989c9fb29a [wpimath] Revert Rotation2D change that limits angles (#4781)
0f5b08ec69 [wpigui] Update imgui to 1.89.1+ (#4780)
fba191099c [examples] AddressableLED: Add unit test (#4779)
b390cad095 [wpilibj] Consistently use ErrorMessages.requireNonNullParam (#4776)
b9772214d9 [wpilib] Sendable: Don't call setter for getter changes
342c375a71 [ntcore] Add subscriber option to exclude single publisher
b0e4053087 [ntcore] Use int for options instead of double
f3e666b7bb [cscore] Convert YUYV and UYVY directly to grayscale (#4777)
b300518bd1 [hal] Add CAN Stream API to Java through JNI bindings (#4193)
be27171236 [wpilibj] Shuffleboard: Check for null sendable (#4772)
4bbdbdfb48 [commands] Move GroupedCommands to CommandScheduler (#4728)
f18fd41ac3 [wpimath] Remove broken and obsoleted ComputerVisionUtil functions (#4775)
2d0faecf4f [glass] DataSource: Add spinlock to protect value (#4771)
348bd107fc [hal] Add CANManufacturer for The Thrifty Bot (#4773)
3149dc64b8 [examples] HatchbotInlined: Use Subsystem factories (#4765)
8618dd4160 [glass, wpilib] Replace remaining references to Speed Controller with Motor Controller (#4769)
72e21a1ed1 [apriltag] Use wpilibsuite fork of apriltag (#4764)
eab0d929e6 [commands] CommandGenericHID POV methods: Fix docs (NFC) (#4760)
6789869663 [wpilib] Call set(0) rather than disable for stopMotor (#4763)
c9dea2968d [cscore] Emit warning that USB Camera isn't supported on OSX (#4766)
8f402645f5 [commands] Fix PIDSubsystem setSetpoint behavior (#4759)
f24ad1d715 [build] Upgrade to googletest 1.12.1 (#4752)
ff88756864 [wpimath] Add new DCMotor functions for alternative calculations and reduction calculation (#4749)
f58873db8e [wpimath] Remove extra terms in matrix for pose estimator docs (#4756)
37e969b41a [wpimath] Add constructors to pose estimators with default standard deviations (#4754)
13cdc29382 [ci] Rename comment command from "/wpiformat" to "/format" (#4755)
6e23985ae6 [examples] Add main include directory to test builds (#4751)
66bb0ffb2c [examples] Add unit testing infrastructure (#4646)
74cc86c4c5 [wpimath] Make transform tests use pose/transform equality operators (#4675)
e22d8cc343 [wpimath] Use Odometry for internal state in Pose Estimation (#4668)
68dba92630 [ci] Update mac and windows builds to Java 17 (#4750)
23bfc2d9ab [sim] Remove unmaintained Gazebo support (#4736)
1f1461e254 [wpilib] Add method to enable/disable LiveWindow in test mode (#4678)
eae68fc165 [wpimath] Add tolerance for Rotation3d rotation matrix special orthogonality (#4744)
4c4545fb4b [apriltag] Suppress warning (#4743)
16ffaa754d [docs] Generate docs for apriltag subproject (#4745)
5e74ff26d8 [apriltag, build] Update native utils, add apriltag impl and JNI (#4733)
53875419a1 [hal] Allow overriding stderr printing by HAL_SendError (#4742)
aa6499e920 [ntcore] Fix special topic multi-subscriber handling (#4740)
df70351107 [build] Fix cmake install of thirdparty includes (#4741)
e9bd50ff9b [glass] NT view: clear meta-topic info on disconnect (#4732)
9b319fd56b [ntcore] Add sub option for local vs remote changes (#4731)
18d28ec5e3 [ntcore] Remove duplicate value checking from ClientImpl
bdfb625211 [ntcore] Send duplicate values to network if necessary
21003e34eb [commands] Update Subsystem factories and example to return CommandBase (#4729)
70080457d5 [commands] Refactor ProxyScheduleCommand, SelectCommand into ProxyCommand (#4534)
e82cd5147b [wpilib] Tweak Color HSV formula and use in AddressableLED (#4724)
ec124bb662 [commands] Allow unsetting a subsystem's default command (#4621)
2b2aa8eef7 [examples] Update all examples to use NWU coordinate conventions (#4725)
cb38bacfe8 [commands] Revert to original Trigger implementation (#4673)
15561338d5 [commands] Remove one more default command isFinished check (#4727)
ca35a2e097 Add simgui files to .gitignore (#4726)
20dbae0cee [examples] Renovate command-based examples (#4409)
1a59737f40 [commands] Add convenience factories (#4460)
42b6d4e3f7 Use defaulted comparison operators in C++ (#4723)
135c13958f [wpigui] Add FontAwesome (#4713)
ffbfc61532 [ntcore] Add NetworkTable table-specific listeners (#4640)
8958b2a4da [commands] Add property tests for command compositions (#4715)
e4ac09077c [wpilib] Add link to MotorSafety article (#4720)
f40de0c120 [commands] Add C++ factory templates (#4686)
51fa3e851f [build] cmake: Use FetchContent instead of ExternalProject (#4714)
1da84b2255 [wpigui] Reload fonts to scale rather than preloading (#4712)
e43e2fbc84 [wpiutil] StringExtras: Add UnescapeCString (#4707)
5804d8fa84 [ntcore] Server: Properly handle multiple subscribers (#4717)
169ef5fabf [glass] Update NT view for topicsOnly and sendAll changes (#4718)
148759ef54 [examples] CANPDP: Expand properties shown (#4687)
58ed112b51 [commands] RepeatCommand: restart on following iteration (#4706)
dd1da77d20 [readme] Fix broken CI badge (#4710)
7cda85df20 [build] Check Gradle plugin repo last to fix CI (#4711)
7ed9b13277 [build] Bump version plugin to fix null tag (#4705)
6b4f26225d [apriltag] Fix pluralization of apriltag artifacts (#4671)
b2d2924b72 [cscore] Add Y16 image support (#4702)
34ec89c041 [wpilibc] Shuffleboard SimpleWidget: Return pointer instead of reference (#4703)
e15200068d [ci] Disable HW testbench runs (#4704)
d5200db6cd [wpimath] Rename HolonomicDriveController.calculate params (#4683)
2ee3d86de4 [wpimath] Clarify Rotation3d roll-pitch-yaw direction (#4699)
9f0a8b930f [cscore] Use MFVideoFormat_L8 for Gray on Windows (#4701)
2bca43779e [cscore] Add UYVY image support (#4700)
4307d0ee8b [glass] Plot: allow for more than 11 plots (#4685)
3fe8d355a1 [examples] StateSpaceDifferentialDriveSimulation: Use encoder reversed constants (#4682)
b44034dadc [ntcore] Allow duplicate client IDs on server (#4676)
52d2c53888 [commands] Rename Java factory wait() to waitSeconds() (#4684)
76e918f71e [build] Fix JNI artifacts linking to incorrect libraries (#4680)
0bee875aff [commands] Change C++ CommandPtr to use CommandBase (#4677)
98e922313b [glass] Don't check IsConnected for NT widgets (#4674)
9a36373b8f [apriltag] Switch 2022 apriltag layout length and width values (#4670)
cf8faa9e67 [wpilib] Update values on controllable sendables (#4667)
5ec067c1f8 [ntcore] Implement keep duplicates pub/sub flag (#4666)
e962fd2916 [ntcore] Allow numeric-compatible value sets (#4620)
88bd67e7de [ci] Update clang repositories to jammy (#4665)
902e8686d3 [wpimath] Rework odometry APIs to improve feature parity (#4645)
e2d49181da Update to native utils 2023.8.0 (#4664)
149bac55b1 [cscore] Add Arducam OV9281 exposure quirk (#4663)
88f7a3ccb9 [wpimath] Fix Pose relativeTo documentation (#4661)
8acce443f0 [examples] Fix swerve examples to use getDistance for turning encoder (#4652)
295a1f8f3b [ntcore] Fix WaitForListenerQueue (#4662)
388e7a4265 [ntcore] Provide mechanism to reset internals of NT instance (#4653)
13aceea8dc [apriltag] Fix FieldDimensions argument order (#4659)
c203f3f0a9 [apriltag] Fix documentation for AprilTagFieldLayout (#4657)
f54d495c90 Fix non initialized hal functionality during motor safety init (#4658)
e6392a1570 [cmd] Change factories return type to CommandBase (#4655)
53904e7cf4 [apriltag] Split AprilTag functionality to a separate library (#4578)
2e88a496c2 [wpimath] Add support for swerve joystick normalization (#4516)
ce4c45df13 [wpimath] Rework function signatures for Pose Estimation / Odometry (#4642)
0401597d3b [readme] Add wpinet to MavenArtifacts.md (#4651)
2e5f9e45bb [wpimath] Remove encoder reset comments on Swerve, Mecanum Odometry and Pose Estimation (#4643)
e4b5795fc7 [docs] Disable Doxygen for memory to fix search (#4636)
03d0ea188c [build] cmake: Add missing wpinet to installed config file (#4637)
3082bd236b [build] Move version file to its own source set (#4638)
b7ca860417 [build] Use build cache for sign step (#4635)
64838e6367 [commands] Remove unsafe default command isFinished check (#4411)
1269d2b901 [myRobot] Disable spotbugs (#4565)
14d8506b72 [wpimath] Fix units docs for LinearSystemId::IdentifyDrivetrainSystem() (#4600)
d1d458db2b [wpimath] Constrain Rotation2d range to -pi to pi (#4611)
f656e99245 [readme] Add links to development build documentation (#4481)
6dd937cef7 [commands] Fix Trigger API docs (NFC) (#4599)
49047c85b9 [commands] Report error on C++ CommandPtr use-after-move (#4575)
d07267fed1 [ci] Upgrade containers to Ubuntu 22.04 and remove libclang installation (#4633)
b53ce1d3f0 [build, wpiutil] Switch macos to universal binaries (#4628)
5a320c326b [upstream_util, wpiutil] Refactor python scripts (#4614)
c4e526d315 [glass] Fix NT Mechanism2D (#4626)
d122e4254f [ci] Run spotlessApply after wpiformat in comment command (#4623)
5a1e7ea036 [wpilibj] FieldObject2d: Add null check to close() (#4619)
179f569113 [ntcore] Notify locally on SetDefault (#4617)
b0f6dc199d [wpilibc] ShuffleboardComponent.WithProperties: Update type (#4615)
7836f661cd [wpimath] Add missing open curly brace to units/base.h (#4613)
dbcc1de37f [wpimath] Add DifferentialDriveFeedforward classes which wrap LinearPlantInversionFeedforward (#4598)
93890c528b [wpimath] Add additional angular acceleration units (#4610)
3d8d5936f9 [wpimath] Add macro for disabling units fmt support (#4609)
2b04159dec [wpimath] Update units/base.h license header (#4608)
2764004fad [wpinet] Fix incorrect jni definitions (#4605)
85f1bb8f2b [wpiutil] Reenable jni check task (#4606)
231ae2c353 [glass] Plot: Fix Y-axis not being saved (#4594)
e92b6dd5f9 [wpilib] Fix AprilTagFieldLayout JSON property name typos (#4597)
2a8e0e1cc8 Update all dependencies that use grgit (#4596)
7d06e517e9 [commands] Move SelectCommand factory impl to header (#4581)
323524fed6 [wpimath] Remove deprecated units/units.h header (#4572)
d426873ed1 [commands] Add missing PS4 triangle methods (#4576)
5be5869b2f [apriltags] Use map as internal data model (#4577)
b1b4c1e9e7 [wpimath] Fix Pose3d transformBy rotation type (#4545)
a4054d702f [commands] Allow composing two triggers directly (#4580)
0190301e09 [wpilibc] Explicitly mark EventLoop as non-copyable/non-movable (#4579)
9d1ce6a6d9 [ntcore] Catch file open error when saving preferences (#4571)
5005e2ca04 [ntcore] Change Java event mask to EnumSet (#4564)
fa44a07938 [upstream-utils][mpack] Add upstream util for mpack (#4500)
4ba16db645 [ntcore] Various fixes and cleanups (#4544)
837415abfd [hal] Fix joysticks either crashing or returning 0 (#4570)
2c20fd0d09 [wpilib] SingleJointedArmSim: Check angle equals limit on wouldHit (#4567)
64a7136e08 [wpimath] SwerveDrivePoseEstimator: Restore comment about encoder reset (#4569)
b2b473b24a [wpilib] Add AprilTag and AprilTagFieldLayout (#4421)
7aab8fa93a [build] Update to Native Utils 2023.6.0 (#4563)
12c2851856 [commands] WrapperCommand: inherit from CommandBase (#4561)
0da169dd84 [wpimath] Remove template argument from ElevatorFeedforward (#4554)
2416827c25 [wpimath] Fix docs for pose estimator local measurement models (#4558)
1177a3522e [wpilib] Fix Xbox/PS4 POV sim for port number constructors (#4548)
102344e27a [commands] HID classes: Add missing methods, tweak return types (#4557)
1831ef3e19 [wpilib] Fix Shuffleboard SuppliedValueWidget (#4559)
a9606ce870 [wpilib] Fix Xbox/PS4 POV sim (#4546)
6c80d5eab3 [wpimath] Remove unused SymbolExports.h include from units/base.h (#4541)
b114006543 [ntcore] Unify listeners (#4536)
32fbfb7da6 [build] cmake: Install ntcore generated include files (#4540)
02465920fb [build] Update native utils to 2023.4.0 (#4539)
3a5a376465 [wpimath] Increase constexpr support in geometry data types (#4231)
1c3c86e9f1 [ntcore] Cache GetEntry(name) values (#4531)
dcda09f90a [command] Rename trigger methods (#4210)
66157397c1 [wpilib] Make drive classes follow NWU axes convention (#4079)
9e22ffbebf [ntcore] Fix null deref in NT3 client (#4530)
648ab6115c [wpigui,dlt,glass,ov] Support arm in GUI tools (#4527)
8bc3b04f5b [wpimath] Make ComputerVisionUtil use 3D geometry classes (#4528)
cfb84a6083 [wpilibc] Don't hang waiting for NT server to start (#4524)
02c47726e1 [wpimath] Remove unused odometry instance from DifferentialDrivePoseEstimator test (#4522)
b2a0093294 [ci] Revert upgrade of github-pages-deploy-action (#4521)
2a98d6b5d7 [wpimath] PIDController: Add getters for position & velocity tolerances (#4458)
9f36301dc8 [ci] Write wpiformat patch to job summary (#4519)
901fc555f4 [wpimath] Position Delta Odometry for Mecanum (#4514)
4170ec6107 [wpimath] Position Delta Odometry for Swerve (#4493)
fe400f68c5 [docs] Add wpinet to docs build (#4517)
794669b346 [ntcore] Revamp listeners (#4511)
dcfa85a5d5 [ci] Build sanitizers with clang-14 (#4518)
15ad855f1d [ntcore] Add UnitTopic<T> (C++ only) (#4497)
11244a49d9 [wpilib] Add IsConnected function to all gyros (#4465)
1d2e8eb153 [build] Update myRobot deployment (#4515)
ad53fb19b4 [hal] Use new HMB api for addressable LED (#4479)
ba850bac3b [hal] Add more shutdown checks and motor safety shutdown (#4510)
023a5989f8 [ntcore] Fix typo in NetworkServer client connect message (#4512)
c970011ccc [docs] Add Doxygen aliases used by Foonathan memory (#4509)
07a43c3d9a [readme] Document clang-format version and /wpiformat (#4503)
a05b212b04 [ci] Revert changes to wpiformat task from #4501 (#4508)
09faf31b67 [commands] Replace Command HID inheritance with delegation (#4470)
9e1f9c1133 [commands] Add command factories (#4476)
f19d2b9b84 [ci] Add NUMBER environment variable to comment command commit script (#4507)
a28f93863c [ci] Push comment command commit directly to PR (#4506)
c9f61669b8 [ci] Fix comment command commit push (#4505)
dcce5ad3b3 [ci] Update github-script API usage (#4504)
6836e5923d [wpilibc] Restore get duty cycle scale factor (#4502)
335188c652 [dlt] Add deselect/select all buttons to download view (#4499)
60a29dcb99 [glass] Field2D: Add "hidden" option for objects (#4498)
b55d5b3034 [ci] Update deprecated github actions (#4501)
10ed4b3969 [ntcore] Various NT4 fixes (#4474)
4a401b89d7 [hal, wpilib] New DS thread model and implementation (#3787)
c195b4fc46 [wpimath] Clean up PoseEstimator nominal dt docs (#4496)
8f2e34c6a3 [build] Remove wpilib prefix from CMake flat install (#4492)
150d692df7 [wpimath] Remove unused private PoseEstimator function (#4495)
3e5bfff1b5 [wpimath] FromFieldRelativeSpeeds: Add ChassisSpeeds overload (#4494)
9c7e66a27d [commands] C++: Add CommandPtr overload for SetDefaultCommand (#4488)
0ca274866b [build] Fix CMake system library opt-ins (#4487)
dc037f8d41 [commands] Remove EndlessCommand (#4483)
16cdc741cf [wpimath] Add Pose3d(Pose2d) constructor (#4485)
9d5055176d [build] cmake: Allow disabling ntcore build (#4486)
d1e66e1296 [build] Compile all java code with inline string concatenation (#4490)
1fc098e696 Enable log macros to work with no args (#4475)
878cc8defb [wpilib] LiveWindow: Add enableAllTelemetry() (#4480)
8153911160 [build] Fix MSVC runtime archiver to grab default runtime (#4478)
fbdc810887 Upgrade to C++20 (#4239)
396143004c [ntcore] Add ntcoreffi binary (#4471)
1f45732700 [build] Update to 2023.2.4 native-utils and new dependencies (#4473)
574cb41c18 [ntcore] Various fixes (#4469)
d9d6c425e7 [build] Force Java 11 source compatibility (#4472)
58b6484dbe Switch away from NI interrupt manager to custom implementation (#3705)
ca43fe2798 [wpimath] Use Units conversions in ComputerVisionUtil docs (NFC) (#4464)
87a64ccedc [hal] Convert DutyCycle Raw output to be a high time measurement (#4466)
89a3d00297 [commands] Add FinallyDo and HandleInterrupt decorators (#4412)
1497665f96 [commands] Add C++ versions of Java-only decorators (#4457)
27b173374e [wpimath] Add minLinearAccel parameter to DifferentialDriveAccelerationLimiter (#4422)
2a13dba8ac [wpilib] TrajectoryUtil: Fix ambiguous documentation (NFC) (#4461)
77301b126c [ntcore] NetworkTables 4 (#3217)
90cfa00115 [build] cmake: Fix libssh include directory order (#4459)
5cf961edb9 [commands] Refactor lambda-based commands to inherit FunctionalCommand (#4451)
b2276e47de [wpimath] Enable continuous angle input for HolonomicDriveController (#4453)
893b46139a [fieldImages] Add utilities to simplify loading of fields (#4456)
60e29627c0 [commands] C++ unique_ptr migration (#4319)
3b81cf6c35 [wpilib] Improve Color.toString (#4450)
5c067d30a0 [wpinet] WebSocket: Add SendFrames() (#4445)
ceaf493811 [wpiutil] MakeJByteArray: Use span<uint8> instead of string_view (#4446)
10e04e2b13 [examples] FrisbeeBot: Fix reference capture (#4449)
726f67c64b [build] Add exeSplitSetup (#4444)
c7b7624c1c [wpiutil] Add MessagePack utility functions (#4448)
d600529ec0 [wpinet] uv::Async: Add UnsafeSend() (#4447)
b53b3526a2 [wpimath] Add CoordinateSystem conversion for Transform3d (#4443)
38bb23eb18 [wpimath] Add scalar multiply and divide operators to all geometry classes (#4438)
3937ff8221 [wpilib] Remove deprecated Controller class (#4440)
abbfe244b5 [wpilib] Improve Color FromHSV (#4439)
4ddb8aa0dd [sim] Provide function that resets all simulation data (#4016)
a791470de7 Clean up Java warning suppressions (#4433)
17f504f548 [hal,wpilib] Fix SPI Mode Setting (#4434)
773198537c [wpiutil] Add wpi::scope_exit (#4432)
5ac658c8f0 [wpiutil] Logger: Conditionalize around WPI_LOG (#4431)
8767e4a941 [wpiutil] DataLog: Fix SetMetadata output (#4430)
8c4af073f4 [wpiutil] Synchronization: shutdown race protection (#4429)
c79f38584a [build] Fix Java integration tests (#4428)
36c08dd97c [build] Fix cmake install of fmtlib (#4426)
69b7b3dd7d [ci] Remove the Windows cmake job (#4425)
738c75fed8 [readme] Fix formatting/linting link (#4423)
4eb1d03fb3 [wpimath] Document C++ LinearFilter exception (#4417)
ba4ec6c967 [build] Fix clang-tidy false positive on Linux (#4406)
97836f0e55 [commands] Fix ProfiledPIDSubsystem setGoal behavior (#4414)
fdfb85f695 [wpimath] Remove Java LQR constructor that takes a controller gain matrix (#4419)
ab1baf4832 [wpimath] Add rotation matrix constructor to Rotation3d (#4413)
9730032866 [wpimath] Document LQR and KalmanFilter exceptions (#4418)
5b656eecf6 [wpimath] Fix HTML5 entity (#4420)
9ae38eaa7c [commands] Add owning overload to ProxyScheduleCommand (#4405)
cb33bd71df [commands] deprecate withInterrupt decorator (#4407)
d9b4e7b8bf [commands] Revert "Change grouping decorator impl to flatten nested group structures (#3335)" (#4402)
0389bf5214 [hal] REVPH: Improve handling of disconnected CAN Bus (#4169)
4267fa08d1 [wpilibc] ADIS IMUs: Fix memory leak (#4170)
65c8fbd452 [wpilib] MotorControllerGroup: Override setVoltage (#4403)
f36162fddc [wpimath] Improve Discretization internal docs (#4400)
5149f7d894 [wpimath] Add two-vector Rotation3d constructor (#4398)
20b5bed1cb [wpimath] Clean up Java Quaternion class (#4399)
f18dd1905d [build] Include all thirdparty sources in distribution (#4397)
aa9d7f1cdc [wpiutil] Import foonathan memory (#4306)
2742662254 [ci] Remove a couple of obsolete clang-tidy checks (#4396)
a5df391166 [hal, wpilib] Fix up DIO pulse API (#4387)
59e6706b75 [glass] Turn on docking by default
8461bb1e03 [glass] Add support for saving docking info
b873e208b4 [wpigui] Add support for imgui config flags
873e72df8c [build] Update imgui to 1.88 docking branch
c8bd6fc5b4 [ci] Fix comment-command (take 2) (#4395)
fed68b83b4 [ci] Fix comment-command action not running runners (#4393)
0ef8a4e1df [wpimath] Support formatting more Eigen types (#4391)
c393b3b367 [build] Update to native utils 2023.1.0 and Gradle 7.5.1 (#4392)
b5a17f762c [wpimath] Add direction to slew rate limiter (#4377)
fafc81ed1a [wpiutil] Upgrade to fmt 9.1.0 (#4389)
cc56bdc787 [wpiutil] SafeThread: Add Synchronization object variant (#4382)
4254438d8d [commands] Mark command group lifecycle methods as final (#4385)
97c15af238 [wpimath] LinearSystemId: Fix docs, move C++ impls out of header (#4388)
d22ff8a158 [wpiutil] Add JNI access to C++ stderr (#4381)
fdb5a2791f [wpiutil] jni_util: Add Mac-friendly MakeJLongArray/JArrayRef (#4383)
c3a93fb995 [commands] Revamp Interruptible (#4192)
f2a8d38d2a [commands] Rename Command.repeat to repeatedly (#4379)
9e24c6eac0 [wpiutil] Logger: paren-protect instance usage in macro (#4384)
fe4d12ce22 [wpimath] Add LTV controller derivations and make enums private (#4380)
eb08486039 [build] Fix MacOS binary rpath generation (#4376)
ccf83c634a [build] Use native-utils platform names instead of raw strings (#4375)
3fd69749e7 [docs] Upgrade to doxygen 1.9.4 (#4370)
594df5fc08 [wpinet] uv/util.h: Pull in ws2_32.lib on Windows for ntohs (#4371)
539070820d [ci] Enable asan for wpinet and wpiutil (#4369)
564a56d99b [wpinet] Fix memory leak in WorkerThreadTest (#4368)
5adf50d93c [upstream_utils] Refactor upstream_utils scripts (#4367)
d80e8039d7 [wpiutil] Suppress fmtlib clang-tidy warning in C++20 consteval contexts (#4364)
0e6d67b23b [upstream_utils] Remove yapf format disable comment (#4366)
be5270697a [build] Suppress enum-enum deprecation warning in OpenCV (#4365)
8d28851263 Add Rosetta install command to build requirements (#4363)
3d2115c93e [wpinet] include-what-you-use in MulticastTest (#4360)
91002ae3cc [wpimath] Upgrade to Drake 1.6.0 (#4361)
148c18e658 [wpinet] Upgrade to libuv 1.44.2 (#4362)
a2a5c926b6 Fix clang-tidy warnings (#4359)
ea6b1d8449 [wpiutil] Remove unused ManagedStatic class (#4358)
ac9be78e27 Use stricter C++ type conversions (#4357)
151dabb2af [wpiutil] Upgrade to fmt 9.0.0 (#4337)
340465c929 [ci] Upgrade to clang-format and clang-tidy 14 (NFC) (#4347)
d45bcddd15 [examples] Add comments to StateSpaceDifferentialDrive (#4341)
0e0786331a Update LLVM libraries to 14.0.6 (#4350)
c5db23f296 [wpimath] Add Eigen sparse matrix and iterative solver support (#4349)
44abc8dfa6 [upstream_utils] Remove git version from upstream patches (#4351)
3fdb2f767d [wpimath] Add comments with Ramsete equations (#4348)
0485f05da9 [wpilibjExamples] Upgrade jacoco to match allwpilib (#4346)
0a5eb65231 [wpinet] Handle empty txt block for mdns announcer (#4072)
19ffebaf3e [wpilib] Add reference to I2C Lockup to API Docs (NFC) (#4340)
ce1a90d639 [hal] Replace SerialHelper "goto done" with continue (#4342)
d25af48797 [ci] Make upstream_utils CI fail on untracked files (#4339)
ebb836dacb [examples] Fix negations in event loop examples (#4334)
d83e202f00 [upstream_utils] Update paths in update_fmt.py (#4338)
3ccf806064 [wpimath] Remove redundant LinearFilter.finiteDifference() argument (#4335)
6f1e01f8bd [wpimath] Document example of online filtering for LinearFilter.finiteDifference() (#4336)
1023c34b1c [readme] Update location of ni-libraries (#4333)
faa29d596c [wpilib] Improve Notifier docs (NFC) (#4326)
add00a96ed [wpimath] Improve DifferentialDriveAccelerationLimiter docs (NFC) (#4323)
82fac41244 [wpimath] Better document trackwidth parameters (NFC) (#4324)
5eb44e22a9 Format Python scripts with black (NFC) (#4325)
2e09fa7325 [build] Fix mpack cmake (#4322)
fe3c24b1ee [command] Add ignoringDisable decorator (#4305)
aa221597bc [build] Add M1 builds, change arm name, update to 2023 deps (#4315)
579a8ee229 [ci] Use one worker for Windows release Gradle build (#4318)
5105c5eab6 [wpilibj] Change "final" to "exit" in the IterativeRobotBase JavaDoc (NFC) (#4317)
787fe6e7a5 [wpiutil] Separate third party libraries (#4190)
6671f8d099 [wpigui] Update portable file dialogs (#4316)
9ac9b69aa2 [command] Reorder Scheduler operations (#4261)
e61028cb18 [build] halsim_gui: Add wpinet dependency (#4313)
661d23eaf5 [glass] Add precision setting for NetworkTable view (#4311)
666040e3e5 [hal] Throw exceptions for invalid sizes in I2C and SPI JNI (#4312)
aebc272449 [build] Upgrade to spotbugs Gradle plugin 5.0.8 (#4310)
fd884581e4 [wpilib] Add BooleanEvent/Trigger factories on HID classes (#4247)
9b1bf5c7f1 [wpimath] Move Drake and Eigen to thirdparty folders (#4307)
c9e620a920 [wpilibc] Change EventLoop data structure to vector (#4304)
41d40dd62f [wpinet] Fix libuv unused variable warning on Mac (#4299)
30f5b68264 [wpinet] Fix JNI loading error (#4295)
f7b3f4b90e [examples] Getting Started: Change Joystick to XboxController (#4194)
a99c11c14c [wpimath] Replace UKF implementation with square root form (#4168)
45b7fc445b [wpilib] Add EventLoop (#4104)
16a4888c52 [wpilib] Default off LiveWindow telemetry (#4301)
17752f1337 [ci] Split debug and release Windows builds (#4277)
abb45a68db [commands] Remove custom test wrappers (#4296)
1280a54ef3 [upstream_utils]: Make work with Python 3.8 (#4298)
f2d243fa68 [build] Change defaults for Java lints (#4300)
a4787130f4 Update using development build to work with 2023 gradlerio (#4294)
af7985e46c [wpiutil] Use invoke_result_t instead of result_of in future.h (#4293)
e9d1b5c2d0 [hal] Remove deprecated SimDevice functions (#4209)
45b598d236 [wpilibj] Add toString() methods to Color and Color8Bit (#4286)
fc37265da5 [wpimath] Add angle measurement convention to ArmFeedforward docs (NFC) (#4285)
a4ec13eb0e [wpilibjexamples] Remove unnecessary voltage desaturation
2fa52007af [wpilibc] Use GetBatteryVoltage() in MotorController::SetVoltage
d9f9cd1140 [wpimath] Reset prev_time on pose estimator reset (#4283)
8b6df88783 [wpilibj] Tachometer.getFrequency(): Fix bug (#4281)
345cff08c0 [wpiutil] Make wpi::array constexpr (#4278)
57428112ac [wpimath] Upgrade to Drake v1.3.0 (#4279)
a18d4ff154 [build] Fix tools not being copied when built with -Ponly* (#4276)
d1cd07b9f3 [wpigui] Add OpenURL (#4273)
e67f8e917a [glass] Use glfwSetKeyCallback for Enter key remap (#4275)
be2fedfe50 [wpimath] Add stdexcept include for std::invalid_argument (IWYU) (#4274)
7ad2be172e [build] Update native-utils to 2023.0.1 (#4272)
abc605c9c9 [ci] Update workflows to 20.04 base image (#4271)
3e94805220 [wpiutil] Reduce llvm collections patches (#4268)
db2e1d170e [upstream_utils] Document how to update thirdparty libraries (#4253)
96ebdcaf16 [wpimath] Remove unused Eigen AutoDiff module (#4267)
553b2a3b12 [upstream_utils] Fix stackwalker (#4265)
3e13ef42eb [wpilibc] Add missing std::array #include (include-what-you-use) (#4266)
d651a1fcec Fix internal deprecation warnings (#4257)
b193b318c1 [commands] Add unless() decorator (#4244)
ef3714223b [commands] Remove docs reference to obsolete interrupted() method (NFC) (#4262)
3d8dbbbac3 [readme] Add quickstart (#4225)
013efdde25 [wpinet] Wrap a number of newer libuv features (#4260)
816aa4e465 [wpilib] Add Pneumatics sim classes (#4033)
046c2c8972 [wpilibc] Rename SpeedControllerGroupTest.cpp (#4258)
d80e9cdf64 [upstream_utils] Use shallow clones for thirdparty repos (#4255)
7576136b4a [upstream_utils] Make update_llvm.py executable (#4254)
c3b223ce60 [wpiutil] Vendor llvm and update to 13.0.0 (#4224)
5aa67f56e6 [wpimath] Clean up math comments (#4252)
fff4d1f44e [wpimath] Extend Eigen warning suppression to GCC 12 (#4251)
0d9956273c [wpimath] Add CoordinateSystem.convert() translation and rotation overloads (#4227)
3fada4e0b4 [wpinet] Update to libuv 1.44.1 (#4232)
65b23ac45e [wpilibc] Fix return value of DriverStation::GetJoystickAxisType() (#4230)
4ac34c0141 [upstream_utils] Cleanup update_libuv.py (#4249)
8bd614bb1e [upstream_utils] Use "git am" instead of "git apply" for patches (#4248)
4253d6d5f0 [upstream_utils] Apply "git am" patches individually (#4250)
6a4752dcdc Fix GCC 12.1 warning false positives (#4246)
5876b40f08 [wpimath] Memoize CoordinateSystem and CoordinateAxis statics (#4241)
5983434a70 [cameraserver] Replace IterativeRobot in comment sample code with TimedRobot (#4238)
a3d44a1e69 [wpimath] Add Translation2d.getAngle() (#4217)
d364bbd5a7 [upstream_utils] Give vendor update scripts execute permissions (#4226)
f341e1b2be [wpimath] Document standard coordinate systems better (NFC) (#4228)
9af389b200 [wpinet] AddrToName: Initialize name (#4229)
2ae4adf2d7 [ci] Add wpiformat command to PRs (#4223)
178b2a1e88 Contributing.md: Correct version of clang-format used (#4222)
18db343cdc [wpiutil, wpinet] Vendor libuv, stack walker (#4219)
f0c821282a [build] Use artifactory mirror (#4220)
d673ead481 [wpinet] Move network portions of wpiutil into new wpinet library (#4077)
b33715db15 [wpimath] Add CoordinateSystem class (#4214)
99424ad562 [sim] Allow creating a PWMSim object from a PWMMotorController (#4039)
dc6f641fd2 [wpimath] PIDController: Reset position and velocity error when reset() is called. (#4064)
f20a20f3f1 [wpimath] Add 3D geometry classes (#4175)
708a4bc3bc [wpimath] Conserve previously calculated swerve module angles when updating states for stationary ChassisSpeeds (#4208)
ef7ed21a9d [wpimath] Improve accuracy of ComputerVisionUtil.calculateDistanceToTarget() (#4215)
b1abf455c1 [wpimath] LTVUnicycleController: Use LUT, provide default hyperparameters (#4213)
d5456cf278 [wpimath] LTVDifferentialDriveController: Remove unused variable (#4212)
99343d40ba [command] Remove old command-based framework (#4211)
ee03a7ad3b Remove most 2022 deprecations (#4205)
ce1a7d698a [wpimath] Refactor WheelVoltages inner class to a separate file (#4203)
87bf70fa8e [wpimath] Add LTV controllers (#4094)
ebd2a303bf [wpimath] Remove deprecated MakeMatrix() function (#4202)
e28776d361 [wpimath] LinearSystemLoop: Add extern templates for common cases
dac1429aa9 [wpimath] LQR: Use extern template instead of Impl class
e767605e94 [wpimath] Add typedefs for common types
97c493241f [wpimath] UnscentedKalmanFilter: Move implementation out-of-line
8ea90d8bc9 [wpimath] ExtendedKalmanFilter: Move implementation out-of-line
ae7b1851ec [wpimath] KalmanFilter: Use extern template instead of Impl class
e3d62c22d3 [wpimath] Add extern templates for common cases
7200c4951d [wpiutil] SymbolExports: Add WPILIB_IMPORTS for dllimport
84056c9347 [wpiutil] SymbolExports: Add EXPORT_TEMPLATE_DECLARE/DEFINE
09cf6eeecb [wpimath] ApplyDeadband: add a scale param (#3865)
03230fc842 [build,ci] Enable artifactory build cache (#4200)
63cf3aaa3f [examples] Don't square ArcadeDrive inputs in auto (#4201)
18ff694f02 [wpimath] Add Rotation2d.fromRadians factory (#4178)
4f79ceedd9 [wpilibc] Add missing #include (#4198)
f7ca72fb41 [command] Rename PerpetualCommand to EndlessCommand (#4177)
a06b3f0307 [hal] Correct documentation on updateNotifierAlarm (#4156)
d926dd1610 [wpimath] Fix pose estimator performance (#4111)
51bc893bc5 [wpiutil] CircularBuffer: Change Java package-private methods to public (#4181)
fbe761f7f6 [build] Increase Gradle JVM heap size (#4172)
5ebe911933 [wpimath] Add DifferentialDriveAccelerationLimiter (#4091)
3919250da2 [wpilibj] Remove finalizers (#4158)
b3aee28388 [commands] Allow BooleanSupplier for Trigger operations (#4103)
9d20ab3024 [wpilib] Allow disabling ElevatorSim gravity (#4145)
aaa69f6717 [ci] Remove 32-bit Windows builds (#4078)
355a11a414 Update Java linters and fix new PMD errors (#4157)
ffc69d406c [examples] Reduce suggested acceleration in Ramsete example (#4171)
922d50079a [wpimath] Units: fix comment in degreesToRotations (NFC) (#4159)
dd163b62ae [wpimath] Rotation2d: Add factory method that uses rotations (#4166)
bd80e220b9 [ci] Upgrade CMake actions (#4161)
aef4b16d4c [wpimath] Remove unnecessary NOLINT in LinearPlantInversionFeedforward (NFC) (#4155)
975171609e [wpilib] Compressor: Rename enabled to isEnabled (#4147)
5bf46a9093 [wpimath] Add ComputerVisionUtil (#4124)
f27a1f9bfb [commands] Fix JoystickButton.getAsBoolean (#4131)
1b26e2d5da [commands] Add RepeatCommand (#4009)
88222daa3d [hal] Fix misspelling in AnalogInput/Output docs (NFC) (#4153)
81c5b41ce1 [wpilibj] Document MechanismLigament2d angle unit (NFC) (#4142)
9650e6733e [wpiutil] DataLog: Document finish and thread safety (NFC) (#4140)
c8905ec29a [wpimath] Remove ImplicitModelFollower dt argument (#4119)
b4620f01f9 [wpimath] Fix Rotation2d interpolation in Java (#4125)
2e462a19d3 [wpimath] Constexprify units unary operators (#4138)
069f932e59 [build] Fix gl3w cmake build (#4139)
126e3de91a [wpilibc] Remove unused SetPriority() call from Ultrasonic (#4123)
ba0dccaae4 [wpimath] Fix reference to Rotation2d.fromRadians() (#4118)
e1b6e5f212 [wpilib] Improve MotorSafety documentation (NFC) (#4120)
8d79dc8738 [wpimath] Add ImplicitModelFollower (#4056)
78108c2aba [wpimath] Fix PIDController having incorrect error after calling SetSetpoint() (#4070)
cdafc723fb [examples] Remove unused LinearPlantInversionFeedforward includes (#4069)
0d70884dce [wpimath] Add InterpolatedTreeMap (#4073)
765efa325e [wpimath] Remove redundant column index from vectors (#4116)
89ffcbbe41 [wpimath] Update TrapezoidProfile class name in comment (NFC) (#4107)
95ae23b0e7 [wpimath] Improve EKF numerical stability (#4093)
d5cb6fed67 [wpimath] Support zero cost entries in MakeCostMatrix() (#4100)
d0fef18378 [wpimath] Remove redundant `this.` from ExtendedKalmanFilter.java (#4115)
d640c0f41f [wpimath] Fix pose estimator local measurement standard deviation docs (NFC) (#4113)
a2fa5e3ff7 [wpilibc] BatterySim: Provide non-initializer list versions of Calculate (#4076)
a3eea9958e [hal] Add link to FRC CAN Spec (NFC) (#4086)
db27331d7b [wpilib] Update DifferentialDrive docs (NFC) (#4085)
fdfb31f164 [dlt] Export boolean[] values (#4082)
f93c3331b3 [wpigui] disable changing directory when initializing on MacOS (#4092)
ab7ac4fbb9 [build] Fix various warnings in cmake builds (#4081)
bc39a1a293 [wpilibc] Fix moved pneumatics objects not destructing properly (#4068)
2668130e70 [wpimath] Remove SwerveDrivePoseEstimator encoder reset warning (#4066)
d27ed3722b [ci] Set actions workflow concurrency (#4060)
dae18308c9 [wpimath] Minor fixes to Rotation2d docs (NFC) (#4055)
d66555e42f [datalogtool] Add datalogtool
9f52d8a3b1 [wpilib] DriverStation: Add DataLog support for modes and joystick data
757ea91932 [wpilib] Add DataLogManager
02a804f1c5 [ntcore] Add DataLog support
9b500df0d9 [wpiutil] Add high speed data logging
5a89575b3a [wpiutil] Import customized LLVM MemoryBuffer
b8c4d7527b [wpiutil] Add MappedFileRegion
ac5d46cfa7 [wpilibc] Fix ProfiledPID SetTolerance default velocity value (#4054)
bc9e96e86f [wpilib] Absolute Encoder API and behavior fixes (#4052)
f88c435dd0 [hal] Add mechanism to cancel all periodic callbacks (#4049)

Change-Id: I49aa5b08abbefc7a045e99e19d48ce2cd8fc4d1b
git-subtree-dir: third_party/allwpilib
git-subtree-split: 83f1860047c86aa3330fcb41caf3b2047e074804
Signed-off-by: James Kuszmaul <jabukuszmaul+collab@gmail.com>
diff --git a/hal/src/dev/native/cpp/main.cpp b/hal/src/dev/native/cpp/main.cpp
index 721c0f6..8614aba 100644
--- a/hal/src/dev/native/cpp/main.cpp
+++ b/hal/src/dev/native/cpp/main.cpp
@@ -8,5 +8,5 @@
 
 int main() {
   fmt::print("Hello World\n");
-  fmt::print("{}\n", HAL_GetRuntimeType());
+  fmt::print("{}\n", static_cast<int32_t>(HAL_GetRuntimeType()));
 }
diff --git a/hal/src/generate/FRCNetComm.java.in b/hal/src/generate/FRCNetComm.java.in
index ce21889..703e034 100644
--- a/hal/src/generate/FRCNetComm.java.in
+++ b/hal/src/generate/FRCNetComm.java.in
@@ -7,7 +7,6 @@
 /**
  * JNI wrapper for library <b>FRC_NetworkCommunication</b><br>.
  */
-@SuppressWarnings({"MethodName", "LineLength"})
 public class FRCNetComm {
   /**
    * Resource type from UsageReporting.
diff --git a/hal/src/main/java/edu/wpi/first/hal/AccumulatorResult.java b/hal/src/main/java/edu/wpi/first/hal/AccumulatorResult.java
index 9340748..441ce9f 100644
--- a/hal/src/main/java/edu/wpi/first/hal/AccumulatorResult.java
+++ b/hal/src/main/java/edu/wpi/first/hal/AccumulatorResult.java
@@ -5,12 +5,11 @@
 package edu.wpi.first.hal;
 
 /** Structure for holding the values stored in an accumulator. */
+@SuppressWarnings("MemberName")
 public class AccumulatorResult {
   /** The total value accumulated. */
-  @SuppressWarnings("MemberName")
   public long value;
   /** The number of sample value was accumulated over. */
-  @SuppressWarnings("MemberName")
   public long count;
 
   /**
diff --git a/hal/src/main/java/edu/wpi/first/hal/AddressableLEDJNI.java b/hal/src/main/java/edu/wpi/first/hal/AddressableLEDJNI.java
index c732ec6..1dd80a7 100644
--- a/hal/src/main/java/edu/wpi/first/hal/AddressableLEDJNI.java
+++ b/hal/src/main/java/edu/wpi/first/hal/AddressableLEDJNI.java
@@ -4,7 +4,6 @@
 
 package edu.wpi.first.hal;
 
-@SuppressWarnings("AbbreviationAsWordInName")
 public class AddressableLEDJNI extends JNIWrapper {
   public static native int initialize(int pwmHandle);
 
diff --git a/hal/src/main/java/edu/wpi/first/hal/AnalogJNI.java b/hal/src/main/java/edu/wpi/first/hal/AnalogJNI.java
index e2deadd..b80388d 100644
--- a/hal/src/main/java/edu/wpi/first/hal/AnalogJNI.java
+++ b/hal/src/main/java/edu/wpi/first/hal/AnalogJNI.java
@@ -111,6 +111,5 @@
 
   public static native boolean getAnalogTriggerOutput(int analogTriggerHandle, int type);
 
-  @SuppressWarnings("AbbreviationAsWordInName")
   public static native int getAnalogTriggerFPGAIndex(int analogTriggerHandle);
 }
diff --git a/hal/src/main/java/edu/wpi/first/hal/CANAPIJNI.java b/hal/src/main/java/edu/wpi/first/hal/CANAPIJNI.java
index 31cf973..227da4d 100644
--- a/hal/src/main/java/edu/wpi/first/hal/CANAPIJNI.java
+++ b/hal/src/main/java/edu/wpi/first/hal/CANAPIJNI.java
@@ -4,7 +4,6 @@
 
 package edu.wpi.first.hal;
 
-@SuppressWarnings("AbbreviationAsWordInName")
 public class CANAPIJNI extends JNIWrapper {
   public static native int initializeCAN(int manufacturer, int deviceId, int deviceType);
 
diff --git a/hal/src/main/java/edu/wpi/first/hal/CANData.java b/hal/src/main/java/edu/wpi/first/hal/CANData.java
index 94e9f57..0a644f6 100644
--- a/hal/src/main/java/edu/wpi/first/hal/CANData.java
+++ b/hal/src/main/java/edu/wpi/first/hal/CANData.java
@@ -4,14 +4,10 @@
 
 package edu.wpi.first.hal;
 
+@SuppressWarnings("MemberName")
 public class CANData {
-  @SuppressWarnings("MemberName")
   public final byte[] data = new byte[8];
-
-  @SuppressWarnings("MemberName")
   public int length;
-
-  @SuppressWarnings("MemberName")
   public long timestamp;
 
   /**
diff --git a/hal/src/main/java/edu/wpi/first/hal/CANStreamMessage.java b/hal/src/main/java/edu/wpi/first/hal/CANStreamMessage.java
new file mode 100644
index 0000000..bdb2112
--- /dev/null
+++ b/hal/src/main/java/edu/wpi/first/hal/CANStreamMessage.java
@@ -0,0 +1,35 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.hal;
+
+public class CANStreamMessage {
+  @SuppressWarnings("MemberName")
+  public final byte[] data = new byte[8];
+
+  @SuppressWarnings("MemberName")
+  public int length;
+
+  @SuppressWarnings("MemberName")
+  public long timestamp;
+
+  @SuppressWarnings("MemberName")
+  public int messageID;
+
+  /**
+   * API used from JNI to set the data.
+   *
+   * @param length Length of packet in bytes.
+   * @param messageID CAN message ID of the message.
+   * @param timestamp CAN frame timestamp in microseconds.
+   * @return Buffer containing CAN frame.
+   */
+  @SuppressWarnings("PMD.MethodReturnsInternalArray")
+  public byte[] setStreamData(int length, int messageID, long timestamp) {
+    this.messageID = messageID;
+    this.length = length;
+    this.timestamp = timestamp;
+    return data;
+  }
+}
diff --git a/hal/src/main/java/edu/wpi/first/hal/CTREPCMJNI.java b/hal/src/main/java/edu/wpi/first/hal/CTREPCMJNI.java
index 20d5cb8..e94b183 100644
--- a/hal/src/main/java/edu/wpi/first/hal/CTREPCMJNI.java
+++ b/hal/src/main/java/edu/wpi/first/hal/CTREPCMJNI.java
@@ -4,7 +4,6 @@
 
 package edu.wpi.first.hal;
 
-@SuppressWarnings("AbbreviationAsWordInName")
 public class CTREPCMJNI extends JNIWrapper {
   public static native int initialize(int module);
 
diff --git a/hal/src/main/java/edu/wpi/first/hal/DIOJNI.java b/hal/src/main/java/edu/wpi/first/hal/DIOJNI.java
index dab1aaf..689a95e 100644
--- a/hal/src/main/java/edu/wpi/first/hal/DIOJNI.java
+++ b/hal/src/main/java/edu/wpi/first/hal/DIOJNI.java
@@ -4,7 +4,6 @@
 
 package edu.wpi.first.hal;
 
-@SuppressWarnings("AbbreviationAsWordInName")
 public class DIOJNI extends JNIWrapper {
   public static native int initializeDIOPort(int halPortHandle, boolean input);
 
@@ -22,7 +21,9 @@
 
   public static native boolean getDIODirection(int dioPortHandle);
 
-  public static native void pulse(int dioPortHandle, double pulseLength);
+  public static native void pulse(int dioPortHandle, double pulseLengthSeconds);
+
+  public static native void pulseMultiple(long channelMask, double pulseLengthSeconds);
 
   public static native boolean isPulsing(int dioPortHandle);
 
@@ -38,5 +39,7 @@
 
   public static native void setDigitalPWMDutyCycle(int pwmGenerator, double dutyCycle);
 
+  public static native void setDigitalPWMPPS(int pwmGenerator, double dutyCycle);
+
   public static native void setDigitalPWMOutputChannel(int pwmGenerator, int channel);
 }
diff --git a/hal/src/main/java/edu/wpi/first/hal/DMAJNI.java b/hal/src/main/java/edu/wpi/first/hal/DMAJNI.java
index 21c06f1..9a0cfeb 100644
--- a/hal/src/main/java/edu/wpi/first/hal/DMAJNI.java
+++ b/hal/src/main/java/edu/wpi/first/hal/DMAJNI.java
@@ -4,7 +4,6 @@
 
 package edu.wpi.first.hal;
 
-@SuppressWarnings("AbbreviationAsWordInName")
 public class DMAJNI extends JNIWrapper {
   public static native int initialize();
 
diff --git a/hal/src/main/java/edu/wpi/first/hal/DMAJNISample.java b/hal/src/main/java/edu/wpi/first/hal/DMAJNISample.java
index 78a0e99..22f21c8 100644
--- a/hal/src/main/java/edu/wpi/first/hal/DMAJNISample.java
+++ b/hal/src/main/java/edu/wpi/first/hal/DMAJNISample.java
@@ -7,7 +7,6 @@
 import java.util.HashMap;
 import java.util.Map;
 
-@SuppressWarnings("AbbreviationAsWordInName")
 public class DMAJNISample {
   private static final int kEnable_Accumulator0 = 8;
   private static final int kEnable_Accumulator1 = 9;
diff --git a/hal/src/main/java/edu/wpi/first/hal/DriverStationJNI.java b/hal/src/main/java/edu/wpi/first/hal/DriverStationJNI.java
new file mode 100644
index 0000000..f40a38c
--- /dev/null
+++ b/hal/src/main/java/edu/wpi/first/hal/DriverStationJNI.java
@@ -0,0 +1,137 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.hal;
+
+import java.nio.ByteBuffer;
+
+public class DriverStationJNI extends JNIWrapper {
+  public static native void observeUserProgramStarting();
+
+  public static native void observeUserProgramDisabled();
+
+  public static native void observeUserProgramAutonomous();
+
+  public static native void observeUserProgramTeleop();
+
+  public static native void observeUserProgramTest();
+
+  public static void report(int resource, int instanceNumber) {
+    report(resource, instanceNumber, 0, "");
+  }
+
+  public static void report(int resource, int instanceNumber, int context) {
+    report(resource, instanceNumber, context, "");
+  }
+
+  /**
+   * Report the usage of a resource of interest.
+   *
+   * <p>Original signature: <code>uint32_t report(tResourceType, uint8_t, uint8_t, const
+   * char*)</code>
+   *
+   * @param resource one of the values in the tResourceType above (max value 51).
+   * @param instanceNumber an index that identifies the resource instance.
+   * @param context an optional additional context number for some cases (such as module number).
+   *     Set to 0 to omit.
+   * @param feature a string to be included describing features in use on a specific resource.
+   *     Setting the same resource more than once allows you to change the feature string.
+   * @return TODO
+   */
+  public static native int report(int resource, int instanceNumber, int context, String feature);
+
+  public static native int nativeGetControlWord();
+
+  @SuppressWarnings("MissingJavadocMethod")
+  public static void getControlWord(ControlWord controlWord) {
+    int word = nativeGetControlWord();
+    controlWord.update(
+        (word & 1) != 0,
+        ((word >> 1) & 1) != 0,
+        ((word >> 2) & 1) != 0,
+        ((word >> 3) & 1) != 0,
+        ((word >> 4) & 1) != 0,
+        ((word >> 5) & 1) != 0);
+  }
+
+  private static native int nativeGetAllianceStation();
+
+  public static final int kRed1AllianceStation = 0;
+  public static final int kRed2AllianceStation = 1;
+  public static final int kRed3AllianceStation = 2;
+  public static final int kBlue1AllianceStation = 3;
+  public static final int kBlue2AllianceStation = 4;
+  public static final int kBlue3AllianceStation = 5;
+
+  @SuppressWarnings("MissingJavadocMethod")
+  public static AllianceStationID getAllianceStation() {
+    switch (nativeGetAllianceStation()) {
+      case kRed1AllianceStation:
+        return AllianceStationID.Red1;
+      case kRed2AllianceStation:
+        return AllianceStationID.Red2;
+      case kRed3AllianceStation:
+        return AllianceStationID.Red3;
+      case kBlue1AllianceStation:
+        return AllianceStationID.Blue1;
+      case kBlue2AllianceStation:
+        return AllianceStationID.Blue2;
+      case kBlue3AllianceStation:
+        return AllianceStationID.Blue3;
+      default:
+        return null;
+    }
+  }
+
+  public static final int kMaxJoystickAxes = 12;
+  public static final int kMaxJoystickPOVs = 12;
+  public static final int kMaxJoysticks = 6;
+
+  public static native int getJoystickAxes(byte joystickNum, float[] axesArray);
+
+  public static native int getJoystickAxesRaw(byte joystickNum, int[] rawAxesArray);
+
+  public static native int getJoystickPOVs(byte joystickNum, short[] povsArray);
+
+  public static native int getJoystickButtons(byte joystickNum, ByteBuffer count);
+
+  public static native void getAllJoystickData(
+      float[] axesArray, byte[] rawAxesArray, short[] povsArray, long[] buttonsAndMetadata);
+
+  public static native int setJoystickOutputs(
+      byte joystickNum, int outputs, short leftRumble, short rightRumble);
+
+  public static native int getJoystickIsXbox(byte joystickNum);
+
+  public static native int getJoystickType(byte joystickNum);
+
+  public static native String getJoystickName(byte joystickNum);
+
+  public static native int getJoystickAxisType(byte joystickNum, byte axis);
+
+  public static native double getMatchTime();
+
+  public static native int getMatchInfo(MatchInfoData info);
+
+  public static native int sendError(
+      boolean isError,
+      int errorCode,
+      boolean isLVCode,
+      String details,
+      String location,
+      String callStack,
+      boolean printMsg);
+
+  public static native int sendConsoleLine(String line);
+
+  public static native void refreshDSData();
+
+  public static native void provideNewDataEventHandle(int handle);
+
+  public static native void removeNewDataEventHandle(int handle);
+
+  public static native boolean getOutputsActive();
+
+  private DriverStationJNI() {}
+}
diff --git a/hal/src/main/java/edu/wpi/first/hal/DutyCycleJNI.java b/hal/src/main/java/edu/wpi/first/hal/DutyCycleJNI.java
index a1bba6f..f2737d8 100644
--- a/hal/src/main/java/edu/wpi/first/hal/DutyCycleJNI.java
+++ b/hal/src/main/java/edu/wpi/first/hal/DutyCycleJNI.java
@@ -13,10 +13,9 @@
 
   public static native double getOutput(int handle);
 
-  public static native int getOutputRaw(int handle);
+  public static native int getHighTime(int handle);
 
   public static native int getOutputScaleFactor(int handle);
 
-  @SuppressWarnings("AbbreviationAsWordInName")
   public static native int getFPGAIndex(int handle);
 }
diff --git a/hal/src/main/java/edu/wpi/first/hal/EncoderJNI.java b/hal/src/main/java/edu/wpi/first/hal/EncoderJNI.java
index f67e3a5..50ecc91 100644
--- a/hal/src/main/java/edu/wpi/first/hal/EncoderJNI.java
+++ b/hal/src/main/java/edu/wpi/first/hal/EncoderJNI.java
@@ -50,7 +50,6 @@
   public static native void setEncoderIndexSource(
       int encoderHandle, int digitalSourceHandle, int analogTriggerType, int indexingType);
 
-  @SuppressWarnings("AbbreviationAsWordInName")
   public static native int getEncoderFPGAIndex(int encoderHandle);
 
   public static native int getEncoderEncodingScale(int encoderHandle);
diff --git a/hal/src/main/java/edu/wpi/first/hal/HAL.java b/hal/src/main/java/edu/wpi/first/hal/HAL.java
index 53198e0..68e6ec8 100644
--- a/hal/src/main/java/edu/wpi/first/hal/HAL.java
+++ b/hal/src/main/java/edu/wpi/first/hal/HAL.java
@@ -4,7 +4,6 @@
 
 package edu.wpi.first.hal;
 
-import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -12,10 +11,7 @@
  * JNI Wrapper for HAL<br>
  * .
  */
-@SuppressWarnings({"AbbreviationAsWordInName", "MethodName"})
 public final class HAL extends JNIWrapper {
-  public static native void waitForDSData();
-
   public static native boolean initialize(int timeout, int mode);
 
   public static native void shutdown();
@@ -117,15 +113,13 @@
     }
   }
 
-  public static native void observeUserProgramStarting();
+  public static native boolean getBrownedOut();
 
-  public static native void observeUserProgramDisabled();
+  public static native boolean getSystemActive();
 
-  public static native void observeUserProgramAutonomous();
+  public static native int getPortWithModule(byte module, byte channel);
 
-  public static native void observeUserProgramTeleop();
-
-  public static native void observeUserProgramTest();
+  public static native int getPort(byte channel);
 
   public static void report(int resource, int instanceNumber) {
     report(resource, instanceNumber, 0, "");
@@ -135,109 +129,9 @@
     report(resource, instanceNumber, context, "");
   }
 
-  /**
-   * Report the usage of a resource of interest.
-   *
-   * <p>Original signature: <code>uint32_t report(tResourceType, uint8_t, uint8_t, const
-   * char*)</code>
-   *
-   * @param resource one of the values in the tResourceType above (max value 51).
-   * @param instanceNumber an index that identifies the resource instance.
-   * @param context an optional additional context number for some cases (such as module number).
-   *     Set to 0 to omit.
-   * @param feature a string to be included describing features in use on a specific resource.
-   *     Setting the same resource more than once allows you to change the feature string.
-   * @return TODO
-   */
-  public static native int report(int resource, int instanceNumber, int context, String feature);
-
-  public static native int nativeGetControlWord();
-
-  @SuppressWarnings("MissingJavadocMethod")
-  public static void getControlWord(ControlWord controlWord) {
-    int word = nativeGetControlWord();
-    controlWord.update(
-        (word & 1) != 0,
-        ((word >> 1) & 1) != 0,
-        ((word >> 2) & 1) != 0,
-        ((word >> 3) & 1) != 0,
-        ((word >> 4) & 1) != 0,
-        ((word >> 5) & 1) != 0);
+  public static int report(int resource, int instanceNumber, int context, String feature) {
+    return DriverStationJNI.report(resource, instanceNumber, context, feature);
   }
 
-  private static native int nativeGetAllianceStation();
-
-  @SuppressWarnings("MissingJavadocMethod")
-  public static AllianceStationID getAllianceStation() {
-    switch (nativeGetAllianceStation()) {
-      case 0:
-        return AllianceStationID.Red1;
-      case 1:
-        return AllianceStationID.Red2;
-      case 2:
-        return AllianceStationID.Red3;
-      case 3:
-        return AllianceStationID.Blue1;
-      case 4:
-        return AllianceStationID.Blue2;
-      case 5:
-        return AllianceStationID.Blue3;
-      default:
-        return null;
-    }
-  }
-
-  @SuppressWarnings("MissingJavadocMethod")
-  public static native boolean isNewControlData();
-
-  @SuppressWarnings("MissingJavadocMethod")
-  public static native void releaseDSMutex();
-
-  @SuppressWarnings("MissingJavadocMethod")
-  public static native boolean waitForDSDataTimeout(double timeout);
-
-  public static final int kMaxJoystickAxes = 12;
-  public static final int kMaxJoystickPOVs = 12;
-
-  public static native short getJoystickAxes(byte joystickNum, float[] axesArray);
-
-  public static native short getJoystickPOVs(byte joystickNum, short[] povsArray);
-
-  public static native int getJoystickButtons(byte joystickNum, ByteBuffer count);
-
-  public static native int setJoystickOutputs(
-      byte joystickNum, int outputs, short leftRumble, short rightRumble);
-
-  public static native int getJoystickIsXbox(byte joystickNum);
-
-  public static native int getJoystickType(byte joystickNum);
-
-  public static native String getJoystickName(byte joystickNum);
-
-  public static native int getJoystickAxisType(byte joystickNum, byte axis);
-
-  public static native double getMatchTime();
-
-  public static native boolean getSystemActive();
-
-  public static native boolean getBrownedOut();
-
-  public static native int getMatchInfo(MatchInfoData info);
-
-  public static native int sendError(
-      boolean isError,
-      int errorCode,
-      boolean isLVCode,
-      String details,
-      String location,
-      String callStack,
-      boolean printMsg);
-
-  public static native int sendConsoleLine(String line);
-
-  public static native int getPortWithModule(byte module, byte channel);
-
-  public static native int getPort(byte channel);
-
   private HAL() {}
 }
diff --git a/hal/src/main/java/edu/wpi/first/hal/HALUtil.java b/hal/src/main/java/edu/wpi/first/hal/HALUtil.java
index 4da20a2..7c0f41a 100644
--- a/hal/src/main/java/edu/wpi/first/hal/HALUtil.java
+++ b/hal/src/main/java/edu/wpi/first/hal/HALUtil.java
@@ -4,7 +4,6 @@
 
 package edu.wpi.first.hal;
 
-@SuppressWarnings("AbbreviationAsWordInName")
 public final class HALUtil extends JNIWrapper {
   public static final int NULL_PARAMETER = -1005;
   public static final int SAMPLE_RATE_TOO_HIGH = 1001;
@@ -23,6 +22,10 @@
 
   public static native int getFPGARevision();
 
+  public static native String getSerialNumber();
+
+  public static native String getComments();
+
   public static native long getFPGATime();
 
   public static native int getHALRuntimeType();
diff --git a/hal/src/main/java/edu/wpi/first/hal/HALValue.java b/hal/src/main/java/edu/wpi/first/hal/HALValue.java
index ded57de..5f5441c 100644
--- a/hal/src/main/java/edu/wpi/first/hal/HALValue.java
+++ b/hal/src/main/java/edu/wpi/first/hal/HALValue.java
@@ -4,7 +4,6 @@
 
 package edu.wpi.first.hal;
 
-@SuppressWarnings("AbbreviationAsWordInName")
 public final class HALValue {
   public static final int kUnassigned = 0;
   public static final int kBoolean = 0x01;
diff --git a/hal/src/main/java/edu/wpi/first/hal/I2CJNI.java b/hal/src/main/java/edu/wpi/first/hal/I2CJNI.java
index 821c89b..63f32a0 100644
--- a/hal/src/main/java/edu/wpi/first/hal/I2CJNI.java
+++ b/hal/src/main/java/edu/wpi/first/hal/I2CJNI.java
@@ -6,7 +6,6 @@
 
 import java.nio.ByteBuffer;
 
-@SuppressWarnings("AbbreviationAsWordInName")
 public class I2CJNI extends JNIWrapper {
   public static native void i2CInitialize(int port);
 
diff --git a/hal/src/main/java/edu/wpi/first/hal/InterruptJNI.java b/hal/src/main/java/edu/wpi/first/hal/InterruptJNI.java
index a47a364..7dc2e6d 100644
--- a/hal/src/main/java/edu/wpi/first/hal/InterruptJNI.java
+++ b/hal/src/main/java/edu/wpi/first/hal/InterruptJNI.java
@@ -11,9 +11,12 @@
 
   public static native void cleanInterrupts(int interruptHandle);
 
-  public static native int waitForInterrupt(
+  public static native long waitForInterrupt(
       int interruptHandle, double timeout, boolean ignorePrevious);
 
+  public static native long waitForMultipleInterrupts(
+      int interruptHandle, long mask, double timeout, boolean ignorePrevious);
+
   public static native long readInterruptRisingTimestamp(int interruptHandle);
 
   public static native long readInterruptFallingTimestamp(int interruptHandle);
diff --git a/hal/src/main/java/edu/wpi/first/hal/MatchInfoData.java b/hal/src/main/java/edu/wpi/first/hal/MatchInfoData.java
index 6737c58..699ecc5 100644
--- a/hal/src/main/java/edu/wpi/first/hal/MatchInfoData.java
+++ b/hal/src/main/java/edu/wpi/first/hal/MatchInfoData.java
@@ -5,25 +5,21 @@
 package edu.wpi.first.hal;
 
 /** Structure for holding the match info data request. */
+@SuppressWarnings("MemberName")
 public class MatchInfoData {
   /** Stores the event name. */
-  @SuppressWarnings("MemberName")
   public String eventName = "";
 
   /** Stores the game specific message. */
-  @SuppressWarnings("MemberName")
   public String gameSpecificMessage = "";
 
   /** Stores the match number. */
-  @SuppressWarnings("MemberName")
   public int matchNumber;
 
   /** Stores the replay number. */
-  @SuppressWarnings("MemberName")
   public int replayNumber;
 
   /** Stores the match type. */
-  @SuppressWarnings("MemberName")
   public int matchType;
 
   /**
@@ -35,7 +31,6 @@
    * @param replayNumber Replay number.
    * @param matchType Match type.
    */
-  @SuppressWarnings("MissingJavadocMethod")
   public void setData(
       String eventName,
       String gameSpecificMessage,
diff --git a/hal/src/main/java/edu/wpi/first/hal/NotifierJNI.java b/hal/src/main/java/edu/wpi/first/hal/NotifierJNI.java
index c8f4eef..648e8a2 100644
--- a/hal/src/main/java/edu/wpi/first/hal/NotifierJNI.java
+++ b/hal/src/main/java/edu/wpi/first/hal/NotifierJNI.java
@@ -52,7 +52,7 @@
   public static native void cleanNotifier(int notifierHandle);
 
   /**
-   * Sets the notifier to wakeup the waiter in another triggerTime microseconds.
+   * Sets the notifier to wake up the waiter at triggerTime microseconds.
    *
    * @param notifierHandle Notifier handle.
    * @param triggerTime Trigger time in microseconds.
diff --git a/hal/src/main/java/edu/wpi/first/hal/PWMConfigDataResult.java b/hal/src/main/java/edu/wpi/first/hal/PWMConfigDataResult.java
index e64d6da..ac3c8f94 100644
--- a/hal/src/main/java/edu/wpi/first/hal/PWMConfigDataResult.java
+++ b/hal/src/main/java/edu/wpi/first/hal/PWMConfigDataResult.java
@@ -5,6 +5,7 @@
 package edu.wpi.first.hal;
 
 /** Structure for holding the config data result for PWM. */
+@SuppressWarnings("MemberName")
 public class PWMConfigDataResult {
   PWMConfigDataResult(int max, int deadbandMax, int center, int deadbandMin, int min) {
     this.max = max;
@@ -15,22 +16,17 @@
   }
 
   /** The maximum PWM value. */
-  @SuppressWarnings("MemberName")
   public int max;
 
   /** The deadband maximum PWM value. */
-  @SuppressWarnings("MemberName")
   public int deadbandMax;
 
   /** The center PWM value. */
-  @SuppressWarnings("MemberName")
   public int center;
 
   /** The deadband minimum PWM value. */
-  @SuppressWarnings("MemberName")
   public int deadbandMin;
 
   /** The minimum PWM value. */
-  @SuppressWarnings("MemberName")
   public int min;
 }
diff --git a/hal/src/main/java/edu/wpi/first/hal/PWMJNI.java b/hal/src/main/java/edu/wpi/first/hal/PWMJNI.java
index 946ad07..1ed562c 100644
--- a/hal/src/main/java/edu/wpi/first/hal/PWMJNI.java
+++ b/hal/src/main/java/edu/wpi/first/hal/PWMJNI.java
@@ -4,7 +4,6 @@
 
 package edu.wpi.first.hal;
 
-@SuppressWarnings("AbbreviationAsWordInName")
 public class PWMJNI extends DIOJNI {
   public static native int initializePWMPort(int halPortHandle);
 
diff --git a/hal/src/main/java/edu/wpi/first/hal/PortsJNI.java b/hal/src/main/java/edu/wpi/first/hal/PortsJNI.java
index 6a06ff9..b4bd6cf 100644
--- a/hal/src/main/java/edu/wpi/first/hal/PortsJNI.java
+++ b/hal/src/main/java/edu/wpi/first/hal/PortsJNI.java
@@ -4,7 +4,6 @@
 
 package edu.wpi.first.hal;
 
-@SuppressWarnings("AbbreviationAsWordInName")
 public class PortsJNI extends JNIWrapper {
   public static native int getNumAccumulators();
 
diff --git a/hal/src/main/java/edu/wpi/first/hal/PowerDistributionFaults.java b/hal/src/main/java/edu/wpi/first/hal/PowerDistributionFaults.java
index bdff599..aa2cac5 100644
--- a/hal/src/main/java/edu/wpi/first/hal/PowerDistributionFaults.java
+++ b/hal/src/main/java/edu/wpi/first/hal/PowerDistributionFaults.java
@@ -4,86 +4,60 @@
 
 package edu.wpi.first.hal;
 
+@SuppressWarnings("MemberName")
 public class PowerDistributionFaults {
-  @SuppressWarnings("MemberName")
   public final boolean Channel0BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel1BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel2BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel3BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel4BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel5BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel6BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel7BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel8BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel9BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel10BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel11BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel12BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel13BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel14BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel15BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel16BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel17BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel18BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel19BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel20BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel21BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel22BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel23BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Brownout;
 
-  @SuppressWarnings("MemberName")
   public final boolean CanWarning;
 
-  @SuppressWarnings("MemberName")
   public final boolean HardwareFault;
 
   /**
diff --git a/hal/src/main/java/edu/wpi/first/hal/PowerDistributionJNI.java b/hal/src/main/java/edu/wpi/first/hal/PowerDistributionJNI.java
index 00c4dd1..8280f93 100644
--- a/hal/src/main/java/edu/wpi/first/hal/PowerDistributionJNI.java
+++ b/hal/src/main/java/edu/wpi/first/hal/PowerDistributionJNI.java
@@ -4,7 +4,6 @@
 
 package edu.wpi.first.hal;
 
-@SuppressWarnings("AbbreviationAsWordInName")
 public class PowerDistributionJNI extends JNIWrapper {
   public static final int AUTOMATIC_TYPE = 0;
   public static final int CTRE_TYPE = 1;
diff --git a/hal/src/main/java/edu/wpi/first/hal/PowerDistributionStickyFaults.java b/hal/src/main/java/edu/wpi/first/hal/PowerDistributionStickyFaults.java
index 0eb4a69..f60f8df 100644
--- a/hal/src/main/java/edu/wpi/first/hal/PowerDistributionStickyFaults.java
+++ b/hal/src/main/java/edu/wpi/first/hal/PowerDistributionStickyFaults.java
@@ -4,89 +4,62 @@
 
 package edu.wpi.first.hal;
 
+@SuppressWarnings("MemberName")
 public class PowerDistributionStickyFaults {
-  @SuppressWarnings("MemberName")
   public final boolean Channel0BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel1BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel2BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel3BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel4BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel5BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel6BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel7BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel8BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel9BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel10BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel11BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel12BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel13BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel14BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel15BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel16BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel17BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel18BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel19BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel20BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel21BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel22BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel23BreakerFault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Brownout;
 
-  @SuppressWarnings("MemberName")
   public final boolean CanWarning;
 
-  @SuppressWarnings("MemberName")
   public final boolean CanBusOff;
 
-  @SuppressWarnings("MemberName")
   public final boolean HasReset;
 
   /**
diff --git a/hal/src/main/java/edu/wpi/first/hal/PowerDistributionVersion.java b/hal/src/main/java/edu/wpi/first/hal/PowerDistributionVersion.java
index 0c733a2..fdd1233 100644
--- a/hal/src/main/java/edu/wpi/first/hal/PowerDistributionVersion.java
+++ b/hal/src/main/java/edu/wpi/first/hal/PowerDistributionVersion.java
@@ -4,23 +4,18 @@
 
 package edu.wpi.first.hal;
 
+@SuppressWarnings("MemberName")
 public class PowerDistributionVersion {
-  @SuppressWarnings("MemberName")
   public final int firmwareMajor;
 
-  @SuppressWarnings("MemberName")
   public final int firmwareMinor;
 
-  @SuppressWarnings("MemberName")
   public final int firmwareFix;
 
-  @SuppressWarnings("MemberName")
   public final int hardwareMinor;
 
-  @SuppressWarnings("MemberName")
   public final int hardwareMajor;
 
-  @SuppressWarnings("MemberName")
   public final int uniqueId;
 
   /**
diff --git a/hal/src/main/java/edu/wpi/first/hal/REVPHFaults.java b/hal/src/main/java/edu/wpi/first/hal/REVPHFaults.java
index f71ef69..3419810 100644
--- a/hal/src/main/java/edu/wpi/first/hal/REVPHFaults.java
+++ b/hal/src/main/java/edu/wpi/first/hal/REVPHFaults.java
@@ -4,72 +4,50 @@
 
 package edu.wpi.first.hal;
 
-@SuppressWarnings("AbbreviationAsWordInName")
+@SuppressWarnings("MemberName")
 public class REVPHFaults {
-  @SuppressWarnings("MemberName")
   public final boolean Channel0Fault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel1Fault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel2Fault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel3Fault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel4Fault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel5Fault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel6Fault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel7Fault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel8Fault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel9Fault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel10Fault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel11Fault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel12Fault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel13Fault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel14Fault;
 
-  @SuppressWarnings("MemberName")
   public final boolean Channel15Fault;
 
-  @SuppressWarnings("MemberName")
   public final boolean CompressorOverCurrent;
 
-  @SuppressWarnings("MemberName")
   public final boolean CompressorOpen;
 
-  @SuppressWarnings("MemberName")
   public final boolean SolenoidOverCurrent;
 
-  @SuppressWarnings("MemberName")
   public final boolean Brownout;
 
-  @SuppressWarnings("MemberName")
   public final boolean CanWarning;
 
-  @SuppressWarnings("MemberName")
   public final boolean HardwareFault;
 
   /**
diff --git a/hal/src/main/java/edu/wpi/first/hal/REVPHJNI.java b/hal/src/main/java/edu/wpi/first/hal/REVPHJNI.java
index 17f0323..44c67a8 100644
--- a/hal/src/main/java/edu/wpi/first/hal/REVPHJNI.java
+++ b/hal/src/main/java/edu/wpi/first/hal/REVPHJNI.java
@@ -4,7 +4,6 @@
 
 package edu.wpi.first.hal;
 
-@SuppressWarnings("AbbreviationAsWordInName")
 public class REVPHJNI extends JNIWrapper {
   public static final int COMPRESSOR_CONFIG_TYPE_DISABLED = 0;
   public static final int COMPRESSOR_CONFIG_TYPE_DIGITAL = 1;
diff --git a/hal/src/main/java/edu/wpi/first/hal/REVPHStickyFaults.java b/hal/src/main/java/edu/wpi/first/hal/REVPHStickyFaults.java
index 6bf9f4f..614389e 100644
--- a/hal/src/main/java/edu/wpi/first/hal/REVPHStickyFaults.java
+++ b/hal/src/main/java/edu/wpi/first/hal/REVPHStickyFaults.java
@@ -4,27 +4,20 @@
 
 package edu.wpi.first.hal;
 
-@SuppressWarnings("AbbreviationAsWordInName")
+@SuppressWarnings("MemberName")
 public class REVPHStickyFaults {
-  @SuppressWarnings("MemberName")
   public final boolean CompressorOverCurrent;
 
-  @SuppressWarnings("MemberName")
   public final boolean CompressorOpen;
 
-  @SuppressWarnings("MemberName")
   public final boolean SolenoidOverCurrent;
 
-  @SuppressWarnings("MemberName")
   public final boolean Brownout;
 
-  @SuppressWarnings("MemberName")
   public final boolean CanWarning;
 
-  @SuppressWarnings("MemberName")
   public final boolean CanBusOff;
 
-  @SuppressWarnings("MemberName")
   public final boolean HasReset;
 
   /**
diff --git a/hal/src/main/java/edu/wpi/first/hal/REVPHVersion.java b/hal/src/main/java/edu/wpi/first/hal/REVPHVersion.java
index 13471e7..86d0743 100644
--- a/hal/src/main/java/edu/wpi/first/hal/REVPHVersion.java
+++ b/hal/src/main/java/edu/wpi/first/hal/REVPHVersion.java
@@ -4,24 +4,18 @@
 
 package edu.wpi.first.hal;
 
-@SuppressWarnings("AbbreviationAsWordInName")
+@SuppressWarnings("MemberName")
 public class REVPHVersion {
-  @SuppressWarnings("MemberName")
   public final int firmwareMajor;
 
-  @SuppressWarnings("MemberName")
   public final int firmwareMinor;
 
-  @SuppressWarnings("MemberName")
   public final int firmwareFix;
 
-  @SuppressWarnings("MemberName")
   public final int hardwareMinor;
 
-  @SuppressWarnings("MemberName")
   public final int hardwareMajor;
 
-  @SuppressWarnings("MemberName")
   public final int uniqueId;
 
   /**
diff --git a/hal/src/main/java/edu/wpi/first/hal/SPIJNI.java b/hal/src/main/java/edu/wpi/first/hal/SPIJNI.java
index 05ac08a..053f192 100644
--- a/hal/src/main/java/edu/wpi/first/hal/SPIJNI.java
+++ b/hal/src/main/java/edu/wpi/first/hal/SPIJNI.java
@@ -6,8 +6,19 @@
 
 import java.nio.ByteBuffer;
 
-@SuppressWarnings("AbbreviationAsWordInName")
 public class SPIJNI extends JNIWrapper {
+  public static final int INVALID_PORT = -1;
+  public static final int ONBOARD_CS0_PORT = 0;
+  public static final int ONBOARD_CS1_PORT = 1;
+  public static final int ONBOARD_CS2_PORT = 2;
+  public static final int ONBOARD_CS3_PORT = 3;
+  public static final int MXP_PORT = 4;
+
+  public static final int SPI_MODE0 = 0;
+  public static final int SPI_MODE1 = 1;
+  public static final int SPI_MODE2 = 2;
+  public static final int SPI_MODE3 = 3;
+
   public static native void spiInitialize(int port);
 
   public static native int spiTransaction(
@@ -28,8 +39,9 @@
 
   public static native void spiSetSpeed(int port, int speed);
 
-  public static native void spiSetOpts(
-      int port, int msbFirst, int sampleOnTrailing, int clkIdleHigh);
+  public static native void spiSetMode(int port, int mode);
+
+  public static native int spiGetMode(int port);
 
   public static native void spiSetChipSelectActiveHigh(int port);
 
diff --git a/hal/src/main/java/edu/wpi/first/hal/SimDevice.java b/hal/src/main/java/edu/wpi/first/hal/SimDevice.java
index 67c39fe..db3a587 100644
--- a/hal/src/main/java/edu/wpi/first/hal/SimDevice.java
+++ b/hal/src/main/java/edu/wpi/first/hal/SimDevice.java
@@ -112,22 +112,6 @@
    * <p>Returns null if not in simulation.
    *
    * @param name value name
-   * @param readonly if the value should not be written from simulation side
-   * @param initialValue initial value
-   * @return simulated value object
-   * @deprecated Use direction function instead
-   */
-  @Deprecated
-  public SimValue createValue(String name, boolean readonly, HALValue initialValue) {
-    return createValue(name, readonly ? Direction.kOutput : Direction.kInput, initialValue);
-  }
-
-  /**
-   * Creates a value on the simulated device.
-   *
-   * <p>Returns null if not in simulation.
-   *
-   * @param name value name
    * @param direction input/output/bidir (from perspective of user code)
    * @param initialValue initial value
    * @return simulated value object
@@ -182,22 +166,6 @@
    * <p>Returns null if not in simulation.
    *
    * @param name value name
-   * @param readonly if the value should not be written from simulation side
-   * @param initialValue initial value
-   * @return simulated double value object
-   * @deprecated Use direction function instead
-   */
-  @Deprecated
-  public SimDouble createDouble(String name, boolean readonly, double initialValue) {
-    return createDouble(name, readonly ? Direction.kOutput : Direction.kInput, initialValue);
-  }
-
-  /**
-   * Creates a double value on the simulated device.
-   *
-   * <p>Returns null if not in simulation.
-   *
-   * @param name value name
    * @param direction input/output/bidir (from perspective of user code)
    * @param initialValue initial value
    * @return simulated double value object
@@ -218,25 +186,6 @@
    * <p>Returns null if not in simulation.
    *
    * @param name value name
-   * @param readonly if the value should not be written from simulation side
-   * @param options array of option descriptions
-   * @param initialValue initial value (selection)
-   * @return simulated enum value object
-   * @deprecated Use direction function instead
-   */
-  @Deprecated
-  public SimEnum createEnum(String name, boolean readonly, String[] options, int initialValue) {
-    return createEnum(name, readonly ? Direction.kOutput : Direction.kInput, options, initialValue);
-  }
-
-  /**
-   * Creates an enumerated value on the simulated device.
-   *
-   * <p>Enumerated values are always in the range 0 to numOptions-1.
-   *
-   * <p>Returns null if not in simulation.
-   *
-   * @param name value name
    * @param direction input/output/bidir (from perspective of user code)
    * @param options array of option descriptions
    * @param initialValue initial value (selection)
@@ -282,22 +231,6 @@
    * <p>Returns null if not in simulation.
    *
    * @param name value name
-   * @param readonly if the value should not be written from simulation side
-   * @param initialValue initial value
-   * @return simulated boolean value object
-   * @deprecated Use direction function instead
-   */
-  @Deprecated
-  public SimBoolean createBoolean(String name, boolean readonly, boolean initialValue) {
-    return createBoolean(name, readonly ? Direction.kOutput : Direction.kInput, initialValue);
-  }
-
-  /**
-   * Creates a boolean value on the simulated device.
-   *
-   * <p>Returns null if not in simulation.
-   *
-   * @param name value name
    * @param direction input/output/bidir (from perspective of user code)
    * @param initialValue initial value
    * @return simulated boolean value object
diff --git a/hal/src/main/java/edu/wpi/first/hal/SimDeviceJNI.java b/hal/src/main/java/edu/wpi/first/hal/SimDeviceJNI.java
index 4279916..8723cd3 100644
--- a/hal/src/main/java/edu/wpi/first/hal/SimDeviceJNI.java
+++ b/hal/src/main/java/edu/wpi/first/hal/SimDeviceJNI.java
@@ -43,30 +43,6 @@
    *
    * @param device simulated device handle
    * @param name value name
-   * @param readonly if the value should not be written from simulation side
-   * @param initialValue initial value
-   * @return simulated value handle
-   * @deprecated Use direction-taking function instead
-   */
-  @Deprecated
-  public static int createSimValue(
-      int device, String name, boolean readonly, HALValue initialValue) {
-    return createSimValueNative(
-        device,
-        name,
-        readonly ? kOutput : kInput,
-        initialValue.getType(),
-        initialValue.getNativeLong(),
-        initialValue.getNativeDouble());
-  }
-
-  /**
-   * Creates a value on a simulated device.
-   *
-   * <p>Returns 0 if not in simulation; this can be used to avoid calls to Set/Get functions.
-   *
-   * @param device simulated device handle
-   * @param name value name
    * @param direction input/output/bidir (from perspective of user code)
    * @param initialValue initial value
    * @return simulated value handle
@@ -118,25 +94,6 @@
    *
    * @param device simulated device handle
    * @param name value name
-   * @param readonly if the value should not be written from simulation side
-   * @param initialValue initial value
-   * @return simulated value handle
-   * @deprecated Use direction-taking function instead
-   */
-  @Deprecated
-  public static int createSimValueDouble(
-      int device, String name, boolean readonly, double initialValue) {
-    return createSimValueNative(
-        device, name, readonly ? kOutput : kInput, HALValue.kDouble, 0, initialValue);
-  }
-
-  /**
-   * Creates a double value on a simulated device.
-   *
-   * <p>Returns 0 if not in simulation; this can be used to avoid calls to Set/Get functions.
-   *
-   * @param device simulated device handle
-   * @param name value name
    * @param direction input/output/bidir (from perspective of user code)
    * @param initialValue initial value
    * @return simulated value handle
@@ -155,27 +112,6 @@
    *
    * @param device simulated device handle
    * @param name value name
-   * @param readonly if the value should not be written from simulation side
-   * @param options array of option descriptions
-   * @param initialValue initial value (selection)
-   * @return simulated value handle
-   * @deprecated Use direction-taking function instead
-   */
-  @Deprecated
-  public static int createSimValueEnum(
-      int device, String name, boolean readonly, String[] options, int initialValue) {
-    return createSimValueEnum(device, name, readonly ? kOutput : kInput, options, initialValue);
-  }
-
-  /**
-   * Creates an enumerated value on a simulated device.
-   *
-   * <p>Enumerated values are always in the range 0 to numOptions-1.
-   *
-   * <p>Returns 0 if not in simulation; this can be used to avoid calls to Set/Get functions.
-   *
-   * @param device simulated device handle
-   * @param name value name
    * @param direction input/output/bidir (from perspective of user code)
    * @param options array of option descriptions
    * @param initialValue initial value (selection)
@@ -214,25 +150,6 @@
    *
    * @param device simulated device handle
    * @param name value name
-   * @param readonly if the value should not be written from simulation side
-   * @param initialValue initial value
-   * @return simulated value handle
-   * @deprecated Use direction-taking function instead
-   */
-  @Deprecated
-  public static int createSimValueBoolean(
-      int device, String name, boolean readonly, boolean initialValue) {
-    return createSimValueNative(
-        device, name, readonly ? kOutput : kInput, HALValue.kBoolean, initialValue ? 1 : 0, 0.0);
-  }
-
-  /**
-   * Creates a boolean value on a simulated device.
-   *
-   * <p>Returns 0 if not in simulation; this can be used to avoid calls to Set/Get functions.
-   *
-   * @param device simulated device handle
-   * @param name value name
    * @param direction input/output/bidir (from perspective of user code)
    * @param initialValue initial value
    * @return simulated value handle
diff --git a/hal/src/main/java/edu/wpi/first/hal/can/CANJNI.java b/hal/src/main/java/edu/wpi/first/hal/can/CANJNI.java
index b4f344f..e0734dd 100644
--- a/hal/src/main/java/edu/wpi/first/hal/can/CANJNI.java
+++ b/hal/src/main/java/edu/wpi/first/hal/can/CANJNI.java
@@ -4,11 +4,12 @@
 
 package edu.wpi.first.hal.can;
 
+import edu.wpi.first.hal.CANStreamMessage;
 import edu.wpi.first.hal.JNIWrapper;
 import java.nio.ByteBuffer;
 import java.nio.IntBuffer;
 
-@SuppressWarnings("AbbreviationAsWordInName")
+@SuppressWarnings("MethodName")
 public class CANJNI extends JNIWrapper {
   public static final int CAN_SEND_PERIOD_NO_REPEAT = 0;
   public static final int CAN_SEND_PERIOD_STOP_REPEATING = -1;
@@ -17,14 +18,18 @@
   public static final int CAN_IS_FRAME_REMOTE = 0x80000000;
   public static final int CAN_IS_FRAME_11BIT = 0x40000000;
 
-  @SuppressWarnings("MethodName")
   public static native void FRCNetCommCANSessionMuxSendMessage(
       int messageID, byte[] data, int periodMs);
 
-  @SuppressWarnings("MethodName")
   public static native byte[] FRCNetCommCANSessionMuxReceiveMessage(
       IntBuffer messageID, int messageIDMask, ByteBuffer timeStamp);
 
-  @SuppressWarnings("MethodName")
   public static native void getCANStatus(CANStatus status);
+
+  public static native int openCANStreamSession(int messageID, int messageIDMask, int maxMessages);
+
+  public static native void closeCANStreamSession(int sessionHandle);
+
+  public static native int readCANStreamSession(
+      int sessionHandle, CANStreamMessage[] messages, int messagesToRead);
 }
diff --git a/hal/src/main/java/edu/wpi/first/hal/can/CANStatus.java b/hal/src/main/java/edu/wpi/first/hal/can/CANStatus.java
index 62df8e8..8ec89f7 100644
--- a/hal/src/main/java/edu/wpi/first/hal/can/CANStatus.java
+++ b/hal/src/main/java/edu/wpi/first/hal/can/CANStatus.java
@@ -5,28 +5,32 @@
 package edu.wpi.first.hal.can;
 
 /** Structure for holding the result of a CAN Status request. */
+@SuppressWarnings("MemberName")
 public class CANStatus {
   /** The utilization of the CAN Bus. */
-  @SuppressWarnings("MemberName")
   public double percentBusUtilization;
 
   /** The CAN Bus off count. */
-  @SuppressWarnings("MemberName")
   public int busOffCount;
 
   /** The CAN Bus TX full count. */
-  @SuppressWarnings("MemberName")
   public int txFullCount;
 
   /** The CAN Bus receive error count. */
-  @SuppressWarnings("MemberName")
   public int receiveErrorCount;
 
   /** The CAN Bus transmit error count. */
-  @SuppressWarnings("MemberName")
   public int transmitErrorCount;
 
-  @SuppressWarnings("MissingJavadocMethod")
+  /**
+   * Set CAN bus status.
+   *
+   * @param percentBusUtilization CAN bus utilization as a percent.
+   * @param busOffCount Bus off event count.
+   * @param txFullCount TX buffer full event count.
+   * @param receiveErrorCount Receive error event count.
+   * @param transmitErrorCount Transmit error event count.
+   */
   public void setStatus(
       double percentBusUtilization,
       int busOffCount,
diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/CTREPCMDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/CTREPCMDataJNI.java
index 9f60e7e..6d94438 100644
--- a/hal/src/main/java/edu/wpi/first/hal/simulation/CTREPCMDataJNI.java
+++ b/hal/src/main/java/edu/wpi/first/hal/simulation/CTREPCMDataJNI.java
@@ -6,7 +6,6 @@
 
 import edu.wpi.first.hal.JNIWrapper;
 
-@SuppressWarnings("AbbreviationAsWordInName")
 public class CTREPCMDataJNI extends JNIWrapper {
   public static native int registerInitializedCallback(
       int index, NotifyCallback callback, boolean initialNotify);
diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/REVPHDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/REVPHDataJNI.java
index ee801f3..96b9701 100644
--- a/hal/src/main/java/edu/wpi/first/hal/simulation/REVPHDataJNI.java
+++ b/hal/src/main/java/edu/wpi/first/hal/simulation/REVPHDataJNI.java
@@ -6,7 +6,6 @@
 
 import edu.wpi.first.hal.JNIWrapper;
 
-@SuppressWarnings("AbbreviationAsWordInName")
 public class REVPHDataJNI extends JNIWrapper {
   public static native int registerInitializedCallback(
       int index, NotifyCallback callback, boolean initialNotify);
diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/RoboRioDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/RoboRioDataJNI.java
index a822ede..ef06067 100644
--- a/hal/src/main/java/edu/wpi/first/hal/simulation/RoboRioDataJNI.java
+++ b/hal/src/main/java/edu/wpi/first/hal/simulation/RoboRioDataJNI.java
@@ -7,17 +7,13 @@
 import edu.wpi.first.hal.JNIWrapper;
 
 public class RoboRioDataJNI extends JNIWrapper {
-  @SuppressWarnings("AbbreviationAsWordInName")
   public static native int registerFPGAButtonCallback(
       NotifyCallback callback, boolean initialNotify);
 
-  @SuppressWarnings("AbbreviationAsWordInName")
   public static native void cancelFPGAButtonCallback(int uid);
 
-  @SuppressWarnings("AbbreviationAsWordInName")
   public static native boolean getFPGAButton();
 
-  @SuppressWarnings("AbbreviationAsWordInName")
   public static native void setFPGAButton(boolean fPGAButton);
 
   public static native int registerVInVoltageCallback(
@@ -155,5 +151,13 @@
 
   public static native void setBrownoutVoltage(double brownoutVoltage);
 
+  public static native String getSerialNumber();
+
+  public static native void setSerialNumber(String serialNumber);
+
+  public static native String getComments();
+
+  public static native void setComments(String comments);
+
   public static native void resetData();
 }
diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/SimDeviceDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/SimDeviceDataJNI.java
index ef6536a..092b955 100644
--- a/hal/src/main/java/edu/wpi/first/hal/simulation/SimDeviceDataJNI.java
+++ b/hal/src/main/java/edu/wpi/first/hal/simulation/SimDeviceDataJNI.java
@@ -28,18 +28,21 @@
 
   public static native int getSimValueDeviceHandle(int handle);
 
+  @SuppressWarnings("MemberName")
   public static class SimDeviceInfo {
-    @SuppressWarnings("JavadocMethod")
+    public String name;
+    public int handle;
+
+    /**
+     * SimDeviceInfo constructor.
+     *
+     * @param name SimDevice name.
+     * @param handle SimDevice handle.
+     */
     public SimDeviceInfo(String name, int handle) {
       this.name = name;
       this.handle = handle;
     }
-
-    @SuppressWarnings("MemberName")
-    public String name;
-
-    @SuppressWarnings("MemberName")
-    public int handle;
   }
 
   public static native SimDeviceInfo[] enumerateSimDevices(String prefix);
@@ -70,32 +73,30 @@
 
   public static native int getSimValueHandle(int device, String name);
 
+  @SuppressWarnings("MemberName")
   public static class SimValueInfo {
-    @SuppressWarnings("JavadocMethod")
+    public String name;
+    public int handle;
+    public int direction;
+    public HALValue value;
+
+    /**
+     * SimValueInfo constructor.
+     *
+     * @param name SimValue name.
+     * @param handle SimValue handle.
+     * @param direction SimValue direction.
+     * @param type SimValue type.
+     * @param value1 Value 1.
+     * @param value2 Value 2.
+     */
     public SimValueInfo(
         String name, int handle, int direction, int type, long value1, double value2) {
       this.name = name;
       this.handle = handle;
-      this.readonly = direction == 1;
       this.direction = direction;
       this.value = HALValue.fromNative(type, value1, value2);
     }
-
-    @SuppressWarnings("MemberName")
-    public String name;
-
-    @SuppressWarnings("MemberName")
-    public int handle;
-
-    @SuppressWarnings("MemberName")
-    @Deprecated
-    public boolean readonly;
-
-    @SuppressWarnings("MemberName")
-    public int direction;
-
-    @SuppressWarnings("MemberName")
-    public HALValue value;
   }
 
   public static native SimValueInfo[] enumerateSimValues(int device);
diff --git a/hal/src/main/java/edu/wpi/first/hal/util/AllocationException.java b/hal/src/main/java/edu/wpi/first/hal/util/AllocationException.java
index e9f9a91..3591a5f 100644
--- a/hal/src/main/java/edu/wpi/first/hal/util/AllocationException.java
+++ b/hal/src/main/java/edu/wpi/first/hal/util/AllocationException.java
@@ -5,7 +5,6 @@
 package edu.wpi.first.hal.util;
 
 /** Exception indicating that the resource is already allocated. */
-@SuppressWarnings("serial")
 public class AllocationException extends RuntimeException {
   /**
    * Create a new AllocationException.
diff --git a/hal/src/main/java/edu/wpi/first/hal/util/BoundaryException.java b/hal/src/main/java/edu/wpi/first/hal/util/BoundaryException.java
index 683980d..bbecbbe 100644
--- a/hal/src/main/java/edu/wpi/first/hal/util/BoundaryException.java
+++ b/hal/src/main/java/edu/wpi/first/hal/util/BoundaryException.java
@@ -7,7 +7,6 @@
 /**
  * This exception represents an error in which a lower limit was set as higher than an upper limit.
  */
-@SuppressWarnings("serial")
 public class BoundaryException extends RuntimeException {
   /**
    * Create a new exception with the given message.
diff --git a/hal/src/main/java/edu/wpi/first/hal/util/CheckedAllocationException.java b/hal/src/main/java/edu/wpi/first/hal/util/CheckedAllocationException.java
index 6155f17..81de150 100644
--- a/hal/src/main/java/edu/wpi/first/hal/util/CheckedAllocationException.java
+++ b/hal/src/main/java/edu/wpi/first/hal/util/CheckedAllocationException.java
@@ -8,7 +8,6 @@
  * Exception indicating that the resource is already allocated This is meant to be thrown by the
  * resource class.
  */
-@SuppressWarnings("serial")
 public class CheckedAllocationException extends Exception {
   /**
    * Create a new CheckedAllocationException.
diff --git a/hal/src/main/java/edu/wpi/first/hal/util/HalHandleException.java b/hal/src/main/java/edu/wpi/first/hal/util/HalHandleException.java
index 65c11f5..aee93bb 100644
--- a/hal/src/main/java/edu/wpi/first/hal/util/HalHandleException.java
+++ b/hal/src/main/java/edu/wpi/first/hal/util/HalHandleException.java
@@ -5,7 +5,6 @@
 package edu.wpi.first.hal.util;
 
 /** Exception indicating that an error has occurred with a HAL Handle. */
-@SuppressWarnings("serial")
 public class HalHandleException extends RuntimeException {
   /**
    * Create a new HalHandleException.
diff --git a/hal/src/main/java/edu/wpi/first/hal/util/UncleanStatusException.java b/hal/src/main/java/edu/wpi/first/hal/util/UncleanStatusException.java
index 90650fc..7be73b4 100644
--- a/hal/src/main/java/edu/wpi/first/hal/util/UncleanStatusException.java
+++ b/hal/src/main/java/edu/wpi/first/hal/util/UncleanStatusException.java
@@ -5,7 +5,6 @@
 package edu.wpi.first.hal.util;
 
 /** Exception for bad status codes from the chip object. */
-@SuppressWarnings("serial")
 public final class UncleanStatusException extends IllegalStateException {
   private final int m_statusCode;
 
diff --git a/hal/src/main/native/athena/Accelerometer.cpp b/hal/src/main/native/athena/Accelerometer.cpp
index 73b1357..b8cc832 100644
--- a/hal/src/main/native/athena/Accelerometer.cpp
+++ b/hal/src/main/native/athena/Accelerometer.cpp
@@ -121,8 +121,9 @@
   // Execute and wait until it's done (up to a millisecond)
   initialTime = HAL_GetFPGATime(&status);
   while (accel->readSTAT(&status) & 1) {
-    if (HAL_GetFPGATime(&status) > initialTime + 1000)
+    if (HAL_GetFPGATime(&status) > initialTime + 1000) {
       break;
+    }
   }
 
   // Send a stop transmit/receive message with the data
@@ -133,8 +134,9 @@
   // Execute and wait until it's done (up to a millisecond)
   initialTime = HAL_GetFPGATime(&status);
   while (accel->readSTAT(&status) & 1) {
-    if (HAL_GetFPGATime(&status) > initialTime + 1000)
+    if (HAL_GetFPGATime(&status) > initialTime + 1000) {
       break;
+    }
   }
 }
 
@@ -151,8 +153,9 @@
   // Execute and wait until it's done (up to a millisecond)
   initialTime = HAL_GetFPGATime(&status);
   while (accel->readSTAT(&status) & 1) {
-    if (HAL_GetFPGATime(&status) > initialTime + 1000)
+    if (HAL_GetFPGATime(&status) > initialTime + 1000) {
       break;
+    }
   }
 
   // Receive a message with the data and stop
@@ -163,8 +166,9 @@
   // Execute and wait until it's done (up to a millisecond)
   initialTime = HAL_GetFPGATime(&status);
   while (accel->readSTAT(&status) & 1) {
-    if (HAL_GetFPGATime(&status) > initialTime + 1000)
+    if (HAL_GetFPGATime(&status) > initialTime + 1000) {
       break;
+    }
   }
 
   return accel->readDATI(&status);
diff --git a/hal/src/main/native/athena/AddressableLED.cpp b/hal/src/main/native/athena/AddressableLED.cpp
index 74a323a..5e55c93 100644
--- a/hal/src/main/native/athena/AddressableLED.cpp
+++ b/hal/src/main/native/athena/AddressableLED.cpp
@@ -6,11 +6,11 @@
 
 #include <cstring>
 
-#include <FRC_FPGA_ChipObject/fpgainterfacecapi/NiFpga_HMB.h>
 #include <fmt/format.h>
 
 #include "ConstantsInternal.h"
 #include "DigitalInternal.h"
+#include "FPGACalls.h"
 #include "HALInitializer.h"
 #include "HALInternal.h"
 #include "PortsInternal.h"
@@ -44,6 +44,8 @@
 }
 }  // namespace hal::init
 
+static constexpr const char* HmbName = "HMB_0_LED";
+
 extern "C" {
 
 HAL_AddressableLEDHandle HAL_InitializeAddressableLED(
@@ -101,8 +103,8 @@
 
   uint32_t session = led->led->getSystemInterface()->getHandle();
 
-  *status = NiFpga_OpenHostMemoryBuffer(session, "HMB_0_LED", &led->ledBuffer,
-                                        &led->ledBufferSize);
+  *status = hal::HAL_NiFpga_OpenHmb(session, HmbName, &led->ledBufferSize,
+                                    &led->ledBuffer);
 
   if (*status != 0) {
     addressableLEDHandles->Free(handle);
@@ -113,6 +115,12 @@
 }
 
 void HAL_FreeAddressableLED(HAL_AddressableLEDHandle handle) {
+  auto led = addressableLEDHandles->Get(handle);
+  if (!led) {
+    return;
+  }
+  uint32_t session = led->led->getSystemInterface()->getHandle();
+  hal::HAL_NiFpga_CloseHmb(session, HmbName);
   addressableLEDHandles->Free(handle);
 }
 
diff --git a/hal/src/main/native/athena/AnalogAccumulator.cpp b/hal/src/main/native/athena/AnalogAccumulator.cpp
index 7ff7d47..32688a0 100644
--- a/hal/src/main/native/athena/AnalogAccumulator.cpp
+++ b/hal/src/main/native/athena/AnalogAccumulator.cpp
@@ -23,8 +23,9 @@
     return false;
   }
   for (int32_t i = 0; i < kNumAccumulators; i++) {
-    if (port->channel == kAccumulatorChannels[i])
+    if (port->channel == kAccumulatorChannels[i]) {
       return true;
+    }
   }
   return false;
 }
diff --git a/hal/src/main/native/athena/AnalogTrigger.cpp b/hal/src/main/native/athena/AnalogTrigger.cpp
index d9e2b92..47ef4f2 100644
--- a/hal/src/main/native/athena/AnalogTrigger.cpp
+++ b/hal/src/main/native/athena/AnalogTrigger.cpp
@@ -5,6 +5,7 @@
 #include "hal/AnalogTrigger.h"
 
 #include "AnalogInternal.h"
+#include "ConstantsInternal.h"
 #include "DutyCycleInternal.h"
 #include "HALInitializer.h"
 #include "HALInternal.h"
@@ -147,16 +148,10 @@
     return;
   }
 
-  int32_t scaleFactor =
-      HAL_GetDutyCycleOutputScaleFactor(trigger->handle, status);
-  if (*status != 0) {
-    return;
-  }
-
-  trigger->trigger->writeLowerLimit(static_cast<int32_t>(scaleFactor * lower),
-                                    status);
-  trigger->trigger->writeUpperLimit(static_cast<int32_t>(scaleFactor * upper),
-                                    status);
+  trigger->trigger->writeLowerLimit(
+      static_cast<int32_t>(kDutyCycleScaleFactor * lower), status);
+  trigger->trigger->writeUpperLimit(
+      static_cast<int32_t>(kDutyCycleScaleFactor * upper), status);
 }
 
 void HAL_SetAnalogTriggerLimitsVoltage(
diff --git a/hal/src/main/native/athena/CANAPI.cpp b/hal/src/main/native/athena/CANAPI.cpp
index a7c5b37..a3e4904 100644
--- a/hal/src/main/native/athena/CANAPI.cpp
+++ b/hal/src/main/native/athena/CANAPI.cpp
@@ -85,6 +85,9 @@
 
 void HAL_CleanCAN(HAL_CANHandle handle) {
   auto data = canHandles->Free(handle);
+  if (data == nullptr) {
+    return;
+  }
 
   std::scoped_lock lock(data->mapMutex);
 
diff --git a/hal/src/main/native/athena/CTREPCM.cpp b/hal/src/main/native/athena/CTREPCM.cpp
index 37faf69..79c1ae3 100644
--- a/hal/src/main/native/athena/CTREPCM.cpp
+++ b/hal/src/main/native/athena/CTREPCM.cpp
@@ -233,9 +233,11 @@
     return;
   }
 
+  int32_t can_status = 0;
+
   std::scoped_lock lock{pcm->lock};
   pcm->control.bits.closedLoopEnable = enabled ? 1 : 0;
-  SendControl(pcm.get(), status);
+  SendControl(pcm.get(), &can_status);
 }
 
 HAL_Bool HAL_GetCTREPCMClosedLoopControl(HAL_CTREPCMHandle handle,
@@ -394,9 +396,8 @@
   }
 
   std::scoped_lock lock{pcm->lock};
-  pcm->oneShot.sol10MsPerUnit[index] =
-      (std::min)(static_cast<uint32_t>(durMs) / 10,
-                 static_cast<uint32_t>(0xFF));
+  pcm->oneShot.sol10MsPerUnit[index] = (std::min)(
+      static_cast<uint32_t>(durMs) / 10, static_cast<uint32_t>(0xFF));
   HAL_WriteCANPacketRepeating(pcm->canHandle, pcm->oneShot.sol10MsPerUnit, 8,
                               Control3, SendPeriod, status);
 }
diff --git a/hal/src/main/native/athena/ConstantsInternal.h b/hal/src/main/native/athena/ConstantsInternal.h
index 21fece4..ca7241c 100644
--- a/hal/src/main/native/athena/ConstantsInternal.h
+++ b/hal/src/main/native/athena/ConstantsInternal.h
@@ -9,5 +9,6 @@
 namespace hal {
 
 constexpr int32_t kSystemClockTicksPerMicrosecond = 40;
+constexpr int32_t kDutyCycleScaleFactor = 4e7 - 1;
 
 }  // namespace hal
diff --git a/hal/src/main/native/athena/DIO.cpp b/hal/src/main/native/athena/DIO.cpp
index 7b26dd1..e6308fb 100644
--- a/hal/src/main/native/athena/DIO.cpp
+++ b/hal/src/main/native/athena/DIO.cpp
@@ -133,8 +133,9 @@
 void HAL_FreeDIOPort(HAL_DigitalHandle dioPortHandle) {
   auto port = digitalChannelHandles->Get(dioPortHandle, HAL_HandleEnum::DIO);
   // no status, so no need to check for a proper free.
-  if (port == nullptr)
+  if (port == nullptr) {
     return;
+  }
   digitalChannelHandles->Free(dioPortHandle, HAL_HandleEnum::DIO);
 
   // Wait for no other object to hold this handle.
@@ -239,6 +240,30 @@
   }
 }
 
+void HAL_SetDigitalPWMPPS(HAL_DigitalPWMHandle pwmGenerator, double dutyCycle,
+                          int32_t* status) {
+  auto port = digitalPWMHandles->Get(pwmGenerator);
+  if (port == nullptr) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+  int32_t id = *port;
+  digitalSystem->writePWMPeriodPower(0xffff, status);
+  double rawDutyCycle = 31.0 * dutyCycle;
+  if (rawDutyCycle > 30.5) {
+    rawDutyCycle = 30.5;
+  }
+  {
+    std::scoped_lock lock(digitalPwmMutex);
+    if (id < 4)
+      digitalSystem->writePWMDutyCycleA(id, static_cast<uint8_t>(rawDutyCycle),
+                                        status);
+    else
+      digitalSystem->writePWMDutyCycleB(
+          id - 4, static_cast<uint8_t>(rawDutyCycle), status);
+  }
+}
+
 void HAL_SetDigitalPWMOutputChannel(HAL_DigitalPWMHandle pwmGenerator,
                                     int32_t channel, int32_t* status) {
   auto port = digitalPWMHandles->Get(pwmGenerator);
@@ -407,13 +432,24 @@
   }
 }
 
-void HAL_Pulse(HAL_DigitalHandle dioPortHandle, double pulseLength,
+void HAL_Pulse(HAL_DigitalHandle dioPortHandle, double pulseLengthSeconds,
                int32_t* status) {
   auto port = digitalChannelHandles->Get(dioPortHandle, HAL_HandleEnum::DIO);
   if (port == nullptr) {
     *status = HAL_HANDLE_ERROR;
     return;
   }
+
+  uint32_t pulseLengthMicroseconds =
+      static_cast<uint32_t>(pulseLengthSeconds * 1e6);
+
+  if (pulseLengthMicroseconds <= 0 || pulseLengthMicroseconds > 0xFFFF) {
+    *status = PARAMETER_OUT_OF_RANGE;
+    hal::SetLastError(status,
+                      "Length must be between 1 and 65535 microseconds");
+    return;
+  }
+
   tDIO::tPulse pulse;
 
   if (port->channel >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
@@ -425,9 +461,29 @@
   }
 
   digitalSystem->writePulseLength(
-      static_cast<uint16_t>(1.0e9 * pulseLength /
-                            (pwmSystem->readLoopTiming(status) * 25)),
-      status);
+      static_cast<uint16_t>(pulseLengthMicroseconds), status);
+  digitalSystem->writePulse(pulse, status);
+}
+
+void HAL_PulseMultiple(uint32_t channelMask, double pulseLengthSeconds,
+                       int32_t* status) {
+  uint32_t pulseLengthMicroseconds =
+      static_cast<uint32_t>(pulseLengthSeconds * 1e6);
+
+  if (pulseLengthMicroseconds <= 0 || pulseLengthMicroseconds > 0xFFFF) {
+    *status = PARAMETER_OUT_OF_RANGE;
+    hal::SetLastError(status,
+                      "Length must be between 1 and 65535 microseconds");
+    return;
+  }
+
+  tDIO::tPulse pulse;
+  pulse.Headers = channelMask & 0x2FF;
+  pulse.MXP = (channelMask & 0xFFFF) >> 10;
+  pulse.SPIPort = (channelMask & 0x1F) >> 26;
+
+  digitalSystem->writePulseLength(
+      static_cast<uint16_t>(pulseLengthMicroseconds), status);
   digitalSystem->writePulse(pulse, status);
 }
 
diff --git a/hal/src/main/native/athena/DMA.cpp b/hal/src/main/native/athena/DMA.cpp
index f7d8b08..23e9264 100644
--- a/hal/src/main/native/athena/DMA.cpp
+++ b/hal/src/main/native/athena/DMA.cpp
@@ -130,8 +130,9 @@
   auto dma = dmaHandles->Get(handle);
   dmaHandles->Free(handle);
 
-  if (!dma)
+  if (!dma) {
     return;
+  }
 
   int32_t status = 0;
   if (dma->manager) {
@@ -709,6 +710,9 @@
 
 void* HAL_GetDMADirectPointer(HAL_DMAHandle handle) {
   auto dma = dmaHandles->Get(handle);
+  if (dma == nullptr) {
+    return nullptr;
+  }
   return dma.get();
 }
 
diff --git a/hal/src/main/native/athena/DigitalInternal.cpp b/hal/src/main/native/athena/DigitalInternal.cpp
index 63ea0aa..b9339ee 100644
--- a/hal/src/main/native/athena/DigitalInternal.cpp
+++ b/hal/src/main/native/athena/DigitalInternal.cpp
@@ -98,8 +98,9 @@
 
   // Make sure that the 9403 IONode has had a chance to initialize before
   // continuing.
-  while (pwmSystem->readLoopTiming(status) == 0)
+  while (pwmSystem->readLoopTiming(status) == 0) {
     std::this_thread::yield();
+  }
 
   if (pwmSystem->readLoopTiming(status) != kExpectedLoopTiming) {
     *status = LOOP_TIMING_ERROR;  // NOTE: Doesn't display the error
diff --git a/hal/src/main/native/athena/DigitalInternal.h b/hal/src/main/native/athena/DigitalInternal.h
index 6b1e909..685987a 100644
--- a/hal/src/main/native/athena/DigitalInternal.h
+++ b/hal/src/main/native/athena/DigitalInternal.h
@@ -38,7 +38,7 @@
  *   reliably down to 10.0 ms; starting at about 8.5ms, the servo sometimes hums
  *   and get hot; by 5.0ms the hum is nearly continuous
  * - 10ms periods work well for Victor 884
- * - 5ms periods allows higher update rates for Luminary Micro Jaguar speed
+ * - 5ms periods allows higher update rates for Luminary Micro Jaguar motor
  *   controllers. Due to the shipping firmware on the Jaguar, we can't run the
  *   update period less than 5.05 ms.
  *
diff --git a/hal/src/main/native/athena/DutyCycle.cpp b/hal/src/main/native/athena/DutyCycle.cpp
index 6d70570..4df14cb 100644
--- a/hal/src/main/native/athena/DutyCycle.cpp
+++ b/hal/src/main/native/athena/DutyCycle.cpp
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "ConstantsInternal.h"
 #include "DigitalInternal.h"
 #include "DutyCycleInternal.h"
 #include "HALInitializer.h"
@@ -30,8 +31,6 @@
 }  // namespace init
 }  // namespace hal
 
-static constexpr int32_t kScaleFactor = 4e7 - 1;
-
 extern "C" {
 HAL_DutyCycleHandle HAL_InitializeDutyCycle(HAL_Handle digitalSourceHandle,
                                             HAL_AnalogTriggerType triggerType,
@@ -90,12 +89,6 @@
 
 double HAL_GetDutyCycleOutput(HAL_DutyCycleHandle dutyCycleHandle,
                               int32_t* status) {
-  return HAL_GetDutyCycleOutputRaw(dutyCycleHandle, status) /
-         static_cast<double>(kScaleFactor);
-}
-
-int32_t HAL_GetDutyCycleOutputRaw(HAL_DutyCycleHandle dutyCycleHandle,
-                                  int32_t* status) {
   auto dutyCycle = dutyCycleHandles->Get(dutyCycleHandle);
   if (!dutyCycle) {
     *status = HAL_HANDLE_ERROR;
@@ -104,12 +97,31 @@
 
   // TODO Handle Overflow
   unsigned char overflow = 0;
-  return dutyCycle->dutyCycle->readOutput(&overflow, status);
+  uint32_t output = dutyCycle->dutyCycle->readOutput(&overflow, status);
+  return output / static_cast<double>(kDutyCycleScaleFactor);
+}
+
+int32_t HAL_GetDutyCycleHighTime(HAL_DutyCycleHandle dutyCycleHandle,
+                                 int32_t* status) {
+  auto dutyCycle = dutyCycleHandles->Get(dutyCycleHandle);
+  if (!dutyCycle) {
+    *status = HAL_HANDLE_ERROR;
+    return 0;
+  }
+
+  // TODO Handle Overflow
+  unsigned char overflow = 0;
+  uint32_t highTime = dutyCycle->dutyCycle->readHighTicks(&overflow, status);
+  if (*status != 0) {
+    return 0;
+  }
+  // Output will be at max 4e7, so x25 will still fit in a 32 bit signed int.
+  return highTime * 25;
 }
 
 int32_t HAL_GetDutyCycleOutputScaleFactor(HAL_DutyCycleHandle dutyCycleHandle,
                                           int32_t* status) {
-  return kScaleFactor;
+  return kDutyCycleScaleFactor;
 }
 
 int32_t HAL_GetDutyCycleFPGAIndex(HAL_DutyCycleHandle dutyCycleHandle,
diff --git a/hal/src/main/native/athena/FPGACalls.cpp b/hal/src/main/native/athena/FPGACalls.cpp
new file mode 100644
index 0000000..0b04019
--- /dev/null
+++ b/hal/src/main/native/athena/FPGACalls.cpp
@@ -0,0 +1,66 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "FPGACalls.h"
+
+#include <cerrno>
+
+#include "dlfcn.h"
+#include "hal/Errors.h"
+
+static void* NiFpgaLibrary = nullptr;
+
+namespace hal {
+HAL_NiFpga_ReserveIrqContextFunc HAL_NiFpga_ReserveIrqContext;
+HAL_NiFpga_UnreserveIrqContextFunc HAL_NiFpga_UnreserveIrqContext;
+HAL_NiFpga_WaitOnIrqsFunc HAL_NiFpga_WaitOnIrqs;
+HAL_NiFpga_AcknowledgeIrqsFunc HAL_NiFpga_AcknowledgeIrqs;
+HAL_NiFpga_OpenHmbFunc HAL_NiFpga_OpenHmb;
+HAL_NiFpga_CloseHmbFunc HAL_NiFpga_CloseHmb;
+
+namespace init {
+int InitializeFPGA() {
+  NiFpgaLibrary = dlopen("libNiFpga.so", RTLD_LAZY);
+  if (!NiFpgaLibrary) {
+    return errno;
+  }
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+  HAL_NiFpga_ReserveIrqContext =
+      reinterpret_cast<HAL_NiFpga_ReserveIrqContextFunc>(
+          dlsym(NiFpgaLibrary, "NiFpgaDll_ReserveIrqContext"));
+  HAL_NiFpga_UnreserveIrqContext =
+      reinterpret_cast<HAL_NiFpga_UnreserveIrqContextFunc>(
+          dlsym(NiFpgaLibrary, "NiFpgaDll_UnreserveIrqContext"));
+  HAL_NiFpga_WaitOnIrqs = reinterpret_cast<HAL_NiFpga_WaitOnIrqsFunc>(
+      dlsym(NiFpgaLibrary, "NiFpgaDll_WaitOnIrqs"));
+  HAL_NiFpga_AcknowledgeIrqs = reinterpret_cast<HAL_NiFpga_AcknowledgeIrqsFunc>(
+      dlsym(NiFpgaLibrary, "NiFpgaDll_AcknowledgeIrqs"));
+  HAL_NiFpga_OpenHmb = reinterpret_cast<HAL_NiFpga_OpenHmbFunc>(
+      dlsym(NiFpgaLibrary, "NiFpgaDll_OpenHmb"));
+  HAL_NiFpga_CloseHmb = reinterpret_cast<HAL_NiFpga_CloseHmbFunc>(
+      dlsym(NiFpgaLibrary, "NiFpgaDll_CloseHmb"));
+#pragma GCC diagnostic pop
+
+  if (HAL_NiFpga_ReserveIrqContext == nullptr ||
+      HAL_NiFpga_UnreserveIrqContext == nullptr ||
+      HAL_NiFpga_WaitOnIrqs == nullptr ||
+      HAL_NiFpga_AcknowledgeIrqs == nullptr || HAL_NiFpga_OpenHmb == nullptr ||
+      HAL_NiFpga_CloseHmb == nullptr) {
+    HAL_NiFpga_ReserveIrqContext = nullptr;
+    HAL_NiFpga_UnreserveIrqContext = nullptr;
+    HAL_NiFpga_WaitOnIrqs = nullptr;
+    HAL_NiFpga_AcknowledgeIrqs = nullptr;
+    HAL_NiFpga_OpenHmb = nullptr;
+    HAL_NiFpga_CloseHmb = nullptr;
+    dlclose(NiFpgaLibrary);
+    NiFpgaLibrary = nullptr;
+    return NO_AVAILABLE_RESOURCES;
+  }
+
+  return HAL_SUCCESS;
+}
+}  // namespace init
+}  // namespace hal
diff --git a/hal/src/main/native/athena/FPGACalls.h b/hal/src/main/native/athena/FPGACalls.h
new file mode 100644
index 0000000..46e8ac4
--- /dev/null
+++ b/hal/src/main/native/athena/FPGACalls.h
@@ -0,0 +1,46 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#pragma once
+
+#include <FRC_FPGA_ChipObject/fpgainterfacecapi/NiFpga.h>
+
+namespace hal {
+namespace init {
+[[nodiscard]] int InitializeFPGA();
+}  // namespace init
+
+using HAL_NiFpga_ReserveIrqContextFunc =
+    NiFpga_Status (*)(NiFpga_Session session, NiFpga_IrqContext* context);
+
+extern HAL_NiFpga_ReserveIrqContextFunc HAL_NiFpga_ReserveIrqContext;
+
+using HAL_NiFpga_UnreserveIrqContextFunc =
+    NiFpga_Status (*)(NiFpga_Session session, NiFpga_IrqContext context);
+
+extern HAL_NiFpga_UnreserveIrqContextFunc HAL_NiFpga_UnreserveIrqContext;
+
+using HAL_NiFpga_WaitOnIrqsFunc = NiFpga_Status (*)(
+    NiFpga_Session session, NiFpga_IrqContext context, uint32_t irqs,
+    uint32_t timeout, uint32_t* irqsAsserted, NiFpga_Bool* timedOut);
+
+extern HAL_NiFpga_WaitOnIrqsFunc HAL_NiFpga_WaitOnIrqs;
+
+using HAL_NiFpga_AcknowledgeIrqsFunc = NiFpga_Status (*)(NiFpga_Session session,
+                                                         uint32_t irqs);
+
+extern HAL_NiFpga_AcknowledgeIrqsFunc HAL_NiFpga_AcknowledgeIrqs;
+
+using HAL_NiFpga_OpenHmbFunc = NiFpga_Status (*)(const NiFpga_Session session,
+                                                 const char* memoryName,
+                                                 size_t* memorySize,
+                                                 void** virtualAddress);
+
+extern HAL_NiFpga_OpenHmbFunc HAL_NiFpga_OpenHmb;
+
+using HAL_NiFpga_CloseHmbFunc = NiFpga_Status (*)(const NiFpga_Session session,
+                                                  const char* memoryName);
+
+extern HAL_NiFpga_CloseHmbFunc HAL_NiFpga_CloseHmb;
+}  // namespace hal
diff --git a/hal/src/main/native/athena/FRCDriverStation.cpp b/hal/src/main/native/athena/FRCDriverStation.cpp
index e7ef194..e8db9a5 100644
--- a/hal/src/main/native/athena/FRCDriverStation.cpp
+++ b/hal/src/main/native/athena/FRCDriverStation.cpp
@@ -13,39 +13,65 @@
 #include <FRC_NetworkCommunication/FRCComm.h>
 #include <FRC_NetworkCommunication/NetCommRPCProxy_Occur.h>
 #include <fmt/format.h>
+#include <wpi/EventVector.h>
 #include <wpi/SafeThread.h>
+#include <wpi/SmallVector.h>
 #include <wpi/condition_variable.h>
 #include <wpi/mutex.h>
 
+#include "HALInitializer.h"
 #include "hal/DriverStation.h"
+#include "hal/Errors.h"
 
 static_assert(sizeof(int32_t) >= sizeof(int),
               "FRC_NetworkComm status variable is larger than 32 bits");
 
+namespace {
 struct HAL_JoystickAxesInt {
   int16_t count;
   int16_t axes[HAL_kMaxJoystickAxes];
 };
+}  // namespace
 
-static constexpr int kJoystickPorts = 6;
+namespace {
+struct JoystickDataCache {
+  JoystickDataCache() { std::memset(this, 0, sizeof(*this)); }
+  void Update();
+
+  HAL_JoystickAxes axes[HAL_kMaxJoysticks];
+  HAL_JoystickPOVs povs[HAL_kMaxJoysticks];
+  HAL_JoystickButtons buttons[HAL_kMaxJoysticks];
+  HAL_AllianceStationID allianceStation;
+  float matchTime;
+};
+static_assert(std::is_standard_layout_v<JoystickDataCache>);
+// static_assert(std::is_trivial_v<JoystickDataCache>);
+
+struct FRCDriverStation {
+  wpi::EventVector newDataEvents;
+};
+}  // namespace
+
+static ::FRCDriverStation* driverStation;
 
 // Message and Data variables
 static wpi::mutex msgMutex;
 
 static int32_t HAL_GetJoystickAxesInternal(int32_t joystickNum,
                                            HAL_JoystickAxes* axes) {
-  HAL_JoystickAxesInt axesInt;
+  HAL_JoystickAxesInt netcommAxes;
 
   int retVal = FRC_NetworkCommunication_getJoystickAxes(
-      joystickNum, reinterpret_cast<JoystickAxes_t*>(&axesInt),
+      joystickNum, reinterpret_cast<JoystickAxes_t*>(&netcommAxes),
       HAL_kMaxJoystickAxes);
 
   // copy integer values to double values
-  axes->count = axesInt.count;
+  axes->count = netcommAxes.count;
   // current scaling is -128 to 127, can easily be patched in the future by
   // changing this function.
-  for (int32_t i = 0; i < axesInt.count; i++) {
-    int8_t value = axesInt.axes[i];
+  for (int32_t i = 0; i < netcommAxes.count; i++) {
+    int8_t value = netcommAxes.axes[i];
+    axes->raw[i] = value;
     if (value < 0) {
       axes->axes[i] = value / 128.0;
     } else {
@@ -68,6 +94,32 @@
   return FRC_NetworkCommunication_getJoystickButtons(
       joystickNum, &buttons->buttons, &buttons->count);
 }
+
+void JoystickDataCache::Update() {
+  for (int i = 0; i < HAL_kMaxJoysticks; i++) {
+    HAL_GetJoystickAxesInternal(i, &axes[i]);
+    HAL_GetJoystickPOVsInternal(i, &povs[i]);
+    HAL_GetJoystickButtonsInternal(i, &buttons[i]);
+  }
+  FRC_NetworkCommunication_getAllianceStation(
+      reinterpret_cast<AllianceStationID_t*>(&allianceStation));
+  FRC_NetworkCommunication_getMatchTime(&matchTime);
+}
+
+#define CHECK_JOYSTICK_NUMBER(stickNum)                  \
+  if ((stickNum) < 0 || (stickNum) >= HAL_kMaxJoysticks) \
+  return PARAMETER_OUT_OF_RANGE
+
+static HAL_ControlWord newestControlWord;
+static JoystickDataCache caches[3];
+static JoystickDataCache* currentRead = &caches[0];
+static JoystickDataCache* currentReadLocal = &caches[0];
+static std::atomic<JoystickDataCache*> currentCache{&caches[1]};
+static JoystickDataCache* lastGiven = &caches[1];
+static JoystickDataCache* cacheToUpdate = &caches[2];
+
+static wpi::mutex cacheMutex;
+
 /**
  * Retrieve the Joystick Descriptor for particular slot.
  *
@@ -102,12 +154,6 @@
   return retval;
 }
 
-static int32_t HAL_GetControlWordInternal(HAL_ControlWord* controlWord) {
-  std::memset(controlWord, 0, sizeof(HAL_ControlWord));
-  return FRC_NetworkCommunication_getControlWord(
-      reinterpret_cast<ControlWord_t*>(controlWord));
-}
-
 static int32_t HAL_GetMatchInfoInternal(HAL_MatchInfo* info) {
   MatchType_t matchType = MatchType_t::kMatchType_none;
   info->gameSpecificMessageSize = sizeof(info->gameSpecificMessage);
@@ -126,19 +172,55 @@
   return status;
 }
 
-static wpi::mutex* newDSDataAvailableMutex;
-static wpi::condition_variable* newDSDataAvailableCond;
-static int newDSDataAvailableCounter{0};
+namespace {
+struct TcpCache {
+  TcpCache() { std::memset(this, 0, sizeof(*this)); }
+  void Update(uint32_t mask);
+  void CloneTo(TcpCache* other) { std::memcpy(other, this, sizeof(*this)); }
+
+  HAL_MatchInfo matchInfo;
+  HAL_JoystickDescriptor descriptors[HAL_kMaxJoysticks];
+};
+static_assert(std::is_standard_layout_v<TcpCache>);
+}  // namespace
+
+static std::atomic_uint32_t tcpMask{0xFFFFFFFF};
+static TcpCache tcpCache;
+static TcpCache tcpCurrent;
+static wpi::mutex tcpCacheMutex;
+
+constexpr uint32_t combinedMatchInfoMask = kTcpRecvMask_MatchInfoOld |
+                                           kTcpRecvMask_MatchInfo |
+                                           kTcpRecvMask_GameSpecific;
+
+void TcpCache::Update(uint32_t mask) {
+  if ((mask & combinedMatchInfoMask) != 0) {
+    HAL_GetMatchInfoInternal(&matchInfo);
+  }
+  for (int i = 0; i < HAL_kMaxJoysticks; i++) {
+    if ((mask & (1 << i)) != 0) {
+      HAL_GetJoystickDescriptorInternal(i, &descriptors[i]);
+    }
+  }
+}
 
 namespace hal::init {
 void InitializeFRCDriverStation() {
-  static wpi::mutex newMutex;
-  newDSDataAvailableMutex = &newMutex;
-  static wpi::condition_variable newCond;
-  newDSDataAvailableCond = &newCond;
+  std::memset(&newestControlWord, 0, sizeof(newestControlWord));
+  static FRCDriverStation ds;
+  driverStation = &ds;
 }
 }  // namespace hal::init
 
+namespace hal {
+static void DefaultPrintErrorImpl(const char* line, size_t size) {
+  std::fwrite(line, size, 1, stderr);
+}
+}  // namespace hal
+
+static std::atomic<void (*)(const char* line, size_t size)> gPrintErrorImpl{
+    hal::DefaultPrintErrorImpl};
+
 extern "C" {
 
 int32_t HAL_SendError(HAL_Bool isError, int32_t errorCode, HAL_Bool isLVCode,
@@ -216,7 +298,8 @@
       if (callStack && callStack[0] != '\0') {
         fmt::format_to(fmt::appender{buf}, "{}\n", callStack);
       }
-      std::fwrite(buf.data(), buf.size(), 1, stderr);
+      auto printError = gPrintErrorImpl.load();
+      printError(buf.data(), buf.size());
     }
     if (i == KEEP_MSGS) {
       // replace the oldest one
@@ -235,6 +318,10 @@
   return retval;
 }
 
+void HAL_SetPrintErrorImpl(void (*func)(const char* line, size_t size)) {
+  gPrintErrorImpl.store(func ? func : hal::DefaultPrintErrorImpl);
+}
+
 int32_t HAL_SendConsoleLine(const char* line) {
   std::string_view lineRef{line};
   if (lineRef.size() <= 65535) {
@@ -248,36 +335,58 @@
 }
 
 int32_t HAL_GetControlWord(HAL_ControlWord* controlWord) {
-  return HAL_GetControlWordInternal(controlWord);
+  std::scoped_lock lock{cacheMutex};
+  *controlWord = newestControlWord;
+  return 0;
 }
 
 int32_t HAL_GetJoystickAxes(int32_t joystickNum, HAL_JoystickAxes* axes) {
-  return HAL_GetJoystickAxesInternal(joystickNum, axes);
+  CHECK_JOYSTICK_NUMBER(joystickNum);
+  std::scoped_lock lock{cacheMutex};
+  *axes = currentRead->axes[joystickNum];
+  return 0;
 }
 
 int32_t HAL_GetJoystickPOVs(int32_t joystickNum, HAL_JoystickPOVs* povs) {
-  return HAL_GetJoystickPOVsInternal(joystickNum, povs);
+  CHECK_JOYSTICK_NUMBER(joystickNum);
+  std::scoped_lock lock{cacheMutex};
+  *povs = currentRead->povs[joystickNum];
+  return 0;
 }
 
 int32_t HAL_GetJoystickButtons(int32_t joystickNum,
                                HAL_JoystickButtons* buttons) {
-  return HAL_GetJoystickButtonsInternal(joystickNum, buttons);
+  CHECK_JOYSTICK_NUMBER(joystickNum);
+  std::scoped_lock lock{cacheMutex};
+  *buttons = currentRead->buttons[joystickNum];
+  return 0;
+}
+
+void HAL_GetAllJoystickData(HAL_JoystickAxes* axes, HAL_JoystickPOVs* povs,
+                            HAL_JoystickButtons* buttons) {
+  std::scoped_lock lock{cacheMutex};
+  std::memcpy(axes, currentRead->axes, sizeof(currentRead->axes));
+  std::memcpy(povs, currentRead->povs, sizeof(currentRead->povs));
+  std::memcpy(buttons, currentRead->buttons, sizeof(currentRead->buttons));
 }
 
 int32_t HAL_GetJoystickDescriptor(int32_t joystickNum,
                                   HAL_JoystickDescriptor* desc) {
-  return HAL_GetJoystickDescriptorInternal(joystickNum, desc);
+  CHECK_JOYSTICK_NUMBER(joystickNum);
+  std::scoped_lock lock{tcpCacheMutex};
+  *desc = tcpCurrent.descriptors[joystickNum];
+  return 0;
 }
 
 int32_t HAL_GetMatchInfo(HAL_MatchInfo* info) {
-  return HAL_GetMatchInfoInternal(info);
+  std::scoped_lock lock{tcpCacheMutex};
+  *info = tcpCurrent.matchInfo;
+  return 0;
 }
 
 HAL_AllianceStationID HAL_GetAllianceStation(int32_t* status) {
-  HAL_AllianceStationID allianceStation;
-  *status = FRC_NetworkCommunication_getAllianceStation(
-      reinterpret_cast<AllianceStationID_t*>(&allianceStation));
-  return allianceStation;
+  std::scoped_lock lock{cacheMutex};
+  return currentRead->allianceStation;
 }
 
 HAL_Bool HAL_GetJoystickIsXbox(int32_t joystickNum) {
@@ -327,14 +436,14 @@
 
 int32_t HAL_SetJoystickOutputs(int32_t joystickNum, int64_t outputs,
                                int32_t leftRumble, int32_t rightRumble) {
+  CHECK_JOYSTICK_NUMBER(joystickNum);
   return FRC_NetworkCommunication_setJoystickOutputs(joystickNum, outputs,
                                                      leftRumble, rightRumble);
 }
 
 double HAL_GetMatchTime(int32_t* status) {
-  float matchTime;
-  *status = FRC_NetworkCommunication_getMatchTime(&matchTime);
-  return matchTime;
+  std::scoped_lock lock{cacheMutex};
+  return currentRead->matchTime;
 }
 
 void HAL_ObserveUserProgramStarting(void) {
@@ -357,103 +466,89 @@
   FRC_NetworkCommunication_observeUserProgramTest();
 }
 
-static int& GetThreadLocalLastCount() {
-  // There is a rollover error condition here. At Packet# = n * (uintmax), this
-  // will return false when instead it should return true. However, this at a
-  // 20ms rate occurs once every 2.7 years of DS connected runtime, so not
-  // worth the cycles to check.
-  thread_local int lastCount{0};
-  return lastCount;
-}
-
-HAL_Bool HAL_IsNewControlData(void) {
-  std::scoped_lock lock{*newDSDataAvailableMutex};
-  int& lastCount = GetThreadLocalLastCount();
-  int currentCount = newDSDataAvailableCounter;
-  if (lastCount == currentCount) {
-    return false;
-  }
-  lastCount = currentCount;
-  return true;
-}
-
-void HAL_WaitForDSData(void) {
-  HAL_WaitForDSDataTimeout(0);
-}
-
-HAL_Bool HAL_WaitForDSDataTimeout(double timeout) {
-  std::unique_lock lock{*newDSDataAvailableMutex};
-  int& lastCount = GetThreadLocalLastCount();
-  int currentCount = newDSDataAvailableCounter;
-  if (lastCount != currentCount) {
-    lastCount = currentCount;
-    return true;
-  }
-  auto timeoutTime =
-      std::chrono::steady_clock::now() + std::chrono::duration<double>(timeout);
-
-  while (newDSDataAvailableCounter == currentCount) {
-    if (timeout > 0) {
-      auto timedOut = newDSDataAvailableCond->wait_until(lock, timeoutTime);
-      if (timedOut == std::cv_status::timeout) {
-        return false;
-      }
-    } else {
-      newDSDataAvailableCond->wait(lock);
-    }
-  }
-  lastCount = newDSDataAvailableCounter;
-  return true;
-}
-
 // Constant number to be used for our occur handle
 constexpr int32_t refNumber = 42;
+constexpr int32_t tcpRefNumber = 94;
 
-static void newDataOccur(uint32_t refNum) {
-  // Since we could get other values, require our specific handle
-  // to signal our threads
-  if (refNum != refNumber) {
-    return;
-  }
-  std::scoped_lock lock{*newDSDataAvailableMutex};
-  // Notify all threads
-  ++newDSDataAvailableCounter;
-  newDSDataAvailableCond->notify_all();
+static void tcpOccur(void) {
+  uint32_t mask = FRC_NetworkCommunication_getNewTcpRecvMask();
+  tcpMask.fetch_or(mask);
 }
 
-/*
- * Call this to initialize the driver station communication. This will properly
- * handle multiple calls. However note that this CANNOT be called from a library
- * that interfaces with LabVIEW.
- */
-void HAL_InitializeDriverStation(void) {
-  static std::atomic_bool initialized{false};
-  static wpi::mutex initializeMutex;
-  // Initial check, as if it's true initialization has finished
-  if (initialized) {
-    return;
-  }
+static void udpOccur(void) {
+  cacheToUpdate->Update();
 
-  std::scoped_lock lock(initializeMutex);
-  // Second check in case another thread was waiting
-  if (initialized) {
-    return;
+  JoystickDataCache* given = cacheToUpdate;
+  JoystickDataCache* prev = currentCache.exchange(cacheToUpdate);
+  if (prev == nullptr) {
+    cacheToUpdate = currentReadLocal;
+    currentReadLocal = lastGiven;
+  } else {
+    // Current read local does not update
+    cacheToUpdate = prev;
   }
+  lastGiven = given;
 
+  driverStation->newDataEvents.Wakeup();
+}
+
+static void newDataOccur(uint32_t refNum) {
+  switch (refNum) {
+    case refNumber:
+      udpOccur();
+      break;
+
+    case tcpRefNumber:
+      tcpOccur();
+      break;
+
+    default:
+      std::printf("Unknown occur %u\n", refNum);
+      break;
+  }
+}
+
+void HAL_RefreshDSData(void) {
+  HAL_ControlWord controlWord;
+  std::memset(&controlWord, 0, sizeof(controlWord));
+  FRC_NetworkCommunication_getControlWord(
+      reinterpret_cast<ControlWord_t*>(&controlWord));
+  std::scoped_lock lock{cacheMutex};
+  JoystickDataCache* prev = currentCache.exchange(nullptr);
+  if (prev != nullptr) {
+    currentRead = prev;
+  }
+  newestControlWord = controlWord;
+
+  uint32_t mask = tcpMask.exchange(0);
+  if (mask != 0) {
+    tcpCache.Update(mask);
+    std::scoped_lock tcpLock(tcpCacheMutex);
+    tcpCache.CloneTo(&tcpCurrent);
+  }
+}
+
+void HAL_ProvideNewDataEventHandle(WPI_EventHandle handle) {
+  hal::init::CheckInit();
+  driverStation->newDataEvents.Add(handle);
+}
+
+void HAL_RemoveNewDataEventHandle(WPI_EventHandle handle) {
+  driverStation->newDataEvents.Remove(handle);
+}
+
+HAL_Bool HAL_GetOutputsEnabled(void) {
+  return FRC_NetworkCommunication_getWatchdogActive();
+}
+
+}  // extern "C"
+
+namespace hal {
+void InitializeDriverStation() {
   // Set up the occur function internally with NetComm
   NetCommRPCProxy_SetOccurFuncPointer(newDataOccur);
   // Set up our occur reference number
   setNewDataOccurRef(refNumber);
-
-  initialized = true;
+  FRC_NetworkCommunication_setNewTcpDataOccurRef(tcpRefNumber);
 }
-
-/*
- * Releases the DS Mutex to allow proper shutdown of any threads that are
- * waiting on it.
- */
-void HAL_ReleaseDSMutex(void) {
-  newDataOccur(refNumber);
-}
-
-}  // extern "C"
+}  // namespace hal
diff --git a/hal/src/main/native/athena/HAL.cpp b/hal/src/main/native/athena/HAL.cpp
index 93e5c15..3a8cefd 100644
--- a/hal/src/main/native/athena/HAL.cpp
+++ b/hal/src/main/native/athena/HAL.cpp
@@ -18,9 +18,14 @@
 #include <FRC_NetworkCommunication/LoadOut.h>
 #include <FRC_NetworkCommunication/UsageReporting.h>
 #include <fmt/format.h>
+#include <wpi/MemoryBuffer.h>
+#include <wpi/SmallString.h>
+#include <wpi/StringExtras.h>
+#include <wpi/fs.h>
 #include <wpi/mutex.h>
 #include <wpi/timestamp.h>
 
+#include "FPGACalls.h"
 #include "HALInitializer.h"
 #include "HALInternal.h"
 #include "hal/ChipObject.h"
@@ -28,6 +33,7 @@
 #include "hal/Errors.h"
 #include "hal/Notifier.h"
 #include "hal/handles/HandlesInternal.h"
+#include "hal/roborio/InterruptManager.h"
 #include "visa/visa.h"
 
 using namespace hal;
@@ -36,9 +42,14 @@
 static std::unique_ptr<tSysWatchdog> watchdog;
 static uint64_t dsStartTime;
 
+static char roboRioCommentsString[64];
+static size_t roboRioCommentsStringSize;
+static bool roboRioCommentsStringInitialized;
+
 using namespace hal;
 
 namespace hal {
+void InitializeDriverStation();
 namespace init {
 void InitializeHAL() {
   InitializeCTREPCM();
@@ -79,6 +90,7 @@
 }  // namespace init
 
 void ReleaseFPGAInterrupt(int32_t interruptNumber) {
+  hal::init::CheckInit();
   if (!global) {
     return;
   }
@@ -249,6 +261,7 @@
 }
 
 int32_t HAL_GetFPGAVersion(int32_t* status) {
+  hal::init::CheckInit();
   if (!global) {
     *status = NiFpga_Status_ResourceNotInitialized;
     return 0;
@@ -257,6 +270,7 @@
 }
 
 int64_t HAL_GetFPGARevision(int32_t* status) {
+  hal::init::CheckInit();
   if (!global) {
     *status = NiFpga_Status_ResourceNotInitialized;
     return 0;
@@ -264,7 +278,83 @@
   return global->readRevision(status);
 }
 
+size_t HAL_GetSerialNumber(char* buffer, size_t size) {
+  const char* serialNum = std::getenv("serialnum");
+  if (serialNum) {
+    std::strncpy(buffer, serialNum, size);
+    buffer[size - 1] = '\0';
+    return std::strlen(buffer);
+  } else {
+    if (size > 0) {
+      buffer[0] = '\0';
+    }
+    return 0;
+  }
+}
+
+void InitializeRoboRioComments(void) {
+  if (!roboRioCommentsStringInitialized) {
+    std::error_code ec;
+    std::unique_ptr<wpi::MemoryBuffer> fileBuffer =
+        wpi::MemoryBuffer::GetFile("/etc/machine-info", ec);
+
+    std::string_view fileContents;
+    if (fileBuffer && !ec) {
+      fileContents =
+          std::string_view(reinterpret_cast<const char*>(fileBuffer->begin()),
+                           fileBuffer->size());
+    } else {
+      roboRioCommentsStringSize = 0;
+      roboRioCommentsStringInitialized = true;
+      return;
+    }
+    std::string_view searchString = "PRETTY_HOSTNAME=\"";
+
+    size_t start = fileContents.find(searchString);
+    if (start == std::string_view::npos) {
+      roboRioCommentsStringSize = 0;
+      roboRioCommentsStringInitialized = true;
+      return;
+    }
+    start += searchString.size();
+    size_t end = fileContents.find("\"", start);
+    if (end == std::string_view::npos) {
+      end = fileContents.size();
+    }
+    std::string_view escapedComments = wpi::slice(fileContents, start, end);
+    wpi::SmallString<64> buf;
+    auto [unescapedComments, rem] = wpi::UnescapeCString(escapedComments, buf);
+    unescapedComments.copy(roboRioCommentsString,
+                           sizeof(roboRioCommentsString));
+
+    if (unescapedComments.size() > sizeof(roboRioCommentsString)) {
+      roboRioCommentsStringSize = sizeof(roboRioCommentsString);
+    } else {
+      roboRioCommentsStringSize = unescapedComments.size();
+    }
+    roboRioCommentsStringInitialized = true;
+  }
+}
+
+size_t HAL_GetComments(char* buffer, size_t size) {
+  if (!roboRioCommentsStringInitialized) {
+    InitializeRoboRioComments();
+  }
+  size_t toCopy = size;
+  if (size > roboRioCommentsStringSize) {
+    toCopy = roboRioCommentsStringSize;
+  }
+  std::memcpy(buffer, roboRioCommentsString, toCopy);
+  if (toCopy < size) {
+    buffer[toCopy] = '\0';
+  } else {
+    buffer[toCopy - 1] = '\0';
+  }
+  return toCopy;
+}
+
 uint64_t HAL_GetFPGATime(int32_t* status) {
+  hal::init::CheckInit();
   if (!global) {
     *status = NiFpga_Status_ResourceNotInitialized;
     return 0;
@@ -311,6 +401,7 @@
 }
 
 HAL_Bool HAL_GetFPGAButton(int32_t* status) {
+  hal::init::CheckInit();
   if (!global) {
     *status = NiFpga_Status_ResourceNotInitialized;
     return false;
@@ -319,6 +410,7 @@
 }
 
 HAL_Bool HAL_GetSystemActive(int32_t* status) {
+  hal::init::CheckInit();
   if (!watchdog) {
     *status = NiFpga_Status_ResourceNotInitialized;
     return false;
@@ -327,6 +419,7 @@
 }
 
 HAL_Bool HAL_GetBrownedOut(int32_t* status) {
+  hal::init::CheckInit();
   if (!watchdog) {
     *status = NiFpga_Status_ResourceNotInitialized;
     return false;
@@ -392,6 +485,11 @@
     return true;
   }
 
+  int fpgaInit = hal::init::InitializeFPGA();
+  if (fpgaInit != HAL_SUCCESS) {
+    return false;
+  }
+
   hal::init::InitializeHAL();
 
   hal::init::HAL_IsInitialized.store(true);
@@ -424,7 +522,9 @@
     return false;
   }
 
-  HAL_InitializeDriverStation();
+  InterruptManager::Initialize(global->getSystemInterface());
+
+  hal::InitializeDriverStation();
 
   dsStartTime = HAL_GetFPGATime(&status);
   if (status != 0) {
diff --git a/hal/src/main/native/athena/InterruptManager.cpp b/hal/src/main/native/athena/InterruptManager.cpp
new file mode 100644
index 0000000..420f806
--- /dev/null
+++ b/hal/src/main/native/athena/InterruptManager.cpp
@@ -0,0 +1,72 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "hal/roborio/InterruptManager.h"
+
+#include <fmt/format.h>
+
+#include "FPGACalls.h"
+#include "HALInternal.h"
+#include "dlfcn.h"
+#include "hal/Errors.h"
+
+using namespace hal;
+
+InterruptManager& InterruptManager::GetInstance() {
+  static InterruptManager manager;
+  return manager;
+}
+
+void InterruptManager::Initialize(tSystemInterface* baseSystem) {
+  auto& manager = GetInstance();
+  manager.fpgaSession = baseSystem->getHandle();
+}
+
+NiFpga_IrqContext InterruptManager::GetContext() noexcept {
+  NiFpga_IrqContext context;
+  HAL_NiFpga_ReserveIrqContext(fpgaSession, &context);
+  return context;
+}
+
+void InterruptManager::ReleaseContext(NiFpga_IrqContext context) noexcept {
+  HAL_NiFpga_UnreserveIrqContext(fpgaSession, context);
+}
+
+uint32_t InterruptManager::WaitForInterrupt(NiFpga_IrqContext context,
+                                            uint32_t mask, bool ignorePrevious,
+                                            uint32_t timeoutMs,
+                                            int32_t* status) {
+  {
+    // Make sure we can safely use this
+    std::scoped_lock lock(currentMaskMutex);
+    if ((currentMask & mask) != 0) {
+      *status = PARAMETER_OUT_OF_RANGE;
+      hal::SetLastError(
+          status, fmt::format("Interrupt mask {} has bits {} already in use",
+                              mask, (currentMask & mask)));
+      return 0;
+    }
+    currentMask |= mask;
+  }
+
+  if (ignorePrevious) {
+    HAL_NiFpga_AcknowledgeIrqs(fpgaSession, mask);
+  }
+
+  uint32_t irqsAsserted = 0;
+  NiFpga_Bool timedOut = 0;
+  *status = HAL_NiFpga_WaitOnIrqs(fpgaSession, context, mask, timeoutMs,
+                                  &irqsAsserted, &timedOut);
+
+  if (!timedOut) {
+    HAL_NiFpga_AcknowledgeIrqs(fpgaSession, irqsAsserted);
+  }
+
+  {
+    std::scoped_lock lock(currentMaskMutex);
+    currentMask &= ~mask;
+  }
+
+  return irqsAsserted;
+}
diff --git a/hal/src/main/native/athena/Interrupts.cpp b/hal/src/main/native/athena/Interrupts.cpp
index 943a1aa..e40c6f4 100644
--- a/hal/src/main/native/athena/Interrupts.cpp
+++ b/hal/src/main/native/athena/Interrupts.cpp
@@ -17,6 +17,7 @@
 #include "hal/HALBase.h"
 #include "hal/handles/HandlesInternal.h"
 #include "hal/handles/LimitedHandleResource.h"
+#include "hal/roborio/InterruptManager.h"
 
 using namespace hal;
 
@@ -24,7 +25,9 @@
 
 struct Interrupt {
   std::unique_ptr<tInterrupt> anInterrupt;
-  std::unique_ptr<tInterruptManager> manager;
+  InterruptManager& manager = InterruptManager::GetInstance();
+  NiFpga_IrqContext irqContext = nullptr;
+  uint32_t mask;
 };
 
 }  // namespace
@@ -55,8 +58,8 @@
   // Expects the calling leaf class to allocate an interrupt index.
   anInterrupt->anInterrupt.reset(tInterrupt::create(interruptIndex, status));
   anInterrupt->anInterrupt->writeConfig_WaitForAck(false, status);
-  anInterrupt->manager = std::make_unique<tInterruptManager>(
-      (1u << interruptIndex) | (1u << (interruptIndex + 8u)), true, status);
+  anInterrupt->irqContext = anInterrupt->manager.GetContext();
+  anInterrupt->mask = (1u << interruptIndex) | (1u << (interruptIndex + 8u));
   return handle;
 }
 
@@ -66,6 +69,9 @@
   if (anInterrupt == nullptr) {
     return;
   }
+  if (anInterrupt->irqContext) {
+    anInterrupt->manager.ReleaseContext(anInterrupt->irqContext);
+  }
 }
 
 int64_t HAL_WaitForInterrupt(HAL_InterruptHandle interruptHandle,
@@ -78,8 +84,33 @@
     return 0;
   }
 
-  result = anInterrupt->manager->watch(static_cast<int32_t>(timeout * 1e3),
-                                       ignorePrevious, status);
+  result = anInterrupt->manager.WaitForInterrupt(
+      anInterrupt->irqContext, anInterrupt->mask, ignorePrevious,
+      static_cast<uint32_t>(timeout * 1e3), status);
+
+  // Don't report a timeout as an error - the return code is enough to tell
+  // that a timeout happened.
+  if (*status == -NiFpga_Status_IrqTimeout) {
+    *status = NiFpga_Status_Success;
+  }
+
+  return result;
+}
+
+int64_t HAL_WaitForMultipleInterrupts(HAL_InterruptHandle interruptHandle,
+                                      int64_t mask, double timeout,
+                                      HAL_Bool ignorePrevious,
+                                      int32_t* status) {
+  uint32_t result;
+  auto anInterrupt = interruptHandles->Get(interruptHandle);
+  if (anInterrupt == nullptr) {
+    *status = HAL_HANDLE_ERROR;
+    return 0;
+  }
+
+  result = anInterrupt->manager.WaitForInterrupt(
+      anInterrupt->irqContext, mask, ignorePrevious,
+      static_cast<uint32_t>(timeout * 1e3), status);
 
   // Don't report a timeout as an error - the return code is enough to tell
   // that a timeout happened.
diff --git a/hal/src/main/native/athena/Notifier.cpp b/hal/src/main/native/athena/Notifier.cpp
index d684031..9c818d8 100644
--- a/hal/src/main/native/athena/Notifier.cpp
+++ b/hal/src/main/native/athena/Notifier.cpp
@@ -20,6 +20,7 @@
 #include "hal/HAL.h"
 #include "hal/Threads.h"
 #include "hal/handles/UnlimitedHandleResource.h"
+#include "hal/roborio/InterruptManager.h"
 
 using namespace hal;
 
@@ -106,15 +107,21 @@
 }
 
 static void notifierThreadMain() {
-  tRioStatusCode status = 0;
-  tInterruptManager manager{1 << kTimerInterruptNumber, true, &status};
+  InterruptManager& manager = InterruptManager::GetInstance();
+  NiFpga_IrqContext context = manager.GetContext();
+  uint32_t mask = 1 << kTimerInterruptNumber;
+  int32_t status = 0;
+
   while (notifierRunning) {
-    auto triggeredMask = manager.watch(10000, false, &status);
+    status = 0;
+    auto triggeredMask =
+        manager.WaitForInterrupt(context, mask, false, 10000, &status);
     if (!notifierRunning) {
       break;
     }
-    if (triggeredMask == 0)
+    if (triggeredMask == 0) {
       continue;
+    }
     alarmCallback();
   }
 }
@@ -195,8 +202,9 @@
 
 void HAL_StopNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
   auto notifier = notifierHandles->Get(notifierHandle);
-  if (!notifier)
+  if (!notifier) {
     return;
+  }
 
   {
     std::scoped_lock lock(notifier->mutex);
@@ -209,8 +217,9 @@
 
 void HAL_CleanNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
   auto notifier = notifierHandles->Free(notifierHandle);
-  if (!notifier)
+  if (!notifier) {
     return;
+  }
 
   // Just in case HAL_StopNotifier() wasn't called...
   {
@@ -244,8 +253,9 @@
 void HAL_UpdateNotifierAlarm(HAL_NotifierHandle notifierHandle,
                              uint64_t triggerTime, int32_t* status) {
   auto notifier = notifierHandles->Get(notifierHandle);
-  if (!notifier)
+  if (!notifier) {
     return;
+  }
 
   {
     std::scoped_lock lock(notifier->mutex);
@@ -270,8 +280,9 @@
 void HAL_CancelNotifierAlarm(HAL_NotifierHandle notifierHandle,
                              int32_t* status) {
   auto notifier = notifierHandles->Get(notifierHandle);
-  if (!notifier)
+  if (!notifier) {
     return;
+  }
 
   {
     std::scoped_lock lock(notifier->mutex);
@@ -282,8 +293,9 @@
 uint64_t HAL_WaitForNotifierAlarm(HAL_NotifierHandle notifierHandle,
                                   int32_t* status) {
   auto notifier = notifierHandles->Get(notifierHandle);
-  if (!notifier)
+  if (!notifier) {
     return 0;
+  }
   std::unique_lock lock(notifier->mutex);
   notifier->cond.wait(lock, [&] {
     return !notifier->active || notifier->triggeredTime != UINT64_MAX;
diff --git a/hal/src/main/native/athena/PortsInternal.h b/hal/src/main/native/athena/PortsInternal.h
index 18ce569..ff57703 100644
--- a/hal/src/main/native/athena/PortsInternal.h
+++ b/hal/src/main/native/athena/PortsInternal.h
@@ -23,7 +23,8 @@
     kNumDigitalHeaders + kNumDigitalMXPChannels + kNumDigitalSPIPortChannels;
 constexpr int32_t kNumPWMChannels = tPWM::kNumMXPRegisters + kNumPWMHeaders;
 constexpr int32_t kNumDigitalPWMOutputs =
-    tDIO::kNumPWMDutyCycleAElements + tDIO::kNumPWMDutyCycleBElements;
+    static_cast<int32_t>(tDIO::kNumPWMDutyCycleAElements) +
+    static_cast<int32_t>(tDIO::kNumPWMDutyCycleBElements);
 constexpr int32_t kNumEncoders = tEncoder::kNumSystems;
 constexpr int32_t kNumInterrupts = tInterrupt::kNumSystems;
 constexpr int32_t kNumRelayChannels = 8;
diff --git a/hal/src/main/native/athena/REVPH.cpp b/hal/src/main/native/athena/REVPH.cpp
index 5b18de6..ae3f460 100644
--- a/hal/src/main/native/athena/REVPH.cpp
+++ b/hal/src/main/native/athena/REVPH.cpp
@@ -228,18 +228,19 @@
               sizeof(hph->desiredSolenoidsState));
   std::memset(&hph->versionInfo, 0, sizeof(hph->versionInfo));
 
+  int32_t can_status = 0;
+
   // Start closed-loop compressor control by starting solenoid state updates
-  HAL_SendREVPHSolenoidsState(hph.get(), status);
+  HAL_SendREVPHSolenoidsState(hph.get(), &can_status);
 
   return handle;
 }
 
 void HAL_FreeREVPH(HAL_REVPHHandle handle) {
   auto hph = REVPHHandles->Get(handle);
-  if (hph == nullptr)
-    return;
-
-  HAL_CleanCAN(hph->hcan);
+  if (hph) {
+    HAL_CleanCAN(hph->hcan);
+  }
 
   REVPHHandles->Free(handle);
 }
diff --git a/hal/src/main/native/athena/SPI.cpp b/hal/src/main/native/athena/SPI.cpp
index 0df6fdb..9d3335e 100644
--- a/hal/src/main/native/athena/SPI.cpp
+++ b/hal/src/main/native/athena/SPI.cpp
@@ -125,8 +125,8 @@
       // CS0 is not a DIO port, so nothing to allocate
       handle = open("/dev/spidev0.0", O_RDWR);
       if (handle < 0) {
-        fmt::print("Failed to open SPI port {}: {}\n", port,
-                   std::strerror(errno));
+        fmt::print("Failed to open SPI port {}: {}\n",
+                   static_cast<int32_t>(port), std::strerror(errno));
         CommonSPIPortFree();
         return;
       }
@@ -147,8 +147,8 @@
       }
       handle = open("/dev/spidev0.1", O_RDWR);
       if (handle < 0) {
-        fmt::print("Failed to open SPI port {}: {}\n", port,
-                   std::strerror(errno));
+        fmt::print("Failed to open SPI port {}: {}\n",
+                   static_cast<int32_t>(port), std::strerror(errno));
         CommonSPIPortFree();
         HAL_FreeDIOPort(digitalHandles[0]);
         return;
@@ -170,8 +170,8 @@
       }
       handle = open("/dev/spidev0.2", O_RDWR);
       if (handle < 0) {
-        fmt::print("Failed to open SPI port {}: {}\n", port,
-                   std::strerror(errno));
+        fmt::print("Failed to open SPI port {}: {}\n",
+                   static_cast<int32_t>(port), std::strerror(errno));
         CommonSPIPortFree();
         HAL_FreeDIOPort(digitalHandles[1]);
         return;
@@ -193,8 +193,8 @@
       }
       handle = open("/dev/spidev0.3", O_RDWR);
       if (handle < 0) {
-        fmt::print("Failed to open SPI port {}: {}\n", port,
-                   std::strerror(errno));
+        fmt::print("Failed to open SPI port {}: {}\n",
+                   static_cast<int32_t>(port), std::strerror(errno));
         CommonSPIPortFree();
         HAL_FreeDIOPort(digitalHandles[2]);
         return;
@@ -240,8 +240,8 @@
           digitalSystem->readEnableMXPSpecialFunction(status) | 0x00F0, status);
       handle = open("/dev/spidev1.0", O_RDWR);
       if (handle < 0) {
-        fmt::print("Failed to open SPI port {}: {}\n", port,
-                   std::strerror(errno));
+        fmt::print("Failed to open SPI port {}: {}\n",
+                   static_cast<int32_t>(port), std::strerror(errno));
         HAL_FreeDIOPort(digitalHandles[5]);  // free the first port allocated
         HAL_FreeDIOPort(digitalHandles[6]);  // free the second port allocated
         HAL_FreeDIOPort(digitalHandles[7]);  // free the third port allocated
@@ -364,19 +364,27 @@
   ioctl(HAL_GetSPIHandle(port), SPI_IOC_WR_MAX_SPEED_HZ, &speed);
 }
 
-void HAL_SetSPIOpts(HAL_SPIPort port, HAL_Bool msbFirst,
-                    HAL_Bool sampleOnTrailing, HAL_Bool clkIdleHigh) {
+void HAL_SetSPIMode(HAL_SPIPort port, HAL_SPIMode mode) {
   if (port < 0 || port >= kSpiMaxHandles) {
     return;
   }
 
-  uint8_t mode = 0;
-  mode |= (!msbFirst ? 8 : 0);
-  mode |= (clkIdleHigh ? 2 : 0);
-  mode |= (sampleOnTrailing ? 1 : 0);
+  uint8_t mode8 = mode & SPI_MODE_3;
 
   std::scoped_lock lock(spiApiMutexes[port]);
-  ioctl(HAL_GetSPIHandle(port), SPI_IOC_WR_MODE, &mode);
+  ioctl(HAL_GetSPIHandle(port), SPI_IOC_WR_MODE, &mode8);
+}
+
+HAL_SPIMode HAL_GetSPIMode(HAL_SPIPort port) {
+  if (port < 0 || port >= kSpiMaxHandles) {
+    return HAL_SPI_kMode0;
+  }
+
+  uint8_t mode8 = 0;
+
+  std::scoped_lock lock(spiApiMutexes[port]);
+  ioctl(HAL_GetSPIHandle(port), SPI_IOC_RD_MODE, &mode8);
+  return static_cast<HAL_SPIMode>(mode8 & SPI_MODE_3);
 }
 
 void HAL_SetSPIChipSelectActiveHigh(HAL_SPIPort port, int32_t* status) {
diff --git a/hal/src/main/native/athena/cpp/SerialHelper.cpp b/hal/src/main/native/athena/cpp/SerialHelper.cpp
index 3831769..fef92d4 100644
--- a/hal/src/main/native/athena/cpp/SerialHelper.cpp
+++ b/hal/src/main/native/athena/cpp/SerialHelper.cpp
@@ -193,7 +193,7 @@
     ViSession vSession;
     *status = viOpen(m_resourceHandle, desc, VI_NULL, VI_NULL, &vSession);
     if (*status < 0)
-      goto done;
+      continue;
     *status = 0;
 
     *status = viGetAttribute(vSession, VI_ATTR_INTF_INST_NAME, &osName);
@@ -201,9 +201,9 @@
     // Use a separate close variable so we can check
     ViStatus closeStatus = viClose(vSession);
     if (*status < 0)
-      goto done;
+      continue;
     if (closeStatus < 0)
-      goto done;
+      continue;
     *status = 0;
 
     // split until (/dev/
diff --git a/hal/src/main/native/athena/mockdata/DriverStationData.cpp b/hal/src/main/native/athena/mockdata/DriverStationData.cpp
index ba519fc..76a8e8b 100644
--- a/hal/src/main/native/athena/mockdata/DriverStationData.cpp
+++ b/hal/src/main/native/athena/mockdata/DriverStationData.cpp
@@ -103,13 +103,13 @@
 
 void HALSIM_SetJoystickType(int32_t stick, int32_t type) {}
 
-void HALSIM_SetJoystickName(int32_t stick, const char* name) {}
+void HALSIM_SetJoystickName(int32_t stick, const char* name, size_t size) {}
 
 void HALSIM_SetJoystickAxisType(int32_t stick, int32_t axis, int32_t type) {}
 
-void HALSIM_SetGameSpecificMessage(const char* message) {}
+void HALSIM_SetGameSpecificMessage(const char* message, size_t size) {}
 
-void HALSIM_SetEventName(const char* name) {}
+void HALSIM_SetEventName(const char* name, size_t size) {}
 
 void HALSIM_SetMatchType(HAL_MatchType type) {}
 
diff --git a/hal/src/main/native/athena/mockdata/MockHooks.cpp b/hal/src/main/native/athena/mockdata/MockHooks.cpp
index 0ea05d0..a6a6804 100644
--- a/hal/src/main/native/athena/mockdata/MockHooks.cpp
+++ b/hal/src/main/native/athena/mockdata/MockHooks.cpp
@@ -48,4 +48,6 @@
 
 void HALSIM_CancelSimPeriodicAfterCallback(int32_t uid) {}
 
+void HALSIM_CancelAllSimPeriodicCallbacks(void) {}
+
 }  // extern "C"
diff --git a/hal/src/main/native/athena/mockdata/Reset.cpp b/hal/src/main/native/athena/mockdata/Reset.cpp
new file mode 100644
index 0000000..7cbeea3
--- /dev/null
+++ b/hal/src/main/native/athena/mockdata/Reset.cpp
@@ -0,0 +1,5 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+extern "C" void HALSIM_ResetAllSimData(void) {}
diff --git a/hal/src/main/native/athena/mockdata/RoboRioData.cpp b/hal/src/main/native/athena/mockdata/RoboRioData.cpp
index 9f6683a..9392fcb 100644
--- a/hal/src/main/native/athena/mockdata/RoboRioData.cpp
+++ b/hal/src/main/native/athena/mockdata/RoboRioData.cpp
@@ -29,6 +29,32 @@
 DEFINE_CAPI(int32_t, UserFaults3V3, 0)
 DEFINE_CAPI(double, BrownoutVoltage, 6.75)
 
+int32_t HALSIM_RegisterRoboRioSerialNumberCallback(
+    HAL_RoboRioStringCallback callback, void* param, HAL_Bool initialNotify) {
+  return 0;
+}
+void HALSIM_CancelRoboRioSerialNumberCallback(int32_t uid) {}
+size_t HALSIM_GetRoboRioSerialNumber(char* buffer, size_t size) {
+  if (size > 0) {
+    buffer[0] = '\0';
+  }
+  return 0;
+}
+void HALSIM_SetRoboRioSerialNumber(const char* buffer, size_t size) {}
+
+int32_t HALSIM_RegisterRoboRioCommentsCallback(
+    HAL_RoboRioStringCallback callback, void* param, HAL_Bool initialNotify) {
+  return 0;
+}
+void HALSIM_CancelRoboRioCommentsCallback(int32_t uid) {}
+size_t HALSIM_GetRoboRioComments(char* buffer, size_t size) {
+  if (size > 0) {
+    buffer[0] = '\0';
+  }
+  return 0;
+}
+void HALSIM_SetRoboRioComments(const char* buffer, size_t size) {}
+
 void HALSIM_RegisterRoboRioAllCallbacks(HAL_NotifyCallback callback,
                                         void* param, HAL_Bool initialNotify) {}
 }  // extern "C"
diff --git a/hal/src/main/native/cpp/jni/CANJNI.cpp b/hal/src/main/native/cpp/jni/CANJNI.cpp
index 968f656..4ad2a74 100644
--- a/hal/src/main/native/cpp/jni/CANJNI.cpp
+++ b/hal/src/main/native/cpp/jni/CANJNI.cpp
@@ -62,9 +62,7 @@
   if (!CheckCANStatus(env, status, *messageIDPtr)) {
     return nullptr;
   }
-  return MakeJByteArray(env,
-                        std::string_view{reinterpret_cast<const char*>(buffer),
-                                         static_cast<size_t>(dataSize)});
+  return MakeJByteArray(env, {buffer, static_cast<size_t>(dataSize)});
 }
 
 /*
@@ -93,4 +91,90 @@
                      txFullCount, receiveErrorCount, transmitErrorCount);
 }
 
+/*
+ * Class:     edu_wpi_first_hal_can_CANJNI
+ * Method:    openCANStreamSession
+ * Signature: (III)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_can_CANJNI_openCANStreamSession
+  (JNIEnv* env, jclass, jint messageID, jint messageIDMask, jint maxMessages)
+{
+  uint32_t handle = 0;
+  int32_t status = 0;
+  HAL_CAN_OpenStreamSession(&handle, static_cast<uint32_t>(messageID),
+                            static_cast<uint32_t>(messageIDMask),
+                            static_cast<uint32_t>(maxMessages), &status);
+
+  if (!CheckStatus(env, status)) {
+    return static_cast<jint>(0);
+  }
+
+  return static_cast<jint>(handle);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_can_CANJNI
+ * Method:    closeCANStreamSession
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_can_CANJNI_closeCANStreamSession
+  (JNIEnv* env, jclass, jint sessionHandle)
+{
+  HAL_CAN_CloseStreamSession(static_cast<uint32_t>(sessionHandle));
+}
+
+/*
+ * Class:     edu_wpi_first_hal_can_CANJNI
+ * Method:    readCANStreamSession
+ * Signature: (I[Ljava/lang/Object;I)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_can_CANJNI_readCANStreamSession
+  (JNIEnv* env, jclass, jint sessionHandle, jobjectArray messages,
+   jint messagesToRead)
+{
+  uint32_t handle = static_cast<uint32_t>(sessionHandle);
+  uint32_t messagesRead = 0;
+
+  wpi::SmallVector<HAL_CANStreamMessage, 16> messageBuffer;
+  messageBuffer.resize_for_overwrite(messagesToRead);
+
+  int32_t status = 0;
+
+  HAL_CAN_ReadStreamSession(handle, messageBuffer.begin(),
+                            static_cast<uint32_t>(messagesToRead),
+                            &messagesRead, &status);
+
+  if (status == HAL_ERR_CANSessionMux_MessageNotFound || messagesRead == 0) {
+    return 0;
+  }
+
+  if (!CheckStatus(env, status)) {
+    return 0;
+  }
+
+  for (int i = 0; i < static_cast<int>(messagesRead); i++) {
+    struct HAL_CANStreamMessage* msg = &messageBuffer[i];
+    JLocal<jobject> elem{
+        env, static_cast<jstring>(env->GetObjectArrayElement(messages, i))};
+    if (!elem) {
+      // TODO decide if should throw
+      continue;
+    }
+    JLocal<jbyteArray> toSetArray{
+        env, SetCANStreamObject(env, elem, msg->dataSize, msg->messageID,
+                                msg->timeStamp)};
+    auto javaLen = env->GetArrayLength(toSetArray);
+    if (javaLen < msg->dataSize) {
+      msg->dataSize = javaLen;
+    }
+    env->SetByteArrayRegion(toSetArray, 0, msg->dataSize,
+                            reinterpret_cast<jbyte*>(msg->data));
+  }
+
+  return static_cast<jint>(messagesRead);
+}
+
 }  // extern "C"
diff --git a/hal/src/main/native/cpp/jni/DIOJNI.cpp b/hal/src/main/native/cpp/jni/DIOJNI.cpp
index 5cd6c2e..dee34c7 100644
--- a/hal/src/main/native/cpp/jni/DIOJNI.cpp
+++ b/hal/src/main/native/cpp/jni/DIOJNI.cpp
@@ -146,6 +146,20 @@
 
 /*
  * Class:     edu_wpi_first_hal_DIOJNI
+ * Method:    pulseMultiple
+ * Signature: (JD)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_DIOJNI_pulseMultiple
+  (JNIEnv* env, jclass, jlong channelMask, jdouble value)
+{
+  int32_t status = 0;
+  HAL_PulseMultiple(static_cast<uint32_t>(channelMask), value, &status);
+  CheckStatus(env, status);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DIOJNI
  * Method:    isPulsing
  * Signature: (I)Z
  */
@@ -248,6 +262,20 @@
 
 /*
  * Class:     edu_wpi_first_hal_DIOJNI
+ * Method:    setDigitalPWMPPS
+ * Signature: (ID)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_DIOJNI_setDigitalPWMPPS
+  (JNIEnv* env, jclass, jint id, jdouble value)
+{
+  int32_t status = 0;
+  HAL_SetDigitalPWMPPS((HAL_DigitalPWMHandle)id, value, &status);
+  CheckStatus(env, status);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DIOJNI
  * Method:    setDigitalPWMOutputChannel
  * Signature: (II)V
  */
diff --git a/hal/src/main/native/cpp/jni/DriverStationJNI.cpp b/hal/src/main/native/cpp/jni/DriverStationJNI.cpp
new file mode 100644
index 0000000..f050f02
--- /dev/null
+++ b/hal/src/main/native/cpp/jni/DriverStationJNI.cpp
@@ -0,0 +1,450 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include <jni.h>
+
+#include <cassert>
+
+#include <fmt/format.h>
+#include <wpi/jni_util.h>
+
+#include "HALUtil.h"
+#include "edu_wpi_first_hal_DriverStationJNI.h"
+#include "hal/DriverStation.h"
+#include "hal/FRCUsageReporting.h"
+#include "hal/HALBase.h"
+
+// TODO Static asserts
+
+using namespace hal;
+using namespace wpi::java;
+
+extern "C" {
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    observeUserProgramStarting
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_observeUserProgramStarting
+  (JNIEnv*, jclass)
+{
+  HAL_ObserveUserProgramStarting();
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    observeUserProgramDisabled
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_observeUserProgramDisabled
+  (JNIEnv*, jclass)
+{
+  HAL_ObserveUserProgramDisabled();
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    observeUserProgramAutonomous
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_observeUserProgramAutonomous
+  (JNIEnv*, jclass)
+{
+  HAL_ObserveUserProgramAutonomous();
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    observeUserProgramTeleop
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_observeUserProgramTeleop
+  (JNIEnv*, jclass)
+{
+  HAL_ObserveUserProgramTeleop();
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    observeUserProgramTest
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_observeUserProgramTest
+  (JNIEnv*, jclass)
+{
+  HAL_ObserveUserProgramTest();
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    report
+ * Signature: (IIILjava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_report
+  (JNIEnv* paramEnv, jclass, jint paramResource, jint paramInstanceNumber,
+   jint paramContext, jstring paramFeature)
+{
+  JStringRef featureStr{paramEnv, paramFeature};
+  jint returnValue = HAL_Report(paramResource, paramInstanceNumber,
+                                paramContext, featureStr.c_str());
+  return returnValue;
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    nativeGetControlWord
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_nativeGetControlWord
+  (JNIEnv*, jclass)
+{
+  static_assert(sizeof(HAL_ControlWord) == sizeof(jint),
+                "Java int must match the size of control word");
+  HAL_ControlWord controlWord;
+  HAL_GetControlWord(&controlWord);
+  jint retVal = 0;
+  std::memcpy(&retVal, &controlWord, sizeof(HAL_ControlWord));
+  return retVal;
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    nativeGetAllianceStation
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_nativeGetAllianceStation
+  (JNIEnv*, jclass)
+{
+  int32_t status = 0;
+  auto allianceStation = HAL_GetAllianceStation(&status);
+  return static_cast<jint>(allianceStation);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    getJoystickAxesRaw
+ * Signature: (B[I)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_getJoystickAxesRaw
+  (JNIEnv* env, jclass, jbyte joystickNum, jintArray axesRawArray)
+{
+  HAL_JoystickAxes axes;
+  HAL_GetJoystickAxes(joystickNum, &axes);
+
+  jsize javaSize = env->GetArrayLength(axesRawArray);
+  if (axes.count > javaSize) {
+    ThrowIllegalArgumentException(
+        env,
+        fmt::format("Native array size larger then passed in java array "
+                    "size\nNative Size: {} Java Size: {}",
+                    static_cast<int>(axes.count), static_cast<int>(javaSize)));
+    return 0;
+  }
+
+  jint raw[HAL_kMaxJoystickAxes];
+  for (int16_t i = 0; i < axes.count; i++) {
+    raw[i] = axes.raw[i];
+  }
+  env->SetIntArrayRegion(axesRawArray, 0, axes.count, raw);
+
+  return axes.count;
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    getJoystickAxes
+ * Signature: (B[F)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_getJoystickAxes
+  (JNIEnv* env, jclass, jbyte joystickNum, jfloatArray axesArray)
+{
+  HAL_JoystickAxes axes;
+  HAL_GetJoystickAxes(joystickNum, &axes);
+
+  jsize javaSize = env->GetArrayLength(axesArray);
+  if (axes.count > javaSize) {
+    ThrowIllegalArgumentException(
+        env,
+        fmt::format("Native array size larger then passed in java array "
+                    "size\nNative Size: {} Java Size: {}",
+                    static_cast<int>(axes.count), static_cast<int>(javaSize)));
+    return 0;
+  }
+
+  env->SetFloatArrayRegion(axesArray, 0, axes.count, axes.axes);
+
+  return axes.count;
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    getJoystickPOVs
+ * Signature: (B[S)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_getJoystickPOVs
+  (JNIEnv* env, jclass, jbyte joystickNum, jshortArray povsArray)
+{
+  HAL_JoystickPOVs povs;
+  HAL_GetJoystickPOVs(joystickNum, &povs);
+
+  jsize javaSize = env->GetArrayLength(povsArray);
+  if (povs.count > javaSize) {
+    ThrowIllegalArgumentException(
+        env,
+        fmt::format("Native array size larger then passed in java array "
+                    "size\nNative Size: {} Java Size: {}",
+                    static_cast<int>(povs.count), static_cast<int>(javaSize)));
+    return 0;
+  }
+
+  env->SetShortArrayRegion(povsArray, 0, povs.count, povs.povs);
+
+  return povs.count;
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    getAllJoystickData
+ * Signature: ([F[B[S[J)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_getAllJoystickData
+  (JNIEnv* env, jclass cls, jfloatArray axesArray, jbyteArray rawAxesArray,
+   jshortArray povsArray, jlongArray buttonsAndMetadataArray)
+{
+  HAL_JoystickAxes axes[HAL_kMaxJoysticks];
+  HAL_JoystickPOVs povs[HAL_kMaxJoysticks];
+  HAL_JoystickButtons buttons[HAL_kMaxJoysticks];
+
+  HAL_GetAllJoystickData(axes, povs, buttons);
+
+  CriticalJFloatArrayRef jAxes(env, axesArray);
+  CriticalJByteArrayRef jRawAxes(env, rawAxesArray);
+  CriticalJShortArrayRef jPovs(env, povsArray);
+  CriticalJLongArrayRef jButtons(env, buttonsAndMetadataArray);
+
+  static_assert(sizeof(jAxes[0]) == sizeof(axes[0].axes[0]));
+  static_assert(sizeof(jRawAxes[0]) == sizeof(axes[0].raw[0]));
+  static_assert(sizeof(jPovs[0]) == sizeof(povs[0].povs[0]));
+
+  for (size_t i = 0; i < HAL_kMaxJoysticks; i++) {
+    std::memcpy(&jAxes[i * HAL_kMaxJoystickAxes], axes[i].axes,
+                sizeof(axes[i].axes));
+    std::memcpy(&jRawAxes[i * HAL_kMaxJoystickAxes], axes[i].raw,
+                sizeof(axes[i].raw));
+    std::memcpy(&jPovs[i * HAL_kMaxJoystickPOVs], povs[i].povs,
+                sizeof(povs[i].povs));
+    jButtons[i * 4] = axes[i].count;
+    jButtons[(i * 4) + 1] = povs[i].count;
+    jButtons[(i * 4) + 2] = buttons[i].count;
+    jButtons[(i * 4) + 3] = buttons[i].buttons;
+  }
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    getJoystickButtons
+ * Signature: (BLjava/lang/Object;)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_getJoystickButtons
+  (JNIEnv* env, jclass, jbyte joystickNum, jobject count)
+{
+  HAL_JoystickButtons joystickButtons;
+  HAL_GetJoystickButtons(joystickNum, &joystickButtons);
+  jbyte* countPtr =
+      reinterpret_cast<jbyte*>(env->GetDirectBufferAddress(count));
+  *countPtr = joystickButtons.count;
+  return joystickButtons.buttons;
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    setJoystickOutputs
+ * Signature: (BISS)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_setJoystickOutputs
+  (JNIEnv*, jclass, jbyte port, jint outputs, jshort leftRumble,
+   jshort rightRumble)
+{
+  return HAL_SetJoystickOutputs(port, outputs, leftRumble, rightRumble);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    getJoystickIsXbox
+ * Signature: (B)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_getJoystickIsXbox
+  (JNIEnv*, jclass, jbyte port)
+{
+  return HAL_GetJoystickIsXbox(port);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    getJoystickType
+ * Signature: (B)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_getJoystickType
+  (JNIEnv*, jclass, jbyte port)
+{
+  return HAL_GetJoystickType(port);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    getJoystickName
+ * Signature: (B)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_getJoystickName
+  (JNIEnv* env, jclass, jbyte port)
+{
+  char* joystickName = HAL_GetJoystickName(port);
+  jstring str = MakeJString(env, joystickName);
+  HAL_FreeJoystickName(joystickName);
+  return str;
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    getJoystickAxisType
+ * Signature: (BB)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_getJoystickAxisType
+  (JNIEnv*, jclass, jbyte joystickNum, jbyte axis)
+{
+  return HAL_GetJoystickAxisType(joystickNum, axis);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    getMatchTime
+ * Signature: ()D
+ */
+JNIEXPORT jdouble JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_getMatchTime
+  (JNIEnv* env, jclass)
+{
+  int32_t status = 0;
+  return HAL_GetMatchTime(&status);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    getMatchInfo
+ * Signature: (Ljava/lang/Object;)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_getMatchInfo
+  (JNIEnv* env, jclass, jobject info)
+{
+  HAL_MatchInfo matchInfo;
+  auto status = HAL_GetMatchInfo(&matchInfo);
+  if (status == 0) {
+    SetMatchInfoObject(env, info, matchInfo);
+  }
+  return status;
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    sendError
+ * Signature: (ZIZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_sendError
+  (JNIEnv* env, jclass, jboolean isError, jint errorCode, jboolean isLVCode,
+   jstring details, jstring location, jstring callStack, jboolean printMsg)
+{
+  JStringRef detailsStr{env, details};
+  JStringRef locationStr{env, location};
+  JStringRef callStackStr{env, callStack};
+
+  jint returnValue =
+      HAL_SendError(isError, errorCode, isLVCode, detailsStr.c_str(),
+                    locationStr.c_str(), callStackStr.c_str(), printMsg);
+  return returnValue;
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    sendConsoleLine
+ * Signature: (Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_sendConsoleLine
+  (JNIEnv* env, jclass, jstring line)
+{
+  JStringRef lineStr{env, line};
+
+  jint returnValue = HAL_SendConsoleLine(lineStr.c_str());
+  return returnValue;
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    refreshDSData
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_refreshDSData
+  (JNIEnv*, jclass)
+{
+  HAL_RefreshDSData();
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    provideNewDataEventHandle
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_provideNewDataEventHandle
+  (JNIEnv*, jclass, jint handle)
+{
+  HAL_ProvideNewDataEventHandle(handle);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    removeNewDataEventHandle
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_removeNewDataEventHandle
+  (JNIEnv*, jclass, jint handle)
+{
+  HAL_RemoveNewDataEventHandle(handle);
+}
+
+/*
+ * Class:     edu_wpi_first_hal_DriverStationJNI
+ * Method:    getOutputsActive
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_edu_wpi_first_hal_DriverStationJNI_getOutputsActive
+  (JNIEnv*, jclass)
+{
+  return HAL_GetOutputsEnabled();
+}
+}  // extern "C"
diff --git a/hal/src/main/native/cpp/jni/DutyCycleJNI.cpp b/hal/src/main/native/cpp/jni/DutyCycleJNI.cpp
index 96cc27b..f83e13c 100644
--- a/hal/src/main/native/cpp/jni/DutyCycleJNI.cpp
+++ b/hal/src/main/native/cpp/jni/DutyCycleJNI.cpp
@@ -74,15 +74,15 @@
 
 /*
  * Class:     edu_wpi_first_hal_DutyCycleJNI
- * Method:    getOutputRaw
+ * Method:    getHighTime
  * Signature: (I)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_first_hal_DutyCycleJNI_getOutputRaw
+Java_edu_wpi_first_hal_DutyCycleJNI_getHighTime
   (JNIEnv* env, jclass, jint handle)
 {
   int32_t status = 0;
-  auto retVal = HAL_GetDutyCycleOutputRaw(
+  auto retVal = HAL_GetDutyCycleHighTime(
       static_cast<HAL_DutyCycleHandle>(handle), &status);
   CheckStatus(env, status);
   return retVal;
diff --git a/hal/src/main/native/cpp/jni/HAL.cpp b/hal/src/main/native/cpp/jni/HAL.cpp
index 4e26032..b603a76 100644
--- a/hal/src/main/native/cpp/jni/HAL.cpp
+++ b/hal/src/main/native/cpp/jni/HAL.cpp
@@ -108,310 +108,6 @@
 
 /*
  * Class:     edu_wpi_first_hal_HAL
- * Method:    observeUserProgramStarting
- * Signature: ()V
- */
-JNIEXPORT void JNICALL
-Java_edu_wpi_first_hal_HAL_observeUserProgramStarting
-  (JNIEnv*, jclass)
-{
-  HAL_ObserveUserProgramStarting();
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
- * Method:    observeUserProgramDisabled
- * Signature: ()V
- */
-JNIEXPORT void JNICALL
-Java_edu_wpi_first_hal_HAL_observeUserProgramDisabled
-  (JNIEnv*, jclass)
-{
-  HAL_ObserveUserProgramDisabled();
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
- * Method:    observeUserProgramAutonomous
- * Signature: ()V
- */
-JNIEXPORT void JNICALL
-Java_edu_wpi_first_hal_HAL_observeUserProgramAutonomous
-  (JNIEnv*, jclass)
-{
-  HAL_ObserveUserProgramAutonomous();
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
- * Method:    observeUserProgramTeleop
- * Signature: ()V
- */
-JNIEXPORT void JNICALL
-Java_edu_wpi_first_hal_HAL_observeUserProgramTeleop
-  (JNIEnv*, jclass)
-{
-  HAL_ObserveUserProgramTeleop();
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
- * Method:    observeUserProgramTest
- * Signature: ()V
- */
-JNIEXPORT void JNICALL
-Java_edu_wpi_first_hal_HAL_observeUserProgramTest
-  (JNIEnv*, jclass)
-{
-  HAL_ObserveUserProgramTest();
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
- * Method:    report
- * Signature: (IIILjava/lang/String;)I
- */
-JNIEXPORT jint JNICALL
-Java_edu_wpi_first_hal_HAL_report
-  (JNIEnv* paramEnv, jclass, jint paramResource, jint paramInstanceNumber,
-   jint paramContext, jstring paramFeature)
-{
-  JStringRef featureStr{paramEnv, paramFeature};
-  jint returnValue = HAL_Report(paramResource, paramInstanceNumber,
-                                paramContext, featureStr.c_str());
-  return returnValue;
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
- * Method:    nativeGetControlWord
- * Signature: ()I
- */
-JNIEXPORT jint JNICALL
-Java_edu_wpi_first_hal_HAL_nativeGetControlWord
-  (JNIEnv*, jclass)
-{
-  static_assert(sizeof(HAL_ControlWord) == sizeof(jint),
-                "Java int must match the size of control word");
-  HAL_ControlWord controlWord;
-  HAL_GetControlWord(&controlWord);
-  jint retVal = 0;
-  std::memcpy(&retVal, &controlWord, sizeof(HAL_ControlWord));
-  return retVal;
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
- * Method:    nativeGetAllianceStation
- * Signature: ()I
- */
-JNIEXPORT jint JNICALL
-Java_edu_wpi_first_hal_HAL_nativeGetAllianceStation
-  (JNIEnv*, jclass)
-{
-  int32_t status = 0;
-  auto allianceStation = HAL_GetAllianceStation(&status);
-  return static_cast<jint>(allianceStation);
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
- * Method:    getJoystickAxes
- * Signature: (B[F)S
- */
-JNIEXPORT jshort JNICALL
-Java_edu_wpi_first_hal_HAL_getJoystickAxes
-  (JNIEnv* env, jclass, jbyte joystickNum, jfloatArray axesArray)
-{
-  HAL_JoystickAxes axes;
-  HAL_GetJoystickAxes(joystickNum, &axes);
-
-  jsize javaSize = env->GetArrayLength(axesArray);
-  if (axes.count > javaSize) {
-    ThrowIllegalArgumentException(
-        env,
-        fmt::format("Native array size larger then passed in java array "
-                    "size\nNative Size: {} Java Size: {}",
-                    static_cast<int>(axes.count), static_cast<int>(javaSize)));
-    return 0;
-  }
-
-  env->SetFloatArrayRegion(axesArray, 0, axes.count, axes.axes);
-
-  return axes.count;
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
- * Method:    getJoystickPOVs
- * Signature: (B[S)S
- */
-JNIEXPORT jshort JNICALL
-Java_edu_wpi_first_hal_HAL_getJoystickPOVs
-  (JNIEnv* env, jclass, jbyte joystickNum, jshortArray povsArray)
-{
-  HAL_JoystickPOVs povs;
-  HAL_GetJoystickPOVs(joystickNum, &povs);
-
-  jsize javaSize = env->GetArrayLength(povsArray);
-  if (povs.count > javaSize) {
-    ThrowIllegalArgumentException(
-        env,
-        fmt::format("Native array size larger then passed in java array "
-                    "size\nNative Size: {} Java Size: {}",
-                    static_cast<int>(povs.count), static_cast<int>(javaSize)));
-    return 0;
-  }
-
-  env->SetShortArrayRegion(povsArray, 0, povs.count, povs.povs);
-
-  return povs.count;
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
- * Method:    getJoystickButtons
- * Signature: (BLjava/lang/Object;)I
- */
-JNIEXPORT jint JNICALL
-Java_edu_wpi_first_hal_HAL_getJoystickButtons
-  (JNIEnv* env, jclass, jbyte joystickNum, jobject count)
-{
-  HAL_JoystickButtons joystickButtons;
-  HAL_GetJoystickButtons(joystickNum, &joystickButtons);
-  jbyte* countPtr =
-      reinterpret_cast<jbyte*>(env->GetDirectBufferAddress(count));
-  *countPtr = joystickButtons.count;
-  return joystickButtons.buttons;
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
- * Method:    setJoystickOutputs
- * Signature: (BISS)I
- */
-JNIEXPORT jint JNICALL
-Java_edu_wpi_first_hal_HAL_setJoystickOutputs
-  (JNIEnv*, jclass, jbyte port, jint outputs, jshort leftRumble,
-   jshort rightRumble)
-{
-  return HAL_SetJoystickOutputs(port, outputs, leftRumble, rightRumble);
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
- * Method:    getJoystickIsXbox
- * Signature: (B)I
- */
-JNIEXPORT jint JNICALL
-Java_edu_wpi_first_hal_HAL_getJoystickIsXbox
-  (JNIEnv*, jclass, jbyte port)
-{
-  return HAL_GetJoystickIsXbox(port);
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
- * Method:    getJoystickType
- * Signature: (B)I
- */
-JNIEXPORT jint JNICALL
-Java_edu_wpi_first_hal_HAL_getJoystickType
-  (JNIEnv*, jclass, jbyte port)
-{
-  return HAL_GetJoystickType(port);
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
- * Method:    getJoystickName
- * Signature: (B)Ljava/lang/String;
- */
-JNIEXPORT jstring JNICALL
-Java_edu_wpi_first_hal_HAL_getJoystickName
-  (JNIEnv* env, jclass, jbyte port)
-{
-  char* joystickName = HAL_GetJoystickName(port);
-  jstring str = MakeJString(env, joystickName);
-  HAL_FreeJoystickName(joystickName);
-  return str;
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
- * Method:    getJoystickAxisType
- * Signature: (BB)I
- */
-JNIEXPORT jint JNICALL
-Java_edu_wpi_first_hal_HAL_getJoystickAxisType
-  (JNIEnv*, jclass, jbyte joystickNum, jbyte axis)
-{
-  return HAL_GetJoystickAxisType(joystickNum, axis);
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
- * Method:    isNewControlData
- * Signature: ()Z
- */
-JNIEXPORT jboolean JNICALL
-Java_edu_wpi_first_hal_HAL_isNewControlData
-  (JNIEnv*, jclass)
-{
-  return static_cast<jboolean>(HAL_IsNewControlData());
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
- * Method:    waitForDSData
- * Signature: ()V
- */
-JNIEXPORT void JNICALL
-Java_edu_wpi_first_hal_HAL_waitForDSData
-  (JNIEnv* env, jclass)
-{
-  HAL_WaitForDSData();
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
- * Method:    releaseDSMutex
- * Signature: ()V
- */
-JNIEXPORT void JNICALL
-Java_edu_wpi_first_hal_HAL_releaseDSMutex
-  (JNIEnv* env, jclass)
-{
-  HAL_ReleaseDSMutex();
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
- * Method:    waitForDSDataTimeout
- * Signature: (D)Z
- */
-JNIEXPORT jboolean JNICALL
-Java_edu_wpi_first_hal_HAL_waitForDSDataTimeout
-  (JNIEnv*, jclass, jdouble timeout)
-{
-  return static_cast<jboolean>(HAL_WaitForDSDataTimeout(timeout));
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
- * Method:    getMatchTime
- * Signature: ()D
- */
-JNIEXPORT jdouble JNICALL
-Java_edu_wpi_first_hal_HAL_getMatchTime
-  (JNIEnv* env, jclass)
-{
-  int32_t status = 0;
-  return HAL_GetMatchTime(&status);
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
  * Method:    getSystemActive
  * Signature: ()Z
  */
@@ -442,58 +138,6 @@
 
 /*
  * Class:     edu_wpi_first_hal_HAL
- * Method:    getMatchInfo
- * Signature: (Ljava/lang/Object;)I
- */
-JNIEXPORT jint JNICALL
-Java_edu_wpi_first_hal_HAL_getMatchInfo
-  (JNIEnv* env, jclass, jobject info)
-{
-  HAL_MatchInfo matchInfo;
-  auto status = HAL_GetMatchInfo(&matchInfo);
-  if (status == 0) {
-    SetMatchInfoObject(env, info, matchInfo);
-  }
-  return status;
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
- * Method:    sendError
- * Signature: (ZIZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)I
- */
-JNIEXPORT jint JNICALL
-Java_edu_wpi_first_hal_HAL_sendError
-  (JNIEnv* env, jclass, jboolean isError, jint errorCode, jboolean isLVCode,
-   jstring details, jstring location, jstring callStack, jboolean printMsg)
-{
-  JStringRef detailsStr{env, details};
-  JStringRef locationStr{env, location};
-  JStringRef callStackStr{env, callStack};
-
-  jint returnValue =
-      HAL_SendError(isError, errorCode, isLVCode, detailsStr.c_str(),
-                    locationStr.c_str(), callStackStr.c_str(), printMsg);
-  return returnValue;
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
- * Method:    sendConsoleLine
- * Signature: (Ljava/lang/String;)I
- */
-JNIEXPORT jint JNICALL
-Java_edu_wpi_first_hal_HAL_sendConsoleLine
-  (JNIEnv* env, jclass, jstring line)
-{
-  JStringRef lineStr{env, line};
-
-  jint returnValue = HAL_SendConsoleLine(lineStr.c_str());
-  return returnValue;
-}
-
-/*
- * Class:     edu_wpi_first_hal_HAL
  * Method:    getPortWithModule
  * Signature: (BB)I
  */
diff --git a/hal/src/main/native/cpp/jni/HALUtil.cpp b/hal/src/main/native/cpp/jni/HALUtil.cpp
index 5d5a958..76c05f8 100644
--- a/hal/src/main/native/cpp/jni/HALUtil.cpp
+++ b/hal/src/main/native/cpp/jni/HALUtil.cpp
@@ -52,6 +52,7 @@
 static JClass matchInfoDataCls;
 static JClass accumulatorResultCls;
 static JClass canDataCls;
+static JClass canStreamMessageCls;
 static JClass halValueCls;
 static JClass baseStoreCls;
 static JClass revPHVersionCls;
@@ -64,6 +65,7 @@
     {"edu/wpi/first/hal/MatchInfoData", &matchInfoDataCls},
     {"edu/wpi/first/hal/AccumulatorResult", &accumulatorResultCls},
     {"edu/wpi/first/hal/CANData", &canDataCls},
+    {"edu/wpi/first/hal/CANStreamMessage", &canStreamMessageCls},
     {"edu/wpi/first/hal/HALValue", &halValueCls},
     {"edu/wpi/first/hal/DMAJNISample$BaseStore", &baseStoreCls},
     {"edu/wpi/first/hal/REVPHVersion", &revPHVersionCls}};
@@ -303,6 +305,18 @@
   return retVal;
 }
 
+jbyteArray SetCANStreamObject(JNIEnv* env, jobject canStreamData,
+                              int32_t length, uint32_t messageID,
+                              uint64_t timestamp) {
+  static jmethodID func =
+      env->GetMethodID(canStreamMessageCls, "setStreamData", "(IIJ)[B");
+
+  jbyteArray retVal = static_cast<jbyteArray>(env->CallObjectMethod(
+      canStreamData, func, static_cast<jint>(length),
+      static_cast<jint>(messageID), static_cast<jlong>(timestamp)));
+  return retVal;
+}
+
 jobject CreateHALValue(JNIEnv* env, const HAL_Value& value) {
   static jmethodID fromNative = env->GetStaticMethodID(
       halValueCls, "fromNative", "(IJD)Ledu/wpi/first/hal/HALValue;");
@@ -444,6 +458,34 @@
 
 /*
  * Class:     edu_wpi_first_hal_HALUtil
+ * Method:    getSerialNumber
+ * Signature: ()Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL
+Java_edu_wpi_first_hal_HALUtil_getSerialNumber
+  (JNIEnv* env, jclass)
+{
+  char serialNum[9];
+  size_t len = HAL_GetSerialNumber(serialNum, sizeof(serialNum));
+  return MakeJString(env, std::string_view(serialNum, len));
+}
+
+/*
+ * Class:     edu_wpi_first_hal_HALUtil
+ * Method:    getComments
+ * Signature: ()Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL
+Java_edu_wpi_first_hal_HALUtil_getComments
+  (JNIEnv* env, jclass)
+{
+  char comments[65];
+  size_t len = HAL_GetComments(comments, sizeof(comments));
+  return MakeJString(env, std::string_view(comments, len));
+}
+
+/*
+ * Class:     edu_wpi_first_hal_HALUtil
  * Method:    getFPGATime
  * Signature: ()J
  */
diff --git a/hal/src/main/native/cpp/jni/HALUtil.h b/hal/src/main/native/cpp/jni/HALUtil.h
index cf3956c..9c9487c 100644
--- a/hal/src/main/native/cpp/jni/HALUtil.h
+++ b/hal/src/main/native/cpp/jni/HALUtil.h
@@ -78,6 +78,10 @@
 jbyteArray SetCANDataObject(JNIEnv* env, jobject canData, int32_t length,
                             uint64_t timestamp);
 
+jbyteArray SetCANStreamObject(JNIEnv* env, jobject canStreamData,
+                              int32_t length, uint32_t messageID,
+                              uint64_t timestamp);
+
 jobject CreateHALValue(JNIEnv* env, const HAL_Value& value);
 
 jobject CreateDMABaseStore(JNIEnv* env, jint valueType, jint index);
diff --git a/hal/src/main/native/cpp/jni/I2CJNI.cpp b/hal/src/main/native/cpp/jni/I2CJNI.cpp
index b605b95..68b8442 100644
--- a/hal/src/main/native/cpp/jni/I2CJNI.cpp
+++ b/hal/src/main/native/cpp/jni/I2CJNI.cpp
@@ -64,6 +64,16 @@
   (JNIEnv* env, jclass, jint port, jbyte address, jbyteArray dataToSend,
    jbyte sendSize, jbyteArray dataReceived, jbyte receiveSize)
 {
+  if (sendSize < 0) {
+    ThrowIllegalArgumentException(env, "I2CJNI.i2cTransactionB() sendSize < 0");
+    return 0;
+  }
+  if (receiveSize < 0) {
+    ThrowIllegalArgumentException(env,
+                                  "I2CJNI.i2cTransactionB() receiveSize < 0");
+    return 0;
+  }
+
   wpi::SmallVector<uint8_t, 128> recvBuf;
   recvBuf.resize(receiveSize);
   jint returnValue =
@@ -142,6 +152,11 @@
   (JNIEnv* env, jclass, jint port, jbyte address, jbyteArray dataReceived,
    jbyte receiveSize)
 {
+  if (receiveSize < 0) {
+    ThrowIllegalArgumentException(env, "I2CJNI.i2cReadB() receiveSize < 0");
+    return 0;
+  }
+
   wpi::SmallVector<uint8_t, 128> recvBuf;
   recvBuf.resize(receiveSize);
   jint returnValue = HAL_ReadI2C(static_cast<HAL_I2CPort>(port), address,
diff --git a/hal/src/main/native/cpp/jni/InterruptJNI.cpp b/hal/src/main/native/cpp/jni/InterruptJNI.cpp
index ed56ce5..e220125 100644
--- a/hal/src/main/native/cpp/jni/InterruptJNI.cpp
+++ b/hal/src/main/native/cpp/jni/InterruptJNI.cpp
@@ -50,15 +50,15 @@
 /*
  * Class:     edu_wpi_first_hal_InterruptJNI
  * Method:    waitForInterrupt
- * Signature: (IDZ)I
+ * Signature: (IDZ)J
  */
-JNIEXPORT jint JNICALL
+JNIEXPORT jlong JNICALL
 Java_edu_wpi_first_hal_InterruptJNI_waitForInterrupt
   (JNIEnv* env, jclass, jint interruptHandle, jdouble timeout,
    jboolean ignorePrevious)
 {
   int32_t status = 0;
-  int32_t result = HAL_WaitForInterrupt((HAL_InterruptHandle)interruptHandle,
+  int64_t result = HAL_WaitForInterrupt((HAL_InterruptHandle)interruptHandle,
                                         timeout, ignorePrevious, &status);
 
   CheckStatus(env, status);
@@ -67,6 +67,25 @@
 
 /*
  * Class:     edu_wpi_first_hal_InterruptJNI
+ * Method:    waitForMultipleInterrupts
+ * Signature: (IJDZ)J
+ */
+JNIEXPORT jlong JNICALL
+Java_edu_wpi_first_hal_InterruptJNI_waitForMultipleInterrupts
+  (JNIEnv* env, jclass, jint interruptHandle, jlong mask, jdouble timeout,
+   jboolean ignorePrevious)
+{
+  int32_t status = 0;
+  int64_t result =
+      HAL_WaitForMultipleInterrupts((HAL_InterruptHandle)interruptHandle, mask,
+                                    timeout, ignorePrevious, &status);
+
+  CheckStatus(env, status);
+  return result;
+}
+
+/*
+ * Class:     edu_wpi_first_hal_InterruptJNI
  * Method:    readInterruptRisingTimestamp
  * Signature: (I)J
  */
diff --git a/hal/src/main/native/cpp/jni/SPIJNI.cpp b/hal/src/main/native/cpp/jni/SPIJNI.cpp
index 67ef56d..4f1e556 100644
--- a/hal/src/main/native/cpp/jni/SPIJNI.cpp
+++ b/hal/src/main/native/cpp/jni/SPIJNI.cpp
@@ -15,6 +15,27 @@
 using namespace hal;
 using namespace wpi::java;
 
+static_assert(HAL_SPIPort::HAL_SPI_kInvalid ==
+              edu_wpi_first_hal_SPIJNI_INVALID_PORT);
+static_assert(HAL_SPIPort::HAL_SPI_kOnboardCS0 ==
+              edu_wpi_first_hal_SPIJNI_ONBOARD_CS0_PORT);
+static_assert(HAL_SPIPort::HAL_SPI_kOnboardCS1 ==
+              edu_wpi_first_hal_SPIJNI_ONBOARD_CS1_PORT);
+static_assert(HAL_SPIPort::HAL_SPI_kOnboardCS2 ==
+              edu_wpi_first_hal_SPIJNI_ONBOARD_CS2_PORT);
+static_assert(HAL_SPIPort::HAL_SPI_kOnboardCS3 ==
+              edu_wpi_first_hal_SPIJNI_ONBOARD_CS3_PORT);
+static_assert(HAL_SPIPort::HAL_SPI_kMXP == edu_wpi_first_hal_SPIJNI_MXP_PORT);
+
+static_assert(HAL_SPIMode::HAL_SPI_kMode0 ==
+              edu_wpi_first_hal_SPIJNI_SPI_MODE0);
+static_assert(HAL_SPIMode::HAL_SPI_kMode1 ==
+              edu_wpi_first_hal_SPIJNI_SPI_MODE1);
+static_assert(HAL_SPIMode::HAL_SPI_kMode2 ==
+              edu_wpi_first_hal_SPIJNI_SPI_MODE2);
+static_assert(HAL_SPIMode::HAL_SPI_kMode3 ==
+              edu_wpi_first_hal_SPIJNI_SPI_MODE3);
+
 extern "C" {
 
 /*
@@ -63,6 +84,11 @@
   (JNIEnv* env, jclass, jint port, jbyteArray dataToSend,
    jbyteArray dataReceived, jbyte size)
 {
+  if (size < 0) {
+    ThrowIllegalArgumentException(env, "SPIJNI.spiTransactionB() size < 0");
+    return 0;
+  }
+
   wpi::SmallVector<uint8_t, 128> recvBuf;
   recvBuf.resize(size);
   jint retVal =
@@ -120,6 +146,11 @@
   (JNIEnv* env, jclass, jint port, jboolean initiate, jobject dataReceived,
    jbyte size)
 {
+  if (size < 0) {
+    ThrowIllegalArgumentException(env, "SPIJNI.spiRead() size < 0");
+    return 0;
+  }
+
   uint8_t* dataReceivedPtr =
       reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(dataReceived));
   jint retVal;
@@ -145,6 +176,11 @@
   (JNIEnv* env, jclass, jint port, jboolean initiate, jbyteArray dataReceived,
    jbyte size)
 {
+  if (size < 0) {
+    ThrowIllegalArgumentException(env, "SPIJNI.spiReadB() size < 0");
+    return 0;
+  }
+
   jint retVal;
   wpi::SmallVector<uint8_t, 128> recvBuf;
   recvBuf.resize(size);
@@ -187,16 +223,27 @@
 
 /*
  * Class:     edu_wpi_first_hal_SPIJNI
- * Method:    spiSetOpts
- * Signature: (IIII)V
+ * Method:    spiSetMode
+ * Signature: (II)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_first_hal_SPIJNI_spiSetOpts
-  (JNIEnv*, jclass, jint port, jint msb_first, jint sample_on_trailing,
-   jint clk_idle_high)
+Java_edu_wpi_first_hal_SPIJNI_spiSetMode
+  (JNIEnv*, jclass, jint port, jint mode)
 {
-  HAL_SetSPIOpts(static_cast<HAL_SPIPort>(port), msb_first, sample_on_trailing,
-                 clk_idle_high);
+  HAL_SetSPIMode(static_cast<HAL_SPIPort>(port),
+                 static_cast<HAL_SPIMode>(mode));
+}
+
+/*
+ * Class:     edu_wpi_first_hal_SPIJNI
+ * Method:    spiGetMode
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_SPIJNI_spiGetMode
+  (JNIEnv*, jclass, jint port)
+{
+  return static_cast<jint>(HAL_GetSPIMode(static_cast<HAL_SPIPort>(port)));
 }
 
 /*
@@ -361,6 +408,12 @@
   (JNIEnv* env, jclass, jint port, jintArray buffer, jint numToRead,
    jdouble timeout)
 {
+  if (numToRead < 0) {
+    ThrowIllegalArgumentException(
+        env, "SPIJNI.spiReadAutoReceivedData() numToRead < 0");
+    return 0;
+  }
+
   wpi::SmallVector<uint32_t, 128> recvBuf;
   recvBuf.resize(numToRead);
   int32_t status = 0;
diff --git a/hal/src/main/native/cpp/jni/simulation/AddressableLEDDataJNI.cpp b/hal/src/main/native/cpp/jni/simulation/AddressableLEDDataJNI.cpp
index e888bd3..70b31ab 100644
--- a/hal/src/main/native/cpp/jni/simulation/AddressableLEDDataJNI.cpp
+++ b/hal/src/main/native/cpp/jni/simulation/AddressableLEDDataJNI.cpp
@@ -257,7 +257,7 @@
       std::make_unique<HAL_AddressableLEDData[]>(HAL_kAddressableLEDMaxLength);
   int32_t length = HALSIM_GetAddressableLEDData(index, data.get());
   return MakeJByteArray(
-      env, wpi::span(reinterpret_cast<jbyte*>(data.get()), length * 4));
+      env, std::span(reinterpret_cast<jbyte*>(data.get()), length * 4));
 }
 
 /*
diff --git a/hal/src/main/native/cpp/jni/simulation/BufferCallbackStore.cpp b/hal/src/main/native/cpp/jni/simulation/BufferCallbackStore.cpp
index 265b363..f83ab62 100644
--- a/hal/src/main/native/cpp/jni/simulation/BufferCallbackStore.cpp
+++ b/hal/src/main/native/cpp/jni/simulation/BufferCallbackStore.cpp
@@ -57,9 +57,8 @@
     std::fflush(stdout);
   }
 
-  auto toCallbackArr = MakeJByteArray(
-      env, std::string_view{reinterpret_cast<const char*>(buffer),
-                            static_cast<size_t>(length)});
+  auto toCallbackArr =
+      MakeJByteArray(env, {buffer, static_cast<size_t>(length)});
 
   env->CallVoidMethod(m_call, sim::GetBufferCallback(), MakeJString(env, name),
                       toCallbackArr, static_cast<jint>(length));
@@ -124,6 +123,9 @@
 void sim::FreeBufferCallback(JNIEnv* env, SIM_JniHandle handle, jint index,
                              FreeBufferCallbackFunc freeCallback) {
   auto callback = callbackHandles->Free(handle);
+  if (callback == nullptr) {
+    return;
+  }
   freeCallback(index, callback->getCallbackId());
   callback->free(env);
 }
diff --git a/hal/src/main/native/cpp/jni/simulation/ConstBufferCallbackStore.cpp b/hal/src/main/native/cpp/jni/simulation/ConstBufferCallbackStore.cpp
index cba0a5b..af57803 100644
--- a/hal/src/main/native/cpp/jni/simulation/ConstBufferCallbackStore.cpp
+++ b/hal/src/main/native/cpp/jni/simulation/ConstBufferCallbackStore.cpp
@@ -58,9 +58,8 @@
     std::fflush(stdout);
   }
 
-  auto toCallbackArr = MakeJByteArray(
-      env, std::string_view{reinterpret_cast<const char*>(buffer),
-                            static_cast<size_t>(length)});
+  auto toCallbackArr =
+      MakeJByteArray(env, {buffer, static_cast<size_t>(length)});
 
   env->CallVoidMethod(m_call, sim::GetConstBufferCallback(),
                       MakeJString(env, name), toCallbackArr,
@@ -117,6 +116,9 @@
 void sim::FreeConstBufferCallback(JNIEnv* env, SIM_JniHandle handle, jint index,
                                   FreeConstBufferCallbackFunc freeCallback) {
   auto callback = callbackHandles->Free(handle);
+  if (callback == nullptr) {
+    return;
+  }
   freeCallback(index, callback->getCallbackId());
   callback->free(env);
 }
diff --git a/hal/src/main/native/cpp/jni/simulation/DriverStationDataJNI.cpp b/hal/src/main/native/cpp/jni/simulation/DriverStationDataJNI.cpp
index acecacb..c50b8e3 100644
--- a/hal/src/main/native/cpp/jni/simulation/DriverStationDataJNI.cpp
+++ b/hal/src/main/native/cpp/jni/simulation/DriverStationDataJNI.cpp
@@ -732,7 +732,8 @@
 Java_edu_wpi_first_hal_simulation_DriverStationDataJNI_setJoystickName
   (JNIEnv* env, jclass, jint stick, jstring name)
 {
-  HALSIM_SetJoystickName(stick, JStringRef{env, name}.c_str());
+  JStringRef nameJString{env, name};
+  HALSIM_SetJoystickName(stick, nameJString.c_str(), nameJString.size());
 }
 
 /*
@@ -756,7 +757,8 @@
 Java_edu_wpi_first_hal_simulation_DriverStationDataJNI_setGameSpecificMessage
   (JNIEnv* env, jclass, jstring message)
 {
-  HALSIM_SetGameSpecificMessage(JStringRef{env, message}.c_str());
+  JStringRef messageJString{env, message};
+  HALSIM_SetGameSpecificMessage(messageJString.c_str(), messageJString.size());
 }
 
 /*
@@ -768,7 +770,8 @@
 Java_edu_wpi_first_hal_simulation_DriverStationDataJNI_setEventName
   (JNIEnv* env, jclass, jstring name)
 {
-  HALSIM_SetEventName(JStringRef{env, name}.c_str());
+  JStringRef nameJString{env, name};
+  HALSIM_SetEventName(nameJString.c_str(), nameJString.size());
 }
 
 /*
diff --git a/hal/src/main/native/cpp/jni/simulation/RoboRioDataJNI.cpp b/hal/src/main/native/cpp/jni/simulation/RoboRioDataJNI.cpp
index 03bb0c5..5bcd114 100644
--- a/hal/src/main/native/cpp/jni/simulation/RoboRioDataJNI.cpp
+++ b/hal/src/main/native/cpp/jni/simulation/RoboRioDataJNI.cpp
@@ -4,11 +4,14 @@
 
 #include <jni.h>
 
+#include <wpi/jni_util.h>
+
 #include "CallbackStore.h"
 #include "edu_wpi_first_hal_simulation_RoboRioDataJNI.h"
 #include "hal/simulation/RoboRioData.h"
 
 using namespace hal;
+using namespace wpi::java;
 
 extern "C" {
 
@@ -827,6 +830,61 @@
 
 /*
  * Class:     edu_wpi_first_hal_simulation_RoboRioDataJNI
+ * Method:    getSerialNumber
+ * Signature: ()Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL
+Java_edu_wpi_first_hal_simulation_RoboRioDataJNI_getSerialNumber
+  (JNIEnv* env, jclass)
+{
+  char serialNum[9];
+  size_t len = HALSIM_GetRoboRioSerialNumber(serialNum, sizeof(serialNum));
+  return MakeJString(env, std::string_view(serialNum, len));
+}
+
+/*
+ * Class:     edu_wpi_first_hal_simulation_RoboRioDataJNI
+ * Method:    setSerialNumber
+ * Signature: (Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_simulation_RoboRioDataJNI_setSerialNumber
+  (JNIEnv* env, jclass, jstring serialNumber)
+{
+  JStringRef serialNumberJString{env, serialNumber};
+  HALSIM_SetRoboRioSerialNumber(serialNumberJString.c_str(),
+                                serialNumberJString.size());
+}
+
+/*
+ * Class:     edu_wpi_first_hal_simulation_RoboRioDataJNI
+ * Method:    getComments
+ * Signature: ()Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL
+Java_edu_wpi_first_hal_simulation_RoboRioDataJNI_getComments
+  (JNIEnv* env, jclass)
+{
+  char comments[65];
+  size_t len = HALSIM_GetRoboRioComments(comments, sizeof(comments));
+  return MakeJString(env, std::string_view(comments, len));
+}
+
+/*
+ * Class:     edu_wpi_first_hal_simulation_RoboRioDataJNI
+ * Method:    setComments
+ * Signature: (Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_simulation_RoboRioDataJNI_setComments
+  (JNIEnv* env, jclass, jstring comments)
+{
+  JStringRef commentsJString{env, comments};
+  HALSIM_SetRoboRioComments(commentsJString.c_str(), commentsJString.size());
+}
+
+/*
+ * Class:     edu_wpi_first_hal_simulation_RoboRioDataJNI
  * Method:    resetData
  * Signature: ()V
  */
diff --git a/hal/src/main/native/cpp/jni/simulation/SimDeviceDataJNI.cpp b/hal/src/main/native/cpp/jni/simulation/SimDeviceDataJNI.cpp
index 60ce0f7..c7c2a19 100644
--- a/hal/src/main/native/cpp/jni/simulation/SimDeviceDataJNI.cpp
+++ b/hal/src/main/native/cpp/jni/simulation/SimDeviceDataJNI.cpp
@@ -242,6 +242,9 @@
 static void FreeDeviceCallback(JNIEnv* env, SIM_JniHandle handle,
                                FreeDeviceCallbackFunc freeCallback) {
   auto callback = deviceCallbackHandles->Free(handle);
+  if (callback == nullptr) {
+    return;
+  }
   freeCallback(callback->getCallbackId());
   callback->free(env);
 }
@@ -296,6 +299,9 @@
 static void FreeValueCallback(JNIEnv* env, SIM_JniHandle handle,
                               FreeValueCallbackFunc freeCallback) {
   auto callback = valueCallbackHandles->Free(handle);
+  if (callback == nullptr) {
+    return;
+  }
   freeCallback(callback->getCallbackId());
   callback->free(env);
 }
@@ -668,7 +674,7 @@
 {
   int32_t numElems = 0;
   const double* elems = HALSIM_GetSimValueEnumDoubleValues(handle, &numElems);
-  return MakeJDoubleArray(env, wpi::span(elems, numElems));
+  return MakeJDoubleArray(env, std::span(elems, numElems));
 }
 
 /*
diff --git a/hal/src/main/native/cpp/jni/simulation/SpiReadAutoReceiveBufferCallbackStore.cpp b/hal/src/main/native/cpp/jni/simulation/SpiReadAutoReceiveBufferCallbackStore.cpp
index c20f607..5b2ff2b 100644
--- a/hal/src/main/native/cpp/jni/simulation/SpiReadAutoReceiveBufferCallbackStore.cpp
+++ b/hal/src/main/native/cpp/jni/simulation/SpiReadAutoReceiveBufferCallbackStore.cpp
@@ -59,7 +59,7 @@
   }
 
   auto toCallbackArr = MakeJIntArray(
-      env, wpi::span<const uint32_t>{buffer, static_cast<size_t>(numToRead)});
+      env, std::span<const uint32_t>{buffer, static_cast<size_t>(numToRead)});
 
   jint ret = env->CallIntMethod(m_call, sim::GetBufferCallback(),
                                 MakeJString(env, name), toCallbackArr,
@@ -127,6 +127,9 @@
 void sim::FreeSpiBufferCallback(JNIEnv* env, SIM_JniHandle handle, jint index,
                                 FreeSpiBufferCallbackFunc freeCallback) {
   auto callback = callbackHandles->Free(handle);
+  if (callback == nullptr) {
+    return;
+  }
   freeCallback(index, callback->getCallbackId());
   callback->free(env);
 }
diff --git a/hal/src/main/native/include/hal/AnalogGyro.h b/hal/src/main/native/include/hal/AnalogGyro.h
index 6e5a9c2..ed7c308 100644
--- a/hal/src/main/native/include/hal/AnalogGyro.h
+++ b/hal/src/main/native/include/hal/AnalogGyro.h
@@ -22,7 +22,7 @@
  * Initializes an analog gyro.
  *
  * @param[in] handle handle to the analog port
- * @param[in] allocationLocation the location where the allocation is occuring
+ * @param[in] allocationLocation the location where the allocation is occurring
  *                                (can be null)
  * @param[out] status the error code, or 0 for success
  * @return the initialized gyro handle
diff --git a/hal/src/main/native/include/hal/AnalogInput.h b/hal/src/main/native/include/hal/AnalogInput.h
index 956cd21..0a1a3c5 100644
--- a/hal/src/main/native/include/hal/AnalogInput.h
+++ b/hal/src/main/native/include/hal/AnalogInput.h
@@ -22,7 +22,7 @@
  * Initializes the analog input port using the given port object.
  *
  * @param[in] portHandle Handle to the port to initialize.
- * @param[in] allocationLocation the location where the allocation is occuring
+ * @param[in] allocationLocation the location where the allocation is occurring
  *                               (can be null)
  * @param[out] status the error code, or 0 for success
  * @return the created analog input handle
@@ -46,7 +46,7 @@
 HAL_Bool HAL_CheckAnalogModule(int32_t module);
 
 /**
- * Checks that the analog output channel number is value.
+ * Checks that the analog output channel number is valid.
  * Verifies that the analog channel number is one of the legal channel numbers.
  * Channel numbers are 0-based.
  *
diff --git a/hal/src/main/native/include/hal/AnalogOutput.h b/hal/src/main/native/include/hal/AnalogOutput.h
index 26e5231..f9a1f36 100644
--- a/hal/src/main/native/include/hal/AnalogOutput.h
+++ b/hal/src/main/native/include/hal/AnalogOutput.h
@@ -22,7 +22,7 @@
  * Initializes the analog output port using the given port object.
  *
  * @param[in] portHandle handle to the port
- * @param[in] allocationLocation the location where the allocation is occuring
+ * @param[in] allocationLocation the location where the allocation is occurring
  *                               (can be null)
  * @param[out] status Error status variable. 0 on success.
  * @return the created analog output handle
@@ -58,7 +58,7 @@
                            int32_t* status);
 
 /**
- * Checks that the analog output channel number is value.
+ * Checks that the analog output channel number is valid.
  *
  * Verifies that the analog channel number is one of the legal channel numbers.
  * Channel numbers are 0-based.
diff --git a/hal/src/main/native/include/hal/CANAPI.h b/hal/src/main/native/include/hal/CANAPI.h
index 29859cf..d5244b5 100644
--- a/hal/src/main/native/include/hal/CANAPI.h
+++ b/hal/src/main/native/include/hal/CANAPI.h
@@ -22,7 +22,8 @@
 /**
  * Initializes a CAN device.
  *
- * These follow the FIRST standard CAN layout. Link TBD
+ * These follow the FIRST standard CAN layout.
+ * https://docs.wpilib.org/en/stable/docs/software/can-devices/can-addressing.html
  *
  * @param[in] manufacturer the can manufacturer
  * @param[in] deviceId     the device ID (0-63)
diff --git a/hal/src/main/native/include/hal/CANAPITypes.h b/hal/src/main/native/include/hal/CANAPITypes.h
index 1be672e..404eecf 100644
--- a/hal/src/main/native/include/hal/CANAPITypes.h
+++ b/hal/src/main/native/include/hal/CANAPITypes.h
@@ -54,7 +54,8 @@
   HAL_CAN_Man_kKauaiLabs = 9,
   HAL_CAN_Man_kCopperforge = 10,
   HAL_CAN_Man_kPWF = 11,
-  HAL_CAN_Man_kStudica = 12
+  HAL_CAN_Man_kStudica = 12,
+  HAL_CAN_Man_kTheThriftyBot = 13
 };
 // clang-format on
 /** @} */
diff --git a/hal/src/main/native/include/hal/ChipObject.h b/hal/src/main/native/include/hal/ChipObject.h
index 4526a1a..23dcb38 100644
--- a/hal/src/main/native/include/hal/ChipObject.h
+++ b/hal/src/main/native/include/hal/ChipObject.h
@@ -34,7 +34,6 @@
 #include <FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tSysWatchdog.h>
 #include <FRC_FPGA_ChipObject/tDMAChannelDescriptor.h>
 #include <FRC_FPGA_ChipObject/tDMAManager.h>
-#include <FRC_FPGA_ChipObject/tInterruptManager.h>
 #include <FRC_FPGA_ChipObject/tSystem.h>
 #include <FRC_FPGA_ChipObject/tSystemInterface.h>
 
diff --git a/hal/src/main/native/include/hal/DIO.h b/hal/src/main/native/include/hal/DIO.h
index e094a0d..6b922b5 100644
--- a/hal/src/main/native/include/hal/DIO.h
+++ b/hal/src/main/native/include/hal/DIO.h
@@ -23,7 +23,7 @@
  *
  * @param[in] portHandle         the port handle to create from
  * @param[in] input              true for input, false for output
- * @param[in] allocationLocation the location where the allocation is occuring
+ * @param[in] allocationLocation the location where the allocation is occurring
  *                               (can be null)
  * @param[out] status            Error status variable. 0 on success.
  * @return the created digital handle
@@ -95,6 +95,16 @@
                                 double dutyCycle, int32_t* status);
 
 /**
+ * Configures the digital PWM to be a PPS signal with specified duty cycle.
+ *
+ * @param[in] pwmGenerator the digital PWM handle
+ * @param[in] dutyCycle    the percent duty cycle to output [0..1]
+ * @param[out] status      Error status variable. 0 on success.
+ */
+void HAL_SetDigitalPWMPPS(HAL_DigitalPWMHandle pwmGenerator, double dutyCycle,
+                          int32_t* status);
+
+/**
  * Configures which DO channel the PWM signal is output on.
  *
  * @param[in] pwmGenerator the digital PWM handle
@@ -150,13 +160,26 @@
  * single pulse going at any time.
  *
  * @param[in] dioPortHandle the digital port handle
- * @param[in] pulseLength   the active length of the pulse (in seconds)
+ * @param[in] pulseLengthSeconds   the active length of the pulse (in seconds)
  * @param[out] status       Error status variable. 0 on success.
  */
-void HAL_Pulse(HAL_DigitalHandle dioPortHandle, double pulseLength,
+void HAL_Pulse(HAL_DigitalHandle dioPortHandle, double pulseLengthSeconds,
                int32_t* status);
 
 /**
+ * Generates a single digital pulse on multiple channels.
+ *
+ * Write a pulse to the channels enabled by the mask. There can only be a
+ * single pulse going at any time.
+ *
+ * @param[in] channelMask the channel mask
+ * @param[in] pulseLengthSeconds   the active length of the pulse (in seconds)
+ * @param[out] status       Error status variable. 0 on success.
+ */
+void HAL_PulseMultiple(uint32_t channelMask, double pulseLengthSeconds,
+                       int32_t* status);
+
+/**
  * Checks a DIO line to see if it is currently generating a pulse.
  *
  * @param[in] dioPortHandle the digital port handle
diff --git a/hal/src/main/native/include/hal/DriverStation.h b/hal/src/main/native/include/hal/DriverStation.h
index 1839cfc..ae68b65 100644
--- a/hal/src/main/native/include/hal/DriverStation.h
+++ b/hal/src/main/native/include/hal/DriverStation.h
@@ -6,6 +6,8 @@
 
 #include <stdint.h>
 
+#include <wpi/Synchronization.h>
+
 #include "hal/DriverStationTypes.h"
 #include "hal/Types.h"
 
@@ -34,6 +36,14 @@
 int32_t HAL_SendError(HAL_Bool isError, int32_t errorCode, HAL_Bool isLVCode,
                       const char* details, const char* location,
                       const char* callStack, HAL_Bool printMsg);
+
+/**
+ * Set the print function used by HAL_SendError
+ *
+ * @param func Function called by HAL_SendError when stderr is printed
+ */
+void HAL_SetPrintErrorImpl(void (*func)(const char* line, size_t size));
+
 /**
  * Sends a line to the driver station console.
  *
@@ -87,6 +97,9 @@
 int32_t HAL_GetJoystickButtons(int32_t joystickNum,
                                HAL_JoystickButtons* buttons);
 
+void HAL_GetAllJoystickData(HAL_JoystickAxes* axes, HAL_JoystickPOVs* povs,
+                            HAL_JoystickButtons* buttons);
+
 /**
  * Retrieves the Joystick Descriptor for particular slot.
  *
@@ -184,6 +197,11 @@
 double HAL_GetMatchTime(int32_t* status);
 
 /**
+ * Gets if outputs are enabled by the control system.
+ */
+HAL_Bool HAL_GetOutputsEnabled(void);
+
+/**
  * Gets info about a specific match.
  *
  * @param[in] info the match info (output)
@@ -191,44 +209,10 @@
  */
 int32_t HAL_GetMatchInfo(HAL_MatchInfo* info);
 
-/**
- * Releases the DS Mutex to allow proper shutdown of any threads that are
- * waiting on it.
- */
-void HAL_ReleaseDSMutex(void);
+void HAL_RefreshDSData(void);
 
-/**
- * Has a new control packet from the driver station arrived since the last
- * time this function was called?
- *
- * @return true if the control data has been updated since the last call
- */
-HAL_Bool HAL_IsNewControlData(void);
-
-/**
- * Waits for the newest DS packet to arrive. Note that this is a blocking call.
- * Checks if new control data has arrived since the last HAL_WaitForDSData or
- * HAL_IsNewControlData call. If new data has not arrived, waits for new data
- * to arrive. Otherwise, returns immediately.
- */
-void HAL_WaitForDSData(void);
-
-/**
- * Waits for the newest DS packet to arrive. If timeout is <= 0, this will wait
- * forever. Otherwise, it will wait until either a new packet, or the timeout
- * time has passed.
- *
- * @param[in] timeout timeout in seconds
- * @return true for new data, false for timeout
- */
-HAL_Bool HAL_WaitForDSDataTimeout(double timeout);
-
-/**
- * Initializes the driver station communication. This will properly
- * handle multiple calls. However note that this CANNOT be called from a library
- * that interfaces with LabVIEW.
- */
-void HAL_InitializeDriverStation(void);
+void HAL_ProvideNewDataEventHandle(WPI_EventHandle handle);
+void HAL_RemoveNewDataEventHandle(WPI_EventHandle handle);
 
 /**
  * Sets the program starting flag in the DS.
diff --git a/hal/src/main/native/include/hal/DriverStationTypes.h b/hal/src/main/native/include/hal/DriverStationTypes.h
index 21f9088..277c286 100644
--- a/hal/src/main/native/include/hal/DriverStationTypes.h
+++ b/hal/src/main/native/include/hal/DriverStationTypes.h
@@ -69,6 +69,7 @@
 struct HAL_JoystickAxes {
   int16_t count;
   float axes[HAL_kMaxJoystickAxes];
+  uint8_t raw[HAL_kMaxJoystickAxes];
 };
 typedef struct HAL_JoystickAxes HAL_JoystickAxes;
 
diff --git a/hal/src/main/native/include/hal/DutyCycle.h b/hal/src/main/native/include/hal/DutyCycle.h
index 05b654b..90266fd 100644
--- a/hal/src/main/native/include/hal/DutyCycle.h
+++ b/hal/src/main/native/include/hal/DutyCycle.h
@@ -71,24 +71,21 @@
                               int32_t* status);
 
 /**
- * Get the raw output ratio of the duty cycle signal.
- *
- * <p> 0 means always low, an output equal to
- * GetOutputScaleFactor() means always high.
+ * Get the raw high time of the duty cycle signal.
  *
  * @param[in] dutyCycleHandle the duty cycle handle
  * @param[out] status Error status variable. 0 on success.
- * @return output ratio in raw units
+ * @return high time of last pulse in nanoseconds
  */
-int32_t HAL_GetDutyCycleOutputRaw(HAL_DutyCycleHandle dutyCycleHandle,
-                                  int32_t* status);
+int32_t HAL_GetDutyCycleHighTime(HAL_DutyCycleHandle dutyCycleHandle,
+                                 int32_t* status);
 
 /**
  * Get the scale factor of the output.
  *
  * <p> An output equal to this value is always high, and then linearly scales
- * down to 0. Divide the result of getOutputRaw by this in order to get the
- * percentage between 0 and 1.
+ * down to 0. Divide a raw result by this in order to get the
+ * percentage between 0 and 1. Used by DMA.
  *
  * @param[in] dutyCycleHandle the duty cycle handle
  * @param[out] status Error status variable. 0 on success.
diff --git a/hal/src/main/native/include/hal/Extensions.h b/hal/src/main/native/include/hal/Extensions.h
index ad3f733..6f64fdb 100644
--- a/hal/src/main/native/include/hal/Extensions.h
+++ b/hal/src/main/native/include/hal/Extensions.h
@@ -10,8 +10,7 @@
  * @defgroup hal_extensions Simulator Extensions
  * @ingroup hal_capi
  * HAL Simulator Extensions.  These are libraries that provide additional
- * simulator functionality, such as a Gazebo interface, or a more light weight
- * simulation.
+ * simulator functionality.
  *
  * An extension must expose the HALSIM_InitExtension entry point which is
  * invoked after the library is loaded.
diff --git a/hal/src/main/native/include/hal/HALBase.h b/hal/src/main/native/include/hal/HALBase.h
index b31ec75..1fe6b3a 100644
--- a/hal/src/main/native/include/hal/HALBase.h
+++ b/hal/src/main/native/include/hal/HALBase.h
@@ -6,6 +6,14 @@
 
 #include <stdint.h>
 
+#ifdef __cplusplus
+#include <cstddef>
+#else
+
+#include <stddef.h>  // NOLINT(build/include_order)
+
+#endif
+
 #include "hal/Types.h"
 
 /**
@@ -67,6 +75,24 @@
 int64_t HAL_GetFPGARevision(int32_t* status);
 
 /**
+ * Returns the serial number.
+ *
+ * @param[out] buffer The serial number.
+ * @param size The maximum characters to copy into buffer.
+ * @return Number of characters copied into buffer.
+ */
+size_t HAL_GetSerialNumber(char* buffer, size_t size);
+
+/**
+ * Returns the comments from the roboRIO web interface.
+ *
+ * @param[out] buffer The comments string.
+ * @param size The maximum characters to copy into buffer.
+ * @return Number of characters copied into buffer.
+ */
+size_t HAL_GetComments(char* buffer, size_t size);
+
+/**
  * Returns the runtime type of the HAL.
  *
  * @return HAL Runtime Type
diff --git a/hal/src/main/native/include/hal/Interrupts.h b/hal/src/main/native/include/hal/Interrupts.h
index def800c..2bccbe9 100644
--- a/hal/src/main/native/include/hal/Interrupts.h
+++ b/hal/src/main/native/include/hal/Interrupts.h
@@ -35,7 +35,7 @@
 void HAL_CleanInterrupts(HAL_InterruptHandle interruptHandle);
 
 /**
- * In synchronous mode, waits for the defined interrupt to occur.
+ * Waits for the defined interrupt to occur.
  *
  * @param[in] interruptHandle the interrupt handle
  * @param[in] timeout         timeout in seconds
@@ -49,6 +49,21 @@
                              int32_t* status);
 
 /**
+ * Waits for any interrupt covered by the mask to occur.
+ *
+ * @param[in] interruptHandle the interrupt handle to use for the context
+ * @param[in] mask            the mask of interrupts to wait for
+ * @param[in] timeout         timeout in seconds
+ * @param[in] ignorePrevious  if true, ignore interrupts that happened before
+ *                            waitForInterrupt was called
+ * @param[out] status         Error status variable. 0 on success.
+ * @return the mask of interrupts that fired
+ */
+int64_t HAL_WaitForMultipleInterrupts(HAL_InterruptHandle interruptHandle,
+                                      int64_t mask, double timeout,
+                                      HAL_Bool ignorePrevious, int32_t* status);
+
+/**
  * Returns the timestamp for the rising interrupt that occurred most recently.
  *
  * This is in the same time domain as HAL_GetFPGATime().  It only contains the
diff --git a/hal/src/main/native/include/hal/PWM.h b/hal/src/main/native/include/hal/PWM.h
index 7fd125e..df9020b 100644
--- a/hal/src/main/native/include/hal/PWM.h
+++ b/hal/src/main/native/include/hal/PWM.h
@@ -22,7 +22,7 @@
  * Initializes a PWM port.
  *
  * @param[in] portHandle the port to initialize
- * @param[in] allocationLocation  the location where the allocation is occuring
+ * @param[in] allocationLocation  the location where the allocation is occurring
  *                                (can be null)
  * @param[out] status             Error status variable. 0 on success.
  * @return the created pwm handle
diff --git a/hal/src/main/native/include/hal/PowerDistribution.h b/hal/src/main/native/include/hal/PowerDistribution.h
index 47cc9b2..7c2b582 100644
--- a/hal/src/main/native/include/hal/PowerDistribution.h
+++ b/hal/src/main/native/include/hal/PowerDistribution.h
@@ -37,7 +37,7 @@
  *
  * @param[in] moduleNumber       the module number to initialize
  * @param[in] type               the type of module to intialize
- * @param[in] allocationLocation the location where the allocation is occuring
+ * @param[in] allocationLocation the location where the allocation is occurring
  * @param[out] status            Error status variable. 0 on success.
  * @return the created PowerDistribution
  */
diff --git a/hal/src/main/native/include/hal/Relay.h b/hal/src/main/native/include/hal/Relay.h
index 7d711b2..9ee104b 100644
--- a/hal/src/main/native/include/hal/Relay.h
+++ b/hal/src/main/native/include/hal/Relay.h
@@ -27,7 +27,7 @@
  * @param[in] portHandle         the port handle to initialize
  * @param[in] fwd                true for the forward port, false for the
  *                               reverse port
- * @param[in] allocationLocation the location where the allocation is occuring
+ * @param[in] allocationLocation the location where the allocation is occurring
  *                               (can be null)
  * @param[out] status            Error status variable. 0 on success.
  * @return the created relay handle
diff --git a/hal/src/main/native/include/hal/SPI.h b/hal/src/main/native/include/hal/SPI.h
index 84cec5d..f3c7fdc 100644
--- a/hal/src/main/native/include/hal/SPI.h
+++ b/hal/src/main/native/include/hal/SPI.h
@@ -90,23 +90,27 @@
  *
  * @param port  The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for
  *              MXP
- * @param speed The speed in Hz (0-1MHz)
+ * @param speed The speed in Hz (500KHz-10MHz)
  */
 void HAL_SetSPISpeed(HAL_SPIPort port, int32_t speed);
 
 /**
- * Sets the SPI options.
+ * Sets the SPI Mode.
  *
- * @param port             The number of the port to use. 0-3 for Onboard
- *                         CS0-CS2, 4 for MXP
- * @param msbFirst         True to write the MSB first, False for LSB first
- * @param sampleOnTrailing True to sample on the trailing edge, False to sample
- *                         on the leading edge
- * @param clkIdleHigh      True to set the clock to active low, False to set the
- *                         clock active high
+ * @param port  The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for
+ * MXP
+ * @param mode  The SPI mode to use
  */
-void HAL_SetSPIOpts(HAL_SPIPort port, HAL_Bool msbFirst,
-                    HAL_Bool sampleOnTrailing, HAL_Bool clkIdleHigh);
+void HAL_SetSPIMode(HAL_SPIPort port, HAL_SPIMode mode);
+
+/**
+ * Gets the SPI Mode.
+ *
+ * @param port  The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for
+ * MXP
+ * @returns     The SPI mode currently set
+ */
+HAL_SPIMode HAL_GetSPIMode(HAL_SPIPort port);
 
 /**
  * Sets the CS Active high for a SPI port.
diff --git a/hal/src/main/native/include/hal/SPITypes.h b/hal/src/main/native/include/hal/SPITypes.h
index de66226..34b5d61 100644
--- a/hal/src/main/native/include/hal/SPITypes.h
+++ b/hal/src/main/native/include/hal/SPITypes.h
@@ -25,6 +25,15 @@
 };
 // clang-format on
 
+// clang-format off
+HAL_ENUM(HAL_SPIMode) {
+  HAL_SPI_kMode0 = 0,
+  HAL_SPI_kMode1 = 1,
+  HAL_SPI_kMode2 = 2,
+  HAL_SPI_kMode3 = 3,
+};
+// clang-format on
+
 #ifdef __cplusplus
 namespace hal {
 
diff --git a/hal/src/main/native/include/hal/SimDevice.h b/hal/src/main/native/include/hal/SimDevice.h
index 7c0cf2d..f90cb9b 100644
--- a/hal/src/main/native/include/hal/SimDevice.h
+++ b/hal/src/main/native/include/hal/SimDevice.h
@@ -8,8 +8,7 @@
 
 #ifdef __cplusplus
 #include <initializer_list>
-
-#include <wpi/span.h>
+#include <span>
 #endif
 
 #include "hal/Types.h"
@@ -832,7 +831,7 @@
    * @return simulated enum value object
    */
   SimEnum CreateEnum(const char* name, int32_t direction,
-                     wpi::span<const char* const> options,
+                     std::span<const char* const> options,
                      int32_t initialValue) {
     return HAL_CreateSimValueEnum(m_handle, name, direction, options.size(),
                                   const_cast<const char**>(options.data()),
@@ -885,8 +884,8 @@
    * @return simulated enum value object
    */
   SimEnum CreateEnumDouble(const char* name, int32_t direction,
-                           wpi::span<const char* const> options,
-                           wpi::span<const double> optionValues,
+                           std::span<const char* const> options,
+                           std::span<const double> optionValues,
                            int32_t initialValue) {
     if (options.size() != optionValues.size()) {
       return {};
diff --git a/hal/src/main/native/include/hal/cpp/UnsafeDIO.h b/hal/src/main/native/include/hal/cpp/UnsafeDIO.h
index eb7f231..c849fd0 100644
--- a/hal/src/main/native/include/hal/cpp/UnsafeDIO.h
+++ b/hal/src/main/native/include/hal/cpp/UnsafeDIO.h
@@ -18,6 +18,16 @@
  * outside of the UnsafeManipulateDIO callback.
  */
 struct DIOSetProxy {
+  DIOSetProxy(tDIO::tOutputEnable setOutputDirReg,
+              tDIO::tOutputEnable unsetOutputDirReg,
+              tDIO::tDO setOutputStateReg, tDIO::tDO unsetOutputStateReg,
+              tDIO* dio)
+      : m_setOutputDirReg{setOutputDirReg},
+        m_unsetOutputDirReg{unsetOutputDirReg},
+        m_setOutputStateReg{setOutputStateReg},
+        m_unsetOutputStateReg{unsetOutputStateReg},
+        m_dio{dio} {}
+
   DIOSetProxy(const DIOSetProxy&) = delete;
   DIOSetProxy(DIOSetProxy&&) = delete;
   DIOSetProxy& operator=(const DIOSetProxy&) = delete;
diff --git a/hal/src/main/native/include/hal/roborio/InterruptManager.h b/hal/src/main/native/include/hal/roborio/InterruptManager.h
new file mode 100644
index 0000000..36d904b
--- /dev/null
+++ b/hal/src/main/native/include/hal/roborio/InterruptManager.h
@@ -0,0 +1,33 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#pragma once
+
+#include <FRC_FPGA_ChipObject/fpgainterfacecapi/NiFpga.h>
+#include <wpi/mutex.h>
+
+#include "hal/ChipObject.h"
+#include "hal/Types.h"
+
+namespace hal {
+class InterruptManager {
+ public:
+  static InterruptManager& GetInstance();
+  static void Initialize(tSystemInterface* baseSystem);
+
+  NiFpga_IrqContext GetContext() noexcept;
+  void ReleaseContext(NiFpga_IrqContext context) noexcept;
+
+  uint32_t WaitForInterrupt(NiFpga_IrqContext context, uint32_t mask,
+                            bool ignorePrevious, uint32_t timeoutInMs,
+                            int32_t* status);
+
+ private:
+  InterruptManager() = default;
+
+  wpi::priority_mutex currentMaskMutex;
+  uint32_t currentMask;
+  NiFpga_Session fpgaSession;
+};
+}  // namespace hal
diff --git a/hal/src/main/native/include/hal/simulation/DriverStationData.h b/hal/src/main/native/include/hal/simulation/DriverStationData.h
index 87223ec..b10cf03 100644
--- a/hal/src/main/native/include/hal/simulation/DriverStationData.h
+++ b/hal/src/main/native/include/hal/simulation/DriverStationData.h
@@ -4,6 +4,8 @@
 
 #pragma once
 
+#include <cstddef>
+
 #include "hal/DriverStationTypes.h"
 #include "hal/Types.h"
 #include "hal/simulation/NotifyListener.h"
@@ -145,11 +147,11 @@
 
 void HALSIM_SetJoystickIsXbox(int32_t stick, HAL_Bool isXbox);
 void HALSIM_SetJoystickType(int32_t stick, int32_t type);
-void HALSIM_SetJoystickName(int32_t stick, const char* name);
+void HALSIM_SetJoystickName(int32_t stick, const char* name, size_t size);
 void HALSIM_SetJoystickAxisType(int32_t stick, int32_t axis, int32_t type);
 
-void HALSIM_SetGameSpecificMessage(const char* message);
-void HALSIM_SetEventName(const char* name);
+void HALSIM_SetGameSpecificMessage(const char* message, size_t size);
+void HALSIM_SetEventName(const char* name, size_t size);
 void HALSIM_SetMatchType(HAL_MatchType type);
 void HALSIM_SetMatchNumber(int32_t matchNumber);
 void HALSIM_SetReplayNumber(int32_t replayNumber);
diff --git a/hal/src/main/native/include/hal/simulation/MockHooks.h b/hal/src/main/native/include/hal/simulation/MockHooks.h
index 330f72e..fe50656 100644
--- a/hal/src/main/native/include/hal/simulation/MockHooks.h
+++ b/hal/src/main/native/include/hal/simulation/MockHooks.h
@@ -36,4 +36,6 @@
     HALSIM_SimPeriodicCallback callback, void* param);
 void HALSIM_CancelSimPeriodicAfterCallback(int32_t uid);
 
+void HALSIM_CancelAllSimPeriodicCallbacks(void);
+
 }  // extern "C"
diff --git a/hal/src/main/native/include/hal/simulation/Reset.h b/hal/src/main/native/include/hal/simulation/Reset.h
new file mode 100644
index 0000000..447321c
--- /dev/null
+++ b/hal/src/main/native/include/hal/simulation/Reset.h
@@ -0,0 +1,7 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#pragma once
+
+extern "C" void HALSIM_ResetAllSimData(void);
diff --git a/hal/src/main/native/include/hal/simulation/RoboRioData.h b/hal/src/main/native/include/hal/simulation/RoboRioData.h
index 8f33bc5..864be5c 100644
--- a/hal/src/main/native/include/hal/simulation/RoboRioData.h
+++ b/hal/src/main/native/include/hal/simulation/RoboRioData.h
@@ -4,9 +4,14 @@
 
 #pragma once
 
+#include <cstddef>
+
 #include "hal/Types.h"
 #include "hal/simulation/NotifyListener.h"
 
+typedef void (*HAL_RoboRioStringCallback)(const char* name, void* param,
+                                          const char* str, size_t size);
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -121,6 +126,18 @@
 double HALSIM_GetRoboRioBrownoutVoltage(void);
 void HALSIM_SetRoboRioBrownoutVoltage(double brownoutVoltage);
 
+int32_t HALSIM_RegisterRoboRioSerialNumberCallback(
+    HAL_RoboRioStringCallback callback, void* param, HAL_Bool initialNotify);
+void HALSIM_CancelRoboRioSerialNumberCallback(int32_t uid);
+size_t HALSIM_GetRoboRioSerialNumber(char* buffer, size_t size);
+void HALSIM_SetRoboRioSerialNumber(const char* serialNumber, size_t size);
+
+int32_t HALSIM_RegisterRoboRioCommentsCallback(
+    HAL_RoboRioStringCallback callback, void* param, HAL_Bool initialNotify);
+void HALSIM_CancelRoboRioCommentsCallback(int32_t uid);
+size_t HALSIM_GetRoboRioComments(char* buffer, size_t size);
+void HALSIM_SetRoboRioComments(const char* comments, size_t size);
+
 void HALSIM_RegisterRoboRioAllCallbacks(HAL_NotifyCallback callback,
                                         void* param, HAL_Bool initialNotify);
 
diff --git a/hal/src/main/native/include/hal/simulation/SimDataValue.h b/hal/src/main/native/include/hal/simulation/SimDataValue.h
index 4368a10..0f0cde5 100644
--- a/hal/src/main/native/include/hal/simulation/SimDataValue.h
+++ b/hal/src/main/native/include/hal/simulation/SimDataValue.h
@@ -87,9 +87,21 @@
           T (*GetDefault)() = nullptr>
 class SimDataValue final : public impl::SimDataValueBase<T, MakeValue> {
  public:
+// FIXME: GCC 12.1 gives the false positive "the address of <GetDefault> will
+// never be NULL" because it doesn't realize the default template parameter can
+// make GetDefault nullptr. In C++20, replace "T (*GetDefault)() = nullptr" with
+// "T (*GetDefault)() = [] { return T(); }" and unconditionally call
+// GetDefault() to fix the warning.
+#if __GNUC__ >= 12
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Waddress"
+#endif  // __GNUC__ >= 12
   SimDataValue()
       : impl::SimDataValueBase<T, MakeValue>(
             GetDefault != nullptr ? GetDefault() : T()) {}
+#if __GNUC__ >= 12
+#pragma GCC diagnostic pop
+#endif  // __GNUC__ >= 12
   explicit SimDataValue(T value)
       : impl::SimDataValueBase<T, MakeValue>(value) {}
 
@@ -136,22 +148,24 @@
  * @param DATA the backing data array
  * @param LOWERNAME the lowercase name of the backing data variable
  */
-#define HAL_SIMDATAVALUE_DEFINE_CAPI(TYPE, NS, CAPINAME, DATA, LOWERNAME)  \
-  int32_t NS##_Register##CAPINAME##Callback(                               \
-      int32_t index, HAL_NotifyCallback callback, void* param,             \
-      HAL_Bool initialNotify) {                                            \
-    return DATA[index].LOWERNAME.RegisterCallback(callback, param,         \
-                                                  initialNotify);          \
-  }                                                                        \
-                                                                           \
-  void NS##_Cancel##CAPINAME##Callback(int32_t index, int32_t uid) {       \
-    DATA[index].LOWERNAME.CancelCallback(uid);                             \
-  }                                                                        \
-                                                                           \
-  TYPE NS##_Get##CAPINAME(int32_t index) { return DATA[index].LOWERNAME; } \
-                                                                           \
-  void NS##_Set##CAPINAME(int32_t index, TYPE LOWERNAME) {                 \
-    DATA[index].LOWERNAME = LOWERNAME;                                     \
+#define HAL_SIMDATAVALUE_DEFINE_CAPI(TYPE, NS, CAPINAME, DATA, LOWERNAME) \
+  int32_t NS##_Register##CAPINAME##Callback(                              \
+      int32_t index, HAL_NotifyCallback callback, void* param,            \
+      HAL_Bool initialNotify) {                                           \
+    return DATA[index].LOWERNAME.RegisterCallback(callback, param,        \
+                                                  initialNotify);         \
+  }                                                                       \
+                                                                          \
+  void NS##_Cancel##CAPINAME##Callback(int32_t index, int32_t uid) {      \
+    DATA[index].LOWERNAME.CancelCallback(uid);                            \
+  }                                                                       \
+                                                                          \
+  TYPE NS##_Get##CAPINAME(int32_t index) {                                \
+    return DATA[index].LOWERNAME;                                         \
+  }                                                                       \
+                                                                          \
+  void NS##_Set##CAPINAME(int32_t index, TYPE LOWERNAME) {                \
+    DATA[index].LOWERNAME = LOWERNAME;                                    \
   }
 
 /**
@@ -220,9 +234,13 @@
     DATA->LOWERNAME.CancelCallback(uid);                                     \
   }                                                                          \
                                                                              \
-  TYPE NS##_Get##CAPINAME(void) { return DATA->LOWERNAME; }                  \
+  TYPE NS##_Get##CAPINAME(void) {                                            \
+    return DATA->LOWERNAME;                                                  \
+  }                                                                          \
                                                                              \
-  void NS##_Set##CAPINAME(TYPE LOWERNAME) { DATA->LOWERNAME = LOWERNAME; }
+  void NS##_Set##CAPINAME(TYPE LOWERNAME) {                                  \
+    DATA->LOWERNAME = LOWERNAME;                                             \
+  }
 
 /**
  * Define a stub standard C API for simulation data.
@@ -249,7 +267,9 @@
                                                                       \
   void NS##_Cancel##CAPINAME##Callback(int32_t index, int32_t uid) {} \
                                                                       \
-  TYPE NS##_Get##CAPINAME(int32_t index) { return RETURN; }           \
+  TYPE NS##_Get##CAPINAME(int32_t index) {                            \
+    return RETURN;                                                    \
+  }                                                                   \
                                                                       \
   void NS##_Set##CAPINAME(int32_t index, TYPE) {}
 
@@ -269,18 +289,20 @@
  * @param CAPINAME the C API name (usually first letter capitalized)
  * @param RETURN what to return from the Get function
  */
-#define HAL_SIMDATAVALUE_STUB_CAPI_CHANNEL(TYPE, NS, CAPINAME, RETURN)       \
-  int32_t NS##_Register##CAPINAME##Callback(                                 \
-      int32_t index, int32_t channel, HAL_NotifyCallback callback,           \
-      void* param, HAL_Bool initialNotify) {                                 \
-    return 0;                                                                \
-  }                                                                          \
-                                                                             \
-  void NS##_Cancel##CAPINAME##Callback(int32_t index, int32_t channel,       \
-                                       int32_t uid) {}                       \
-                                                                             \
-  TYPE NS##_Get##CAPINAME(int32_t index, int32_t channel) { return RETURN; } \
-                                                                             \
+#define HAL_SIMDATAVALUE_STUB_CAPI_CHANNEL(TYPE, NS, CAPINAME, RETURN) \
+  int32_t NS##_Register##CAPINAME##Callback(                           \
+      int32_t index, int32_t channel, HAL_NotifyCallback callback,     \
+      void* param, HAL_Bool initialNotify) {                           \
+    return 0;                                                          \
+  }                                                                    \
+                                                                       \
+  void NS##_Cancel##CAPINAME##Callback(int32_t index, int32_t channel, \
+                                       int32_t uid) {}                 \
+                                                                       \
+  TYPE NS##_Get##CAPINAME(int32_t index, int32_t channel) {            \
+    return RETURN;                                                     \
+  }                                                                    \
+                                                                       \
   void NS##_Set##CAPINAME(int32_t index, int32_t channel, TYPE) {}
 
 /**
@@ -306,7 +328,9 @@
                                                                           \
   void NS##_Cancel##CAPINAME##Callback(int32_t uid) {}                    \
                                                                           \
-  TYPE NS##_Get##CAPINAME(void) { return RETURN; }                        \
+  TYPE NS##_Get##CAPINAME(void) {                                         \
+    return RETURN;                                                        \
+  }                                                                       \
                                                                           \
   void NS##_Set##CAPINAME(TYPE) {}
 
diff --git a/hal/src/main/native/sim/CANAPI.cpp b/hal/src/main/native/sim/CANAPI.cpp
index 4d733bb..38014bd 100644
--- a/hal/src/main/native/sim/CANAPI.cpp
+++ b/hal/src/main/native/sim/CANAPI.cpp
@@ -93,6 +93,9 @@
 
 void HAL_CleanCAN(HAL_CANHandle handle) {
   auto data = canHandles->Free(handle);
+  if (data == nullptr) {
+    return;
+  }
 
   std::scoped_lock lock(data->mapMutex);
 
diff --git a/hal/src/main/native/sim/DIO.cpp b/hal/src/main/native/sim/DIO.cpp
index 1b744a1..b611b35 100644
--- a/hal/src/main/native/sim/DIO.cpp
+++ b/hal/src/main/native/sim/DIO.cpp
@@ -151,6 +151,23 @@
   SimDigitalPWMData[id].dutyCycle = dutyCycle;
 }
 
+void HAL_SetDigitalPWMPPS(HAL_DigitalPWMHandle pwmGenerator, double dutyCycle,
+                          int32_t* status) {
+  auto port = digitalPWMHandles->Get(pwmGenerator);
+  if (port == nullptr) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+  int32_t id = *port;
+  if (dutyCycle > 1.0) {
+    dutyCycle = 1.0;
+  }
+  if (dutyCycle < 0.0) {
+    dutyCycle = 0.0;
+  }
+  SimDigitalPWMData[id].dutyCycle = dutyCycle;
+}
+
 void HAL_SetDigitalPWMOutputChannel(HAL_DigitalPWMHandle pwmGenerator,
                                     int32_t channel, int32_t* status) {
   auto port = digitalPWMHandles->Get(pwmGenerator);
@@ -225,7 +242,7 @@
   return value;
 }
 
-void HAL_Pulse(HAL_DigitalHandle dioPortHandle, double pulseLength,
+void HAL_Pulse(HAL_DigitalHandle dioPortHandle, double pulseLengthSeconds,
                int32_t* status) {
   auto port = digitalChannelHandles->Get(dioPortHandle, HAL_HandleEnum::DIO);
   if (port == nullptr) {
@@ -235,6 +252,11 @@
   // TODO (Thad) Add this
 }
 
+void HAL_PulseMultiple(uint32_t channelMask, double pulseLengthSeconds,
+                       int32_t* status) {
+  // TODO (Thad) Add this
+}
+
 HAL_Bool HAL_IsPulsing(HAL_DigitalHandle dioPortHandle, int32_t* status) {
   auto port = digitalChannelHandles->Get(dioPortHandle, HAL_HandleEnum::DIO);
   if (port == nullptr) {
diff --git a/hal/src/main/native/sim/DigitalInternal.h b/hal/src/main/native/sim/DigitalInternal.h
index cd1ac5f..e7f531e 100644
--- a/hal/src/main/native/sim/DigitalInternal.h
+++ b/hal/src/main/native/sim/DigitalInternal.h
@@ -30,7 +30,7 @@
  *   reliably down to 10.0 ms; starting at about 8.5ms, the servo sometimes hums
  *   and get hot; by 5.0ms the hum is nearly continuous
  * - 10ms periods work well for Victor 884
- * - 5ms periods allows higher update rates for Luminary Micro Jaguar speed
+ * - 5ms periods allows higher update rates for Luminary Micro Jaguar motor
  *   controllers. Due to the shipping firmware on the Jaguar, we can't run the
  *   update period less than 5.05 ms.
  *
diff --git a/hal/src/main/native/sim/DriverStation.cpp b/hal/src/main/native/sim/DriverStation.cpp
index 723cdac..c99493d 100644
--- a/hal/src/main/native/sim/DriverStation.cpp
+++ b/hal/src/main/native/sim/DriverStation.cpp
@@ -8,37 +8,94 @@
 #include <pthread.h>
 #endif
 
+#include <atomic>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
 #include <string>
 
 #include <fmt/format.h>
+#include <wpi/EventVector.h>
 #include <wpi/condition_variable.h>
 #include <wpi/mutex.h>
 
 #include "HALInitializer.h"
+#include "hal/Errors.h"
 #include "hal/cpp/fpga_clock.h"
 #include "hal/simulation/MockHooks.h"
 #include "mockdata/DriverStationDataInternal.h"
 
 static wpi::mutex msgMutex;
-static wpi::condition_variable* newDSDataAvailableCond;
-static wpi::mutex newDSDataAvailableMutex;
-static int newDSDataAvailableCounter{0};
-static std::atomic_bool isFinalized{false};
 static std::atomic<HALSIM_SendErrorHandler> sendErrorHandler{nullptr};
 static std::atomic<HALSIM_SendConsoleLineHandler> sendConsoleLineHandler{
     nullptr};
 
+using namespace hal;
+
+static constexpr int kJoystickPorts = 6;
+
+namespace {
+struct JoystickDataCache {
+  JoystickDataCache() { std::memset(this, 0, sizeof(*this)); }
+  void Update();
+
+  HAL_JoystickAxes axes[kJoystickPorts];
+  HAL_JoystickPOVs povs[kJoystickPorts];
+  HAL_JoystickButtons buttons[kJoystickPorts];
+  HAL_AllianceStationID allianceStation;
+  double matchTime;
+};
+static_assert(std::is_standard_layout_v<JoystickDataCache>);
+// static_assert(std::is_trivial_v<JoystickDataCache>);
+
+static std::atomic_bool gShutdown{false};
+
+struct FRCDriverStation {
+  ~FRCDriverStation() { gShutdown = true; }
+  wpi::EventVector newDataEvents;
+  wpi::mutex cacheMutex;
+};
+}  // namespace
+
+void JoystickDataCache::Update() {
+  for (int i = 0; i < kJoystickPorts; i++) {
+    SimDriverStationData->GetJoystickAxes(i, &axes[i]);
+    SimDriverStationData->GetJoystickPOVs(i, &povs[i]);
+    SimDriverStationData->GetJoystickButtons(i, &buttons[i]);
+  }
+  allianceStation = SimDriverStationData->allianceStationId;
+  matchTime = SimDriverStationData->matchTime;
+}
+
+#define CHECK_JOYSTICK_NUMBER(stickNum)                  \
+  if ((stickNum) < 0 || (stickNum) >= HAL_kMaxJoysticks) \
+  return PARAMETER_OUT_OF_RANGE
+
+static HAL_ControlWord newestControlWord;
+static JoystickDataCache caches[3];
+static JoystickDataCache* currentRead = &caches[0];
+static JoystickDataCache* currentReadLocal = &caches[0];
+static std::atomic<JoystickDataCache*> currentCache{&caches[1]};
+static JoystickDataCache* lastGiven = &caches[1];
+static JoystickDataCache* cacheToUpdate = &caches[2];
+
+static ::FRCDriverStation* driverStation;
+
 namespace hal::init {
 void InitializeDriverStation() {
-  static wpi::condition_variable nddaC;
-  newDSDataAvailableCond = &nddaC;
+  static FRCDriverStation ds;
+  driverStation = &ds;
 }
 }  // namespace hal::init
 
-using namespace hal;
+namespace hal {
+static void DefaultPrintErrorImpl(const char* line, size_t size) {
+  std::fwrite(line, size, 1, stderr);
+}
+}  // namespace hal
+
+static std::atomic<void (*)(const char* line, size_t size)> gPrintErrorImpl{
+    hal::DefaultPrintErrorImpl};
 
 extern "C" {
 
@@ -92,7 +149,8 @@
       if (callStack && callStack[0] != '\0') {
         fmt::format_to(fmt::appender{buf}, "{}\n", callStack);
       }
-      std::fwrite(buf.data(), buf.size(), 1, stderr);
+      auto printError = gPrintErrorImpl.load();
+      printError(buf.data(), buf.size());
     }
     if (i == KEEP_MSGS) {
       // replace the oldest one
@@ -111,6 +169,10 @@
   return retval;
 }
 
+void HAL_SetPrintErrorImpl(void (*func)(const char* line, size_t size)) {
+  gPrintErrorImpl.store(func ? func : hal::DefaultPrintErrorImpl);
+}
+
 int32_t HAL_SendConsoleLine(const char* line) {
   auto handler = sendConsoleLineHandler.load();
   if (handler) {
@@ -122,39 +184,67 @@
 }
 
 int32_t HAL_GetControlWord(HAL_ControlWord* controlWord) {
-  std::memset(controlWord, 0, sizeof(HAL_ControlWord));
-  controlWord->enabled = SimDriverStationData->enabled;
-  controlWord->autonomous = SimDriverStationData->autonomous;
-  controlWord->test = SimDriverStationData->test;
-  controlWord->eStop = SimDriverStationData->eStop;
-  controlWord->fmsAttached = SimDriverStationData->fmsAttached;
-  controlWord->dsAttached = SimDriverStationData->dsAttached;
+  if (gShutdown) {
+    return INCOMPATIBLE_STATE;
+  }
+  std::scoped_lock lock{driverStation->cacheMutex};
+  *controlWord = newestControlWord;
   return 0;
 }
 
 HAL_AllianceStationID HAL_GetAllianceStation(int32_t* status) {
-  *status = 0;
-  return SimDriverStationData->allianceStationId;
+  if (gShutdown) {
+    return HAL_AllianceStationID_kRed1;
+  }
+  std::scoped_lock lock{driverStation->cacheMutex};
+  return currentRead->allianceStation;
 }
 
 int32_t HAL_GetJoystickAxes(int32_t joystickNum, HAL_JoystickAxes* axes) {
-  SimDriverStationData->GetJoystickAxes(joystickNum, axes);
+  if (gShutdown) {
+    return INCOMPATIBLE_STATE;
+  }
+  CHECK_JOYSTICK_NUMBER(joystickNum);
+  std::scoped_lock lock{driverStation->cacheMutex};
+  *axes = currentRead->axes[joystickNum];
   return 0;
 }
 
 int32_t HAL_GetJoystickPOVs(int32_t joystickNum, HAL_JoystickPOVs* povs) {
-  SimDriverStationData->GetJoystickPOVs(joystickNum, povs);
+  if (gShutdown) {
+    return INCOMPATIBLE_STATE;
+  }
+  CHECK_JOYSTICK_NUMBER(joystickNum);
+  std::scoped_lock lock{driverStation->cacheMutex};
+  *povs = currentRead->povs[joystickNum];
   return 0;
 }
 
 int32_t HAL_GetJoystickButtons(int32_t joystickNum,
                                HAL_JoystickButtons* buttons) {
-  SimDriverStationData->GetJoystickButtons(joystickNum, buttons);
+  if (gShutdown) {
+    return INCOMPATIBLE_STATE;
+  }
+  CHECK_JOYSTICK_NUMBER(joystickNum);
+  std::scoped_lock lock{driverStation->cacheMutex};
+  *buttons = currentRead->buttons[joystickNum];
   return 0;
 }
 
+void HAL_GetAllJoystickData(HAL_JoystickAxes* axes, HAL_JoystickPOVs* povs,
+                            HAL_JoystickButtons* buttons) {
+  if (gShutdown) {
+    return;
+  }
+  std::scoped_lock lock{driverStation->cacheMutex};
+  std::memcpy(axes, currentRead->axes, sizeof(currentRead->axes));
+  std::memcpy(povs, currentRead->povs, sizeof(currentRead->povs));
+  std::memcpy(buttons, currentRead->buttons, sizeof(currentRead->buttons));
+}
+
 int32_t HAL_GetJoystickDescriptor(int32_t joystickNum,
                                   HAL_JoystickDescriptor* desc) {
+  CHECK_JOYSTICK_NUMBER(joystickNum);
   SimDriverStationData->GetJoystickDescriptor(joystickNum, desc);
   return 0;
 }
@@ -196,7 +286,11 @@
 }
 
 double HAL_GetMatchTime(int32_t* status) {
-  return SimDriverStationData->matchTime;
+  if (gShutdown) {
+    return 0;
+  }
+  std::scoped_lock lock{driverStation->cacheMutex};
+  return currentRead->matchTime;
 }
 
 int32_t HAL_GetMatchInfo(HAL_MatchInfo* info) {
@@ -224,103 +318,74 @@
   // TODO
 }
 
-static int& GetThreadLocalLastCount() {
-  // There is a rollover error condition here. At Packet# = n * (uintmax), this
-  // will return false when instead it should return true. However, this at a
-  // 20ms rate occurs once every 2.7 years of DS connected runtime, so not
-  // worth the cycles to check.
-  thread_local int lastCount{0};
-  return lastCount;
+void HAL_RefreshDSData(void) {
+  if (gShutdown) {
+    return;
+  }
+  HAL_ControlWord controlWord;
+  std::memset(&controlWord, 0, sizeof(controlWord));
+  controlWord.enabled = SimDriverStationData->enabled;
+  controlWord.autonomous = SimDriverStationData->autonomous;
+  controlWord.test = SimDriverStationData->test;
+  controlWord.eStop = SimDriverStationData->eStop;
+  controlWord.fmsAttached = SimDriverStationData->fmsAttached;
+  controlWord.dsAttached = SimDriverStationData->dsAttached;
+  std::scoped_lock lock{driverStation->cacheMutex};
+  JoystickDataCache* prev = currentCache.exchange(nullptr);
+  if (prev != nullptr) {
+    currentRead = prev;
+  }
+  newestControlWord = controlWord;
 }
 
-HAL_Bool HAL_IsNewControlData(void) {
-  std::scoped_lock lock(newDSDataAvailableMutex);
-  int& lastCount = GetThreadLocalLastCount();
-  int currentCount = newDSDataAvailableCounter;
-  if (lastCount == currentCount) {
-    return false;
+void HAL_ProvideNewDataEventHandle(WPI_EventHandle handle) {
+  if (gShutdown) {
+    return;
   }
-  lastCount = currentCount;
-  return true;
-}
-
-void HAL_WaitForDSData(void) {
-  HAL_WaitForDSDataTimeout(0);
-}
-
-HAL_Bool HAL_WaitForDSDataTimeout(double timeout) {
-  std::unique_lock lock(newDSDataAvailableMutex);
-  int& lastCount = GetThreadLocalLastCount();
-  int currentCount = newDSDataAvailableCounter;
-  if (lastCount != currentCount) {
-    lastCount = currentCount;
-    return true;
-  }
-
-  if (isFinalized.load()) {
-    return false;
-  }
-  auto timeoutTime =
-      std::chrono::steady_clock::now() + std::chrono::duration<double>(timeout);
-
-  while (newDSDataAvailableCounter == currentCount) {
-    if (timeout > 0) {
-      auto timedOut = newDSDataAvailableCond->wait_until(lock, timeoutTime);
-      if (timedOut == std::cv_status::timeout) {
-        return false;
-      }
-    } else {
-      newDSDataAvailableCond->wait(lock);
-    }
-  }
-  lastCount = newDSDataAvailableCounter;
-  return true;
-}
-
-// Constant number to be used for our occur handle
-constexpr int32_t refNumber = 42;
-
-static int32_t newDataOccur(uint32_t refNum) {
-  // Since we could get other values, require our specific handle
-  // to signal our threads
-  if (refNum != refNumber) {
-    return 0;
-  }
-  SimDriverStationData->CallNewDataCallbacks();
-  std::scoped_lock lock(newDSDataAvailableMutex);
-  // Nofify all threads
-  newDSDataAvailableCounter++;
-  newDSDataAvailableCond->notify_all();
-  return 0;
-}
-
-void HAL_InitializeDriverStation(void) {
   hal::init::CheckInit();
-  static std::atomic_bool initialized{false};
-  static wpi::mutex initializeMutex;
-  // Initial check, as if it's true initialization has finished
-  if (initialized) {
-    return;
-  }
-
-  std::scoped_lock lock(initializeMutex);
-  // Second check in case another thread was waiting
-  if (initialized) {
-    return;
-  }
-
-  SimDriverStationData->ResetData();
-
-  std::atexit([]() {
-    isFinalized.store(true);
-    HAL_ReleaseDSMutex();
-  });
-
-  initialized = true;
+  driverStation->newDataEvents.Add(handle);
 }
 
-void HAL_ReleaseDSMutex(void) {
-  newDataOccur(refNumber);
+void HAL_RemoveNewDataEventHandle(WPI_EventHandle handle) {
+  if (gShutdown) {
+    return;
+  }
+  driverStation->newDataEvents.Remove(handle);
+}
+
+HAL_Bool HAL_GetOutputsEnabled(void) {
+  if (gShutdown) {
+    return false;
+  }
+  std::scoped_lock lock{driverStation->cacheMutex};
+  return newestControlWord.enabled && newestControlWord.dsAttached;
 }
 
 }  // extern "C"
+
+namespace hal {
+void NewDriverStationData() {
+  if (gShutdown) {
+    return;
+  }
+  cacheToUpdate->Update();
+
+  JoystickDataCache* given = cacheToUpdate;
+  JoystickDataCache* prev = currentCache.exchange(cacheToUpdate);
+  if (prev == nullptr) {
+    cacheToUpdate = currentReadLocal;
+    currentReadLocal = lastGiven;
+  } else {
+    // Current read local does not update
+    cacheToUpdate = prev;
+  }
+  lastGiven = given;
+
+  driverStation->newDataEvents.Wakeup();
+  SimDriverStationData->CallNewDataCallbacks();
+}
+
+void InitializeDriverStation() {
+  SimDriverStationData->ResetData();
+}
+}  // namespace hal
diff --git a/hal/src/main/native/sim/DutyCycle.cpp b/hal/src/main/native/sim/DutyCycle.cpp
index 51c6506..f46d2da 100644
--- a/hal/src/main/native/sim/DutyCycle.cpp
+++ b/hal/src/main/native/sim/DutyCycle.cpp
@@ -84,6 +84,7 @@
   }
   return SimDutyCycleData[dutyCycle->index].frequency;
 }
+
 double HAL_GetDutyCycleOutput(HAL_DutyCycleHandle dutyCycleHandle,
                               int32_t* status) {
   auto dutyCycle = dutyCycleHandles->Get(dutyCycleHandle);
@@ -93,20 +94,29 @@
   }
   return SimDutyCycleData[dutyCycle->index].output;
 }
-int32_t HAL_GetDutyCycleOutputRaw(HAL_DutyCycleHandle dutyCycleHandle,
-                                  int32_t* status) {
+
+int32_t HAL_GetDutyCycleHighTime(HAL_DutyCycleHandle dutyCycleHandle,
+                                 int32_t* status) {
   auto dutyCycle = dutyCycleHandles->Get(dutyCycleHandle);
   if (dutyCycle == nullptr) {
     *status = HAL_HANDLE_ERROR;
     return 0;
   }
-  return SimDutyCycleData[dutyCycle->index].output *
-         HAL_GetDutyCycleOutputScaleFactor(dutyCycleHandle, status);
+
+  if (SimDutyCycleData[dutyCycle->index].frequency == 0) {
+    return 0;
+  }
+
+  double periodSeconds = 1.0 / SimDutyCycleData[dutyCycle->index].frequency;
+  double periodNanoSeconds = periodSeconds * 1e9;
+  return periodNanoSeconds * SimDutyCycleData[dutyCycle->index].output;
 }
+
 int32_t HAL_GetDutyCycleOutputScaleFactor(HAL_DutyCycleHandle dutyCycleHandle,
                                           int32_t* status) {
   return 4e7 - 1;
 }
+
 int32_t HAL_GetDutyCycleFPGAIndex(HAL_DutyCycleHandle dutyCycleHandle,
                                   int32_t* status) {
   auto dutyCycle = dutyCycleHandles->Get(dutyCycleHandle);
diff --git a/hal/src/main/native/sim/Encoder.cpp b/hal/src/main/native/sim/Encoder.cpp
index 78aa281..137ee78 100644
--- a/hal/src/main/native/sim/Encoder.cpp
+++ b/hal/src/main/native/sim/Encoder.cpp
@@ -53,7 +53,7 @@
                           HAL_FPGAEncoderHandle* fpgaHandle,
                           HAL_CounterHandle* counterHandle) {
   auto encoder = encoderHandles->Get(handle);
-  if (!handle) {
+  if (!encoder) {
     return false;
   }
 
diff --git a/hal/src/main/native/sim/HAL.cpp b/hal/src/main/native/sim/HAL.cpp
index 8d911df..82dd8c4 100644
--- a/hal/src/main/native/sim/HAL.cpp
+++ b/hal/src/main/native/sim/HAL.cpp
@@ -57,6 +57,10 @@
 static SimPeriodicCallbackRegistry gSimPeriodicBefore;
 static SimPeriodicCallbackRegistry gSimPeriodicAfter;
 
+namespace hal {
+void InitializeDriverStation();
+}  // namespace hal
+
 namespace hal::init {
 void InitializeHAL() {
   InitializeAccelerometerData();
@@ -276,6 +280,14 @@
   return 0;  // TODO: Find a better number to return;
 }
 
+size_t HAL_GetSerialNumber(char* buffer, size_t size) {
+  return HALSIM_GetRoboRioSerialNumber(buffer, size);
+}
+
+size_t HAL_GetComments(char* buffer, size_t size) {
+  return HALSIM_GetRoboRioComments(buffer, size);
+}
+
 uint64_t HAL_GetFPGATime(int32_t* status) {
   return hal::GetFPGATime();
 }
@@ -335,7 +347,7 @@
   hal::init::HAL_IsInitialized.store(true);
 
   hal::RestartTiming();
-  HAL_InitializeDriverStation();
+  hal::InitializeDriverStation();
 
   initialized = true;
 
@@ -409,6 +421,11 @@
   gSimPeriodicAfter.Cancel(uid);
 }
 
+void HALSIM_CancelAllSimPeriodicCallbacks(void) {
+  gSimPeriodicBefore.Reset();
+  gSimPeriodicAfter.Reset();
+}
+
 int64_t HAL_Report(int32_t resource, int32_t instanceNumber, int32_t context,
                    const char* feature) {
   return 0;  // Do nothing for now
diff --git a/hal/src/main/native/sim/Interrupts.cpp b/hal/src/main/native/sim/Interrupts.cpp
index e55316d..ac9f7f8 100644
--- a/hal/src/main/native/sim/Interrupts.cpp
+++ b/hal/src/main/native/sim/Interrupts.cpp
@@ -351,6 +351,26 @@
   }
 }
 
+int64_t HAL_WaitForMultipleInterrupts(HAL_InterruptHandle interruptHandle,
+                                      int64_t mask, double timeout,
+                                      HAL_Bool ignorePrevious,
+                                      int32_t* status) {
+  // TODO make this properly work, will require a decent rewrite
+  auto interrupt = interruptHandles->Get(interruptHandle);
+  if (interrupt == nullptr) {
+    *status = HAL_HANDLE_ERROR;
+    return WaitResult::Timeout;
+  }
+
+  if (interrupt->isAnalog) {
+    return WaitForInterruptAnalog(interruptHandle, interrupt.get(), timeout,
+                                  ignorePrevious);
+  } else {
+    return WaitForInterruptDigital(interruptHandle, interrupt.get(), timeout,
+                                   ignorePrevious);
+  }
+}
+
 int64_t HAL_ReadInterruptRisingTimestamp(HAL_InterruptHandle interruptHandle,
                                          int32_t* status) {
   auto interrupt = interruptHandles->Get(interruptHandle);
diff --git a/hal/src/main/native/sim/MockHooks.cpp b/hal/src/main/native/sim/MockHooks.cpp
index 4e20c6a..93c385e 100644
--- a/hal/src/main/native/sim/MockHooks.cpp
+++ b/hal/src/main/native/sim/MockHooks.cpp
@@ -122,7 +122,7 @@
     int32_t status = 0;
     uint64_t curTime = HAL_GetFPGATime(&status);
     uint64_t nextTimeout = HALSIM_GetNextNotifierTimeout();
-    uint64_t step = std::min(delta, nextTimeout - curTime);
+    uint64_t step = (std::min)(delta, nextTimeout - curTime);
 
     StepTiming(step);
     delta -= step;
diff --git a/hal/src/main/native/sim/PowerDistribution.cpp b/hal/src/main/native/sim/PowerDistribution.cpp
index ddda420..22e4bba 100644
--- a/hal/src/main/native/sim/PowerDistribution.cpp
+++ b/hal/src/main/native/sim/PowerDistribution.cpp
@@ -147,11 +147,23 @@
 }
 double HAL_GetPowerDistributionTotalCurrent(HAL_PowerDistributionHandle handle,
                                             int32_t* status) {
-  return 0.0;
+  auto module = hal::can::GetCANModuleFromHandle(handle, status);
+  if (*status != 0) {
+    return 0.0;
+  }
+
+  double total = 0.0;
+  auto& data = SimPowerDistributionData[module];
+  for (int i = 0; i < kNumPDSimChannels; i++) {
+    total += data.current[i];
+  }
+  return total;
 }
 double HAL_GetPowerDistributionTotalPower(HAL_PowerDistributionHandle handle,
                                           int32_t* status) {
-  return 0.0;
+  double voltage = HAL_GetPowerDistributionVoltage(handle, status);
+  double current = HAL_GetPowerDistributionTotalCurrent(handle, status);
+  return voltage * current;
 }
 double HAL_GetPowerDistributionTotalEnergy(HAL_PowerDistributionHandle handle,
                                            int32_t* status) {
diff --git a/hal/src/main/native/sim/SPI.cpp b/hal/src/main/native/sim/SPI.cpp
index bf362a8..70e2bde 100644
--- a/hal/src/main/native/sim/SPI.cpp
+++ b/hal/src/main/native/sim/SPI.cpp
@@ -34,8 +34,10 @@
   SimSPIData[port].initialized = false;
 }
 void HAL_SetSPISpeed(HAL_SPIPort port, int32_t speed) {}
-void HAL_SetSPIOpts(HAL_SPIPort port, HAL_Bool msbFirst,
-                    HAL_Bool sampleOnTrailing, HAL_Bool clkIdleHigh) {}
+void HAL_SetSPIMode(HAL_SPIPort port, HAL_SPIMode mode) {}
+HAL_SPIMode HAL_GetSPIMode(HAL_SPIPort port) {
+  return HAL_SPI_kMode0;
+}
 void HAL_SetSPIChipSelectActiveHigh(HAL_SPIPort port, int32_t* status) {}
 void HAL_SetSPIChipSelectActiveLow(HAL_SPIPort port, int32_t* status) {}
 int32_t HAL_GetSPIHandle(HAL_SPIPort port) {
diff --git a/hal/src/main/native/sim/mockdata/DriverStationData.cpp b/hal/src/main/native/sim/mockdata/DriverStationData.cpp
index 1c76a7a..a0cae1c 100644
--- a/hal/src/main/native/sim/mockdata/DriverStationData.cpp
+++ b/hal/src/main/native/sim/mockdata/DriverStationData.cpp
@@ -216,8 +216,12 @@
   m_newDataCallbacks(&empty);
 }
 
+namespace hal {
+void NewDriverStationData();
+}  // namespace hal
+
 void DriverStationData::NotifyNewData() {
-  HAL_ReleaseDSMutex();
+  hal::NewDriverStationData();
 }
 
 void DriverStationData::SetJoystickButton(int32_t stick, int32_t button,
@@ -335,14 +339,17 @@
   m_joystickDescriptorCallbacks(stick, &m_joystickData[stick].descriptor);
 }
 
-void DriverStationData::SetJoystickName(int32_t stick, const char* name) {
+void DriverStationData::SetJoystickName(int32_t stick, const char* name,
+                                        size_t size) {
   if (stick < 0 || stick >= kNumJoysticks) {
     return;
   }
   std::scoped_lock lock(m_joystickDataMutex);
-  std::strncpy(m_joystickData[stick].descriptor.name, name,
-               sizeof(m_joystickData[stick].descriptor.name) - 1);
-  *(std::end(m_joystickData[stick].descriptor.name) - 1) = '\0';
+  if (size > sizeof(m_joystickData[stick].descriptor.name) - 1) {
+    size = sizeof(m_joystickData[stick].descriptor.name) - 1;
+  }
+  std::strncpy(m_joystickData[stick].descriptor.name, name, size);
+  m_joystickData[stick].descriptor.name[size] = '\0';
   m_joystickDescriptorCallbacks(stick, &m_joystickData[stick].descriptor);
 }
 
@@ -359,19 +366,27 @@
   m_joystickDescriptorCallbacks(stick, &m_joystickData[stick].descriptor);
 }
 
-void DriverStationData::SetGameSpecificMessage(const char* message) {
+void DriverStationData::SetGameSpecificMessage(const char* message,
+                                               size_t size) {
   std::scoped_lock lock(m_matchInfoMutex);
+  if (size > sizeof(m_matchInfo.gameSpecificMessage) - 1) {
+    size = sizeof(m_matchInfo.gameSpecificMessage) - 1;
+  }
   std::strncpy(reinterpret_cast<char*>(m_matchInfo.gameSpecificMessage),
-               message, sizeof(m_matchInfo.gameSpecificMessage) - 1);
-  *(std::end(m_matchInfo.gameSpecificMessage) - 1) = '\0';
-  m_matchInfo.gameSpecificMessageSize = std::strlen(message);
+               message, size);
+  m_matchInfo.gameSpecificMessage[size] = '\0';
+  m_matchInfo.gameSpecificMessageSize =
+      std::strlen(reinterpret_cast<char*>(m_matchInfo.gameSpecificMessage));
   m_matchInfoCallbacks(&m_matchInfo);
 }
 
-void DriverStationData::SetEventName(const char* name) {
+void DriverStationData::SetEventName(const char* name, size_t size) {
   std::scoped_lock lock(m_matchInfoMutex);
-  std::strncpy(m_matchInfo.eventName, name, sizeof(m_matchInfo.eventName) - 1);
-  *(std::end(m_matchInfo.eventName) - 1) = '\0';
+  if (size > sizeof(m_matchInfo.eventName) - 1) {
+    size = sizeof(m_matchInfo.eventName) - 1;
+  }
+  std::strncpy(m_matchInfo.eventName, name, size);
+  m_matchInfo.eventName[size] = '\0';
   m_matchInfoCallbacks(&m_matchInfo);
 }
 
@@ -536,20 +551,20 @@
   SimDriverStationData->SetJoystickType(stick, type);
 }
 
-void HALSIM_SetJoystickName(int32_t stick, const char* name) {
-  SimDriverStationData->SetJoystickName(stick, name);
+void HALSIM_SetJoystickName(int32_t stick, const char* name, size_t size) {
+  SimDriverStationData->SetJoystickName(stick, name, size);
 }
 
 void HALSIM_SetJoystickAxisType(int32_t stick, int32_t axis, int32_t type) {
   SimDriverStationData->SetJoystickAxisType(stick, axis, type);
 }
 
-void HALSIM_SetGameSpecificMessage(const char* message) {
-  SimDriverStationData->SetGameSpecificMessage(message);
+void HALSIM_SetGameSpecificMessage(const char* message, size_t size) {
+  SimDriverStationData->SetGameSpecificMessage(message, size);
 }
 
-void HALSIM_SetEventName(const char* name) {
-  SimDriverStationData->SetEventName(name);
+void HALSIM_SetEventName(const char* name, size_t size) {
+  SimDriverStationData->SetEventName(name, size);
 }
 
 void HALSIM_SetMatchType(HAL_MatchType type) {
diff --git a/hal/src/main/native/sim/mockdata/DriverStationDataInternal.h b/hal/src/main/native/sim/mockdata/DriverStationDataInternal.h
index 2470b04..763b465 100644
--- a/hal/src/main/native/sim/mockdata/DriverStationDataInternal.h
+++ b/hal/src/main/native/sim/mockdata/DriverStationDataInternal.h
@@ -107,11 +107,11 @@
 
   void SetJoystickIsXbox(int32_t stick, HAL_Bool isXbox);
   void SetJoystickType(int32_t stick, int32_t type);
-  void SetJoystickName(int32_t stick, const char* name);
+  void SetJoystickName(int32_t stick, const char* name, size_t size);
   void SetJoystickAxisType(int32_t stick, int32_t axis, int32_t type);
 
-  void SetGameSpecificMessage(const char* message);
-  void SetEventName(const char* name);
+  void SetGameSpecificMessage(const char* message, size_t size);
+  void SetEventName(const char* name, size_t size);
   void SetMatchType(HAL_MatchType type);
   void SetMatchNumber(int32_t matchNumber);
   void SetReplayNumber(int32_t replayNumber);
diff --git a/hal/src/main/native/sim/mockdata/Reset.cpp b/hal/src/main/native/sim/mockdata/Reset.cpp
new file mode 100644
index 0000000..bb83973
--- /dev/null
+++ b/hal/src/main/native/sim/mockdata/Reset.cpp
@@ -0,0 +1,109 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include <hal/simulation/AccelerometerData.h>
+#include <hal/simulation/AddressableLEDData.h>
+#include <hal/simulation/AnalogGyroData.h>
+#include <hal/simulation/AnalogInData.h>
+#include <hal/simulation/AnalogOutData.h>
+#include <hal/simulation/AnalogTriggerData.h>
+#include <hal/simulation/CTREPCMData.h>
+#include <hal/simulation/CanData.h>
+#include <hal/simulation/DIOData.h>
+#include <hal/simulation/DigitalPWMData.h>
+#include <hal/simulation/DriverStationData.h>
+#include <hal/simulation/DutyCycleData.h>
+#include <hal/simulation/EncoderData.h>
+#include <hal/simulation/I2CData.h>
+#include <hal/simulation/PWMData.h>
+#include <hal/simulation/PowerDistributionData.h>
+#include <hal/simulation/REVPHData.h>
+#include <hal/simulation/RelayData.h>
+#include <hal/simulation/RoboRioData.h>
+#include <hal/simulation/SPIAccelerometerData.h>
+#include <hal/simulation/SPIData.h>
+#include <hal/simulation/SimDeviceData.h>
+
+#include "../PortsInternal.h"
+
+extern "C" void HALSIM_ResetAllSimData(void) {
+  for (int32_t i = 0; i < hal::kAccelerometers; i++) {
+    HALSIM_ResetAccelerometerData(i);
+  }
+
+  for (int32_t i = 0; i < hal::kNumAddressableLEDs; i++) {
+    HALSIM_ResetAddressableLEDData(i);
+  }
+
+  for (int32_t i = 0; i < hal::kNumAccumulators; i++) {
+    HALSIM_ResetAnalogGyroData(i);
+  }
+
+  for (int32_t i = 0; i < hal::kNumAnalogInputs; i++) {
+    HALSIM_ResetAnalogInData(i);
+  }
+
+  for (int32_t i = 0; i < hal::kNumAnalogOutputs; i++) {
+    HALSIM_ResetAnalogOutData(i);
+  }
+
+  for (int32_t i = 0; i < hal::kNumAnalogTriggers; i++) {
+    HALSIM_ResetAnalogTriggerData(i);
+  }
+
+  HALSIM_ResetCanData();
+
+  for (int32_t i = 0; i < hal::kNumCTREPCMModules; i++) {
+    HALSIM_ResetCTREPCMData(i);
+  }
+
+  for (int32_t i = 0; i < hal::kNumDigitalPWMOutputs; i++) {
+    HALSIM_ResetDigitalPWMData(i);
+  }
+
+  for (int32_t i = 0; i < hal::kNumDigitalChannels; i++) {
+    HALSIM_ResetDIOData(i);
+  }
+
+  HALSIM_ResetDriverStationData();
+
+  for (int32_t i = 0; i < hal::kNumDutyCycles; i++) {
+    HALSIM_ResetDutyCycleData(i);
+  }
+
+  for (int32_t i = 0; i < hal::kNumEncoders; i++) {
+    HALSIM_ResetEncoderData(i);
+  }
+
+  for (int32_t i = 0; i < hal::kI2CPorts; i++) {
+    HALSIM_ResetI2CData(i);
+  }
+
+  for (int32_t i = 0; i < hal::kNumPDSimModules; i++) {
+    HALSIM_ResetPowerDistributionData(i);
+  }
+
+  for (int32_t i = 0; i < hal::kNumPWMChannels; i++) {
+    HALSIM_ResetPWMData(i);
+  }
+
+  for (int32_t i = 0; i < hal::kNumRelayHeaders; i++) {
+    HALSIM_ResetRelayData(i);
+  }
+
+  for (int32_t i = 0; i < hal::kNumREVPHModules; i++) {
+    HALSIM_ResetREVPHData(i);
+  }
+
+  HALSIM_ResetRoboRioData();
+  HALSIM_ResetSimDeviceData();
+
+  for (int32_t i = 0; i < hal::kSPIAccelerometers; i++) {
+    HALSIM_ResetSPIAccelerometerData(i);
+  }
+
+  for (int32_t i = 0; i < hal::kSPIPorts; i++) {
+    HALSIM_ResetSPIData(i);
+  }
+}
diff --git a/hal/src/main/native/sim/mockdata/RoboRioData.cpp b/hal/src/main/native/sim/mockdata/RoboRioData.cpp
index 6932620..b73b0d9 100644
--- a/hal/src/main/native/sim/mockdata/RoboRioData.cpp
+++ b/hal/src/main/native/sim/mockdata/RoboRioData.cpp
@@ -32,6 +32,79 @@
   userFaults5V.Reset(0);
   userFaults3V3.Reset(0);
   brownoutVoltage.Reset(6.75);
+  m_serialNumber = "";
+  m_comments = "";
+}
+
+int32_t RoboRioData::RegisterSerialNumberCallback(
+    HAL_RoboRioStringCallback callback, void* param, HAL_Bool initialNotify) {
+  std::scoped_lock lock(m_serialNumberMutex);
+  int32_t uid = m_serialNumberCallbacks.Register(callback, param);
+  if (initialNotify) {
+    callback(GetSerialNumberName(), param, m_serialNumber.c_str(),
+             m_serialNumber.size());
+  }
+  return uid;
+}
+
+void RoboRioData::CancelSerialNumberCallback(int32_t uid) {
+  m_serialNumberCallbacks.Cancel(uid);
+}
+
+size_t RoboRioData::GetSerialNumber(char* buffer, size_t size) {
+  std::scoped_lock lock(m_serialNumberMutex);
+  size_t copied = m_serialNumber.copy(buffer, size);
+  // Null terminate
+  if (copied == size) {
+    copied -= 1;
+  }
+  buffer[copied] = '\0';
+  return copied;
+}
+
+void RoboRioData::SetSerialNumber(const char* serialNumber, size_t size) {
+  // Limit serial number to 8 characters internally- serialnum environment
+  // variable is always 8 characters
+  if (size > 8) {
+    size = 8;
+  }
+  std::scoped_lock lock(m_serialNumberMutex);
+  m_serialNumber = std::string(serialNumber, size);
+  m_serialNumberCallbacks(m_serialNumber.c_str(), m_serialNumber.size());
+}
+
+int32_t RoboRioData::RegisterCommentsCallback(
+    HAL_RoboRioStringCallback callback, void* param, HAL_Bool initialNotify) {
+  std::scoped_lock lock(m_commentsMutex);
+  int32_t uid = m_commentsCallbacks.Register(callback, param);
+  if (initialNotify) {
+    callback(GetCommentsName(), param, m_comments.c_str(),
+             m_serialNumber.size());
+  }
+  return uid;
+}
+
+void RoboRioData::CancelCommentsCallback(int32_t uid) {
+  m_commentsCallbacks.Cancel(uid);
+}
+
+size_t RoboRioData::GetComments(char* buffer, size_t size) {
+  std::scoped_lock lock(m_commentsMutex);
+  size_t copied = m_comments.copy(buffer, size);
+  // Null terminate if there is room
+  if (copied < size) {
+    buffer[copied] = '\0';
+  }
+  return copied;
+}
+
+void RoboRioData::SetComments(const char* comments, size_t size) {
+  if (size > 64) {
+    size = 64;
+  }
+  std::scoped_lock lock(m_commentsMutex);
+  m_comments = std::string(comments, size);
+  m_commentsCallbacks(m_comments.c_str(), m_comments.size());
 }
 
 extern "C" {
@@ -60,6 +133,39 @@
 DEFINE_CAPI(int32_t, UserFaults3V3, userFaults3V3)
 DEFINE_CAPI(double, BrownoutVoltage, brownoutVoltage)
 
+int32_t HALSIM_RegisterRoboRioSerialNumberCallback(
+    HAL_RoboRioStringCallback callback, void* param, HAL_Bool initialNotify) {
+  return SimRoboRioData->RegisterSerialNumberCallback(callback, param,
+                                                      initialNotify);
+}
+void HALSIM_CancelRoboRioSerialNumberCallback(int32_t uid) {
+  return SimRoboRioData->CancelSerialNumberCallback(uid);
+}
+size_t HALSIM_GetRoboRioSerialNumber(char* buffer, size_t size) {
+  return SimRoboRioData->GetSerialNumber(buffer, size);
+}
+void HALSIM_SetRoboRioSerialNumber(const char* serialNumber, size_t size) {
+  SimRoboRioData->SetSerialNumber(serialNumber, size);
+}
+
+int32_t HALSIM_RegisterRoboRioCommentsCallback(
+    HAL_RoboRioStringCallback callback, void* param, HAL_Bool initialNotify) {
+  return SimRoboRioData->RegisterCommentsCallback(callback, param,
+                                                  initialNotify);
+}
+void HALSIM_CancelRoboRioCommentsCallback(int32_t uid) {
+  SimRoboRioData->CancelCommentsCallback(uid);
+}
+size_t HALSIM_GetRoboRioComments(char* buffer, size_t size) {
+  return SimRoboRioData->GetComments(buffer, size);
+}
+void HALSIM_SetRoboRioComments(const char* comments, size_t size) {
+  SimRoboRioData->SetComments(comments, size);
+}
+
+void HALSIM_RegisterRoboRioAllCallbacks(HAL_NotifyCallback callback,
+                                        void* param, HAL_Bool initialNotify);
+
 #define REGISTER(NAME) \
   SimRoboRioData->NAME.RegisterCallback(callback, param, initialNotify)
 
diff --git a/hal/src/main/native/sim/mockdata/RoboRioDataInternal.h b/hal/src/main/native/sim/mockdata/RoboRioDataInternal.h
index 99e61ea..c3ff17a 100644
--- a/hal/src/main/native/sim/mockdata/RoboRioDataInternal.h
+++ b/hal/src/main/native/sim/mockdata/RoboRioDataInternal.h
@@ -4,6 +4,11 @@
 
 #pragma once
 
+#include <cstddef>
+#include <string>
+
+#include <wpi/spinlock.h>
+
 #include "hal/simulation/RoboRioData.h"
 #include "hal/simulation/SimDataValue.h"
 
@@ -26,6 +31,9 @@
   HAL_SIMDATAVALUE_DEFINE_NAME(UserFaults3V3)
   HAL_SIMDATAVALUE_DEFINE_NAME(BrownoutVoltage)
 
+  HAL_SIMCALLBACKREGISTRY_DEFINE_NAME(SerialNumber)
+  HAL_SIMCALLBACKREGISTRY_DEFINE_NAME(Comments);
+
  public:
   SimDataValue<HAL_Bool, HAL_MakeBoolean, GetFPGAButtonName> fpgaButton{false};
   SimDataValue<double, HAL_MakeDouble, GetVInVoltageName> vInVoltage{12.0};
@@ -50,7 +58,32 @@
   SimDataValue<double, HAL_MakeDouble, GetBrownoutVoltageName> brownoutVoltage{
       6.75};
 
+  int32_t RegisterSerialNumberCallback(HAL_RoboRioStringCallback callback,
+                                       void* param, HAL_Bool initialNotify);
+  void CancelSerialNumberCallback(int32_t uid);
+  size_t GetSerialNumber(char* buffer, size_t size);
+  void SetSerialNumber(const char* serialNumber, size_t size);
+
+  int32_t RegisterCommentsCallback(HAL_RoboRioStringCallback callback,
+                                   void* param, HAL_Bool initialNotify);
+  void CancelCommentsCallback(int32_t uid);
+  size_t GetComments(char* buffer, size_t size);
+  void SetComments(const char* comments, size_t size);
+
   virtual void ResetData();
+
+ private:
+  wpi::spinlock m_serialNumberMutex;
+  std::string m_serialNumber;
+
+  wpi::spinlock m_commentsMutex;
+  std::string m_comments;
+
+  SimCallbackRegistry<HAL_RoboRioStringCallback, GetSerialNumberName>
+      m_serialNumberCallbacks;
+
+  SimCallbackRegistry<HAL_RoboRioStringCallback, GetCommentsName>
+      m_commentsCallbacks;
 };
 extern RoboRioData* SimRoboRioData;
 }  // namespace hal
diff --git a/hal/src/test/java/edu/wpi/first/hal/JNITest.java b/hal/src/test/java/edu/wpi/first/hal/JNITest.java
new file mode 100644
index 0000000..49e201d
--- /dev/null
+++ b/hal/src/test/java/edu/wpi/first/hal/JNITest.java
@@ -0,0 +1,15 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.hal;
+
+import org.junit.jupiter.api.Test;
+
+public class JNITest {
+  @Test
+  void jniLinkTest() {
+    // Test to verify that the JNI test link works correctly.
+    HAL.initialize(500, 0);
+  }
+}
diff --git a/hal/src/test/native/cpp/mockdata/DriverStationDataTest.cpp b/hal/src/test/native/cpp/mockdata/DriverStationDataTest.cpp
index 4925e16..59658cb 100644
--- a/hal/src/test/native/cpp/mockdata/DriverStationDataTest.cpp
+++ b/hal/src/test/native/cpp/mockdata/DriverStationDataTest.cpp
@@ -61,6 +61,9 @@
   HALSIM_SetJoystickPOVs(joystickUnderTest, &set_povs);
   HALSIM_SetJoystickButtons(joystickUnderTest, &set_buttons);
 
+  HALSIM_NotifyDriverStationNewData();
+  HAL_RefreshDSData();
+
   // Check the set values
   HAL_GetJoystickAxes(joystickUnderTest, &axes);
   HAL_GetJoystickPOVs(joystickUnderTest, &povs);
@@ -89,6 +92,9 @@
 
   // Reset
   HALSIM_ResetDriverStationData();
+  HALSIM_NotifyDriverStationNewData();
+  HAL_RefreshDSData();
+
   for (int joystickNum = 0; joystickNum < 6; ++joystickNum) {
     HAL_GetJoystickAxes(joystickNum, &axes);
     HAL_GetJoystickPOVs(joystickNum, &povs);