blob: a01dd9d52b99b7758f9e631e7e48652e3a67669a [file] [log] [blame]
Austin Schuh812d0d12021-11-04 20:16:48 -07001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
Brian Silverman8fce7482020-01-05 13:18:21 -08004
5#include "hal/Relay.h"
6
Austin Schuh812d0d12021-11-04 20:16:48 -07007#include <string>
8
Brian Silverman8fce7482020-01-05 13:18:21 -08009#include "DigitalInternal.h"
10#include "HALInitializer.h"
Austin Schuh812d0d12021-11-04 20:16:48 -070011#include "HALInternal.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080012#include "PortsInternal.h"
13#include "hal/handles/IndexedHandleResource.h"
14
15using namespace hal;
16
17namespace {
18
19struct Relay {
20 uint8_t channel;
21 bool fwd;
Austin Schuh812d0d12021-11-04 20:16:48 -070022 std::string previousAllocation;
Brian Silverman8fce7482020-01-05 13:18:21 -080023};
24
25} // namespace
26
27static IndexedHandleResource<HAL_RelayHandle, Relay, kNumRelayChannels,
28 HAL_HandleEnum::Relay>* relayHandles;
29
30// Create a mutex to protect changes to the relay values
31static wpi::mutex digitalRelayMutex;
32
Austin Schuh812d0d12021-11-04 20:16:48 -070033namespace hal::init {
Brian Silverman8fce7482020-01-05 13:18:21 -080034void InitializeRelay() {
35 static IndexedHandleResource<HAL_RelayHandle, Relay, kNumRelayChannels,
36 HAL_HandleEnum::Relay>
37 rH;
38 relayHandles = &rH;
39}
Austin Schuh812d0d12021-11-04 20:16:48 -070040} // namespace hal::init
Brian Silverman8fce7482020-01-05 13:18:21 -080041
42extern "C" {
43
44HAL_RelayHandle HAL_InitializeRelayPort(HAL_PortHandle portHandle, HAL_Bool fwd,
Austin Schuh812d0d12021-11-04 20:16:48 -070045 const char* allocationLocation,
Brian Silverman8fce7482020-01-05 13:18:21 -080046 int32_t* status) {
47 hal::init::CheckInit();
48 initializeDigital(status);
49
Austin Schuh812d0d12021-11-04 20:16:48 -070050 if (*status != 0) {
Brian Silverman8fce7482020-01-05 13:18:21 -080051 return HAL_kInvalidHandle;
52 }
53
Austin Schuh812d0d12021-11-04 20:16:48 -070054 int16_t channel = getPortHandleChannel(portHandle);
55 if (channel == InvalidHandleIndex || channel >= kNumRelayChannels) {
56 *status = RESOURCE_OUT_OF_RANGE;
57 hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for Relay", 0,
58 kNumRelayChannels, channel);
Brian Silverman8fce7482020-01-05 13:18:21 -080059 return HAL_kInvalidHandle;
60 }
61
62 if (!fwd) {
Austin Schuh812d0d12021-11-04 20:16:48 -070063 channel += kNumRelayHeaders; // add 4 to reverse channels
64 }
65
66 HAL_RelayHandle handle;
67 auto port = relayHandles->Allocate(channel, &handle, status);
68
69 if (*status != 0) {
70 if (port) {
71 hal::SetLastErrorPreviouslyAllocated(status, "Relay", channel,
72 port->previousAllocation);
73 } else {
74 hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for Relay", 0,
75 kNumRelayChannels, channel);
76 }
77 return HAL_kInvalidHandle; // failed to allocate. Pass error back.
78 }
79
80 if (!fwd) {
Brian Silverman8fce7482020-01-05 13:18:21 -080081 // Subtract number of headers to put channel in range
82 channel -= kNumRelayHeaders;
83
84 port->fwd = false; // set to reverse
85 } else {
86 port->fwd = true; // set to forward
87 }
88
89 port->channel = static_cast<uint8_t>(channel);
Austin Schuh812d0d12021-11-04 20:16:48 -070090 port->previousAllocation = allocationLocation ? allocationLocation : "";
Brian Silverman8fce7482020-01-05 13:18:21 -080091 return handle;
92}
93
94void HAL_FreeRelayPort(HAL_RelayHandle relayPortHandle) {
95 // no status, so no need to check for a proper free.
96 relayHandles->Free(relayPortHandle);
97}
98
99HAL_Bool HAL_CheckRelayChannel(int32_t channel) {
100 // roboRIO only has 4 headers, and the FPGA has
Austin Schuh1e69f942020-11-14 15:06:14 -0800101 // separate functions for forward and reverse,
102 // instead of separate channel IDs
Brian Silverman8fce7482020-01-05 13:18:21 -0800103 return channel < kNumRelayHeaders && channel >= 0;
104}
105
106void HAL_SetRelay(HAL_RelayHandle relayPortHandle, HAL_Bool on,
107 int32_t* status) {
108 auto port = relayHandles->Get(relayPortHandle);
109 if (port == nullptr) {
110 *status = HAL_HANDLE_ERROR;
111 return;
112 }
113 std::scoped_lock lock(digitalRelayMutex);
114 uint8_t relays = 0;
115 if (port->fwd) {
116 relays = relaySystem->readValue_Forward(status);
117 } else {
118 relays = relaySystem->readValue_Reverse(status);
119 }
120
Austin Schuh812d0d12021-11-04 20:16:48 -0700121 if (*status != 0) {
122 return; // bad status read
123 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800124
125 if (on) {
126 relays |= 1 << port->channel;
127 } else {
128 relays &= ~(1 << port->channel);
129 }
130
131 if (port->fwd) {
132 relaySystem->writeValue_Forward(relays, status);
133 } else {
134 relaySystem->writeValue_Reverse(relays, status);
135 }
136}
137
138HAL_Bool HAL_GetRelay(HAL_RelayHandle relayPortHandle, int32_t* status) {
139 auto port = relayHandles->Get(relayPortHandle);
140 if (port == nullptr) {
141 *status = HAL_HANDLE_ERROR;
142 return false;
143 }
144
145 uint8_t relays = 0;
146 if (port->fwd) {
147 relays = relaySystem->readValue_Forward(status);
148 } else {
149 relays = relaySystem->readValue_Reverse(status);
150 }
151
152 return (relays & (1 << port->channel)) != 0;
153}
154
155} // extern "C"