blob: 479f6463cadfeb4c3985789fc9c42da3a2a3d5e3 [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/I2C.h"
6
7#include <fcntl.h>
8#include <linux/i2c-dev.h>
9#include <linux/i2c.h>
10#include <sys/ioctl.h>
11#include <unistd.h>
12
13#include <cstring>
14
Austin Schuh812d0d12021-11-04 20:16:48 -070015#include <fmt/format.h>
16
Brian Silverman8fce7482020-01-05 13:18:21 -080017#include "DigitalInternal.h"
18#include "HALInitializer.h"
Austin Schuh812d0d12021-11-04 20:16:48 -070019#include "HALInternal.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080020#include "hal/DIO.h"
21#include "hal/HAL.h"
22
23using namespace hal;
24
25static wpi::mutex digitalI2COnBoardMutex;
26static wpi::mutex digitalI2CMXPMutex;
27
28static uint8_t i2COnboardObjCount{0};
29static uint8_t i2CMXPObjCount{0};
30static int i2COnBoardHandle{-1};
31static int i2CMXPHandle{-1};
32
33static HAL_DigitalHandle i2CMXPDigitalHandle1{HAL_kInvalidHandle};
34static HAL_DigitalHandle i2CMXPDigitalHandle2{HAL_kInvalidHandle};
35
Austin Schuh812d0d12021-11-04 20:16:48 -070036namespace hal::init {
Brian Silverman8fce7482020-01-05 13:18:21 -080037void InitializeI2C() {}
Austin Schuh812d0d12021-11-04 20:16:48 -070038} // namespace hal::init
Brian Silverman8fce7482020-01-05 13:18:21 -080039
40extern "C" {
41
42void HAL_InitializeI2C(HAL_I2CPort port, int32_t* status) {
43 hal::init::CheckInit();
44 initializeDigital(status);
Austin Schuh812d0d12021-11-04 20:16:48 -070045 if (*status != 0) {
46 return;
47 }
Brian Silverman8fce7482020-01-05 13:18:21 -080048
49 if (port < 0 || port > 1) {
Austin Schuh812d0d12021-11-04 20:16:48 -070050 *status = RESOURCE_OUT_OF_RANGE;
51 hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for I2C", 0, 1,
52 port);
Brian Silverman8fce7482020-01-05 13:18:21 -080053 return;
54 }
55
56 if (port == HAL_I2C_kOnboard) {
Austin Schuh75263e32022-02-22 18:05:32 -080057 HAL_SendError(0, 0, 0,
58 "Onboard I2C port is subject to system lockups. See Known "
59 "Issues page for details",
60 "", "", true);
Brian Silverman8fce7482020-01-05 13:18:21 -080061 std::scoped_lock lock(digitalI2COnBoardMutex);
62 i2COnboardObjCount++;
Austin Schuh812d0d12021-11-04 20:16:48 -070063 if (i2COnboardObjCount > 1) {
64 return;
65 }
Brian Silverman8fce7482020-01-05 13:18:21 -080066 int handle = open("/dev/i2c-2", O_RDWR);
67 if (handle < 0) {
Austin Schuh812d0d12021-11-04 20:16:48 -070068 fmt::print("Failed to open onboard i2c bus: {}\n", std::strerror(errno));
Brian Silverman8fce7482020-01-05 13:18:21 -080069 return;
70 }
71 i2COnBoardHandle = handle;
72 } else {
73 std::scoped_lock lock(digitalI2CMXPMutex);
74 i2CMXPObjCount++;
Austin Schuh812d0d12021-11-04 20:16:48 -070075 if (i2CMXPObjCount > 1) {
76 return;
77 }
Brian Silverman8fce7482020-01-05 13:18:21 -080078 if ((i2CMXPDigitalHandle1 = HAL_InitializeDIOPort(
Austin Schuh812d0d12021-11-04 20:16:48 -070079 HAL_GetPort(24), false, nullptr, status)) == HAL_kInvalidHandle) {
Brian Silverman8fce7482020-01-05 13:18:21 -080080 return;
81 }
82 if ((i2CMXPDigitalHandle2 = HAL_InitializeDIOPort(
Austin Schuh812d0d12021-11-04 20:16:48 -070083 HAL_GetPort(25), false, nullptr, status)) == HAL_kInvalidHandle) {
Brian Silverman8fce7482020-01-05 13:18:21 -080084 HAL_FreeDIOPort(i2CMXPDigitalHandle1); // free the first port allocated
85 return;
86 }
87 digitalSystem->writeEnableMXPSpecialFunction(
88 digitalSystem->readEnableMXPSpecialFunction(status) | 0xC000, status);
89 int handle = open("/dev/i2c-1", O_RDWR);
90 if (handle < 0) {
Austin Schuh812d0d12021-11-04 20:16:48 -070091 fmt::print("Failed to open MXP i2c bus: {}\n", std::strerror(errno));
Brian Silverman8fce7482020-01-05 13:18:21 -080092 return;
93 }
94 i2CMXPHandle = handle;
95 }
96}
97
98int32_t HAL_TransactionI2C(HAL_I2CPort port, int32_t deviceAddress,
99 const uint8_t* dataToSend, int32_t sendSize,
100 uint8_t* dataReceived, int32_t receiveSize) {
101 if (port < 0 || port > 1) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700102 int32_t status = 0;
103 hal::SetLastErrorIndexOutOfRange(&status, "Invalid Index for I2C", 0, 1,
104 port);
Brian Silverman8fce7482020-01-05 13:18:21 -0800105 return -1;
106 }
107
108 struct i2c_msg msgs[2];
109 msgs[0].addr = deviceAddress;
110 msgs[0].flags = 0;
111 msgs[0].len = sendSize;
112 msgs[0].buf = const_cast<uint8_t*>(dataToSend);
113 msgs[1].addr = deviceAddress;
114 msgs[1].flags = I2C_M_RD;
115 msgs[1].len = receiveSize;
116 msgs[1].buf = dataReceived;
117
118 struct i2c_rdwr_ioctl_data rdwr;
119 rdwr.msgs = msgs;
120 rdwr.nmsgs = 2;
121
122 if (port == HAL_I2C_kOnboard) {
123 std::scoped_lock lock(digitalI2COnBoardMutex);
124 return ioctl(i2COnBoardHandle, I2C_RDWR, &rdwr);
125 } else {
126 std::scoped_lock lock(digitalI2CMXPMutex);
127 return ioctl(i2CMXPHandle, I2C_RDWR, &rdwr);
128 }
129}
130
131int32_t HAL_WriteI2C(HAL_I2CPort port, int32_t deviceAddress,
132 const uint8_t* dataToSend, int32_t sendSize) {
133 if (port < 0 || port > 1) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700134 int32_t status = 0;
135 hal::SetLastErrorIndexOutOfRange(&status, "Invalid Index for I2C", 0, 1,
136 port);
Brian Silverman8fce7482020-01-05 13:18:21 -0800137 return -1;
138 }
139
140 struct i2c_msg msg;
141 msg.addr = deviceAddress;
142 msg.flags = 0;
143 msg.len = sendSize;
144 msg.buf = const_cast<uint8_t*>(dataToSend);
145
146 struct i2c_rdwr_ioctl_data rdwr;
147 rdwr.msgs = &msg;
148 rdwr.nmsgs = 1;
149
150 if (port == HAL_I2C_kOnboard) {
151 std::scoped_lock lock(digitalI2COnBoardMutex);
152 return ioctl(i2COnBoardHandle, I2C_RDWR, &rdwr);
153 } else {
154 std::scoped_lock lock(digitalI2CMXPMutex);
155 return ioctl(i2CMXPHandle, I2C_RDWR, &rdwr);
156 }
157}
158
159int32_t HAL_ReadI2C(HAL_I2CPort port, int32_t deviceAddress, uint8_t* buffer,
160 int32_t count) {
161 if (port < 0 || port > 1) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700162 int32_t status = 0;
163 hal::SetLastErrorIndexOutOfRange(&status, "Invalid Index for I2C", 0, 1,
164 port);
Brian Silverman8fce7482020-01-05 13:18:21 -0800165 return -1;
166 }
167
168 struct i2c_msg msg;
169 msg.addr = deviceAddress;
170 msg.flags = I2C_M_RD;
171 msg.len = count;
172 msg.buf = buffer;
173
174 struct i2c_rdwr_ioctl_data rdwr;
175 rdwr.msgs = &msg;
176 rdwr.nmsgs = 1;
177
178 if (port == HAL_I2C_kOnboard) {
179 std::scoped_lock lock(digitalI2COnBoardMutex);
180 return ioctl(i2COnBoardHandle, I2C_RDWR, &rdwr);
181 } else {
182 std::scoped_lock lock(digitalI2CMXPMutex);
183 return ioctl(i2CMXPHandle, I2C_RDWR, &rdwr);
184 }
185}
186
187void HAL_CloseI2C(HAL_I2CPort port) {
188 if (port < 0 || port > 1) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700189 int32_t status = 0;
190 hal::SetLastErrorIndexOutOfRange(&status, "Invalid Index for I2C", 0, 1,
191 port);
Brian Silverman8fce7482020-01-05 13:18:21 -0800192 return;
193 }
194
195 if (port == HAL_I2C_kOnboard) {
196 std::scoped_lock lock(digitalI2COnBoardMutex);
197 if (i2COnboardObjCount-- == 0) {
198 close(i2COnBoardHandle);
199 }
200 } else {
201 std::scoped_lock lock(digitalI2CMXPMutex);
202 if (i2CMXPObjCount-- == 0) {
203 close(i2CMXPHandle);
204 }
205 HAL_FreeDIOPort(i2CMXPDigitalHandle1);
206 HAL_FreeDIOPort(i2CMXPDigitalHandle2);
207 }
208}
209
210} // extern "C"