blob: 93e280efc6fa2ddabace5165074d53c0bfacb93c [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) {
57 std::scoped_lock lock(digitalI2COnBoardMutex);
58 i2COnboardObjCount++;
Austin Schuh812d0d12021-11-04 20:16:48 -070059 if (i2COnboardObjCount > 1) {
60 return;
61 }
Brian Silverman8fce7482020-01-05 13:18:21 -080062 int handle = open("/dev/i2c-2", O_RDWR);
63 if (handle < 0) {
Austin Schuh812d0d12021-11-04 20:16:48 -070064 fmt::print("Failed to open onboard i2c bus: {}\n", std::strerror(errno));
Brian Silverman8fce7482020-01-05 13:18:21 -080065 return;
66 }
67 i2COnBoardHandle = handle;
68 } else {
69 std::scoped_lock lock(digitalI2CMXPMutex);
70 i2CMXPObjCount++;
Austin Schuh812d0d12021-11-04 20:16:48 -070071 if (i2CMXPObjCount > 1) {
72 return;
73 }
Brian Silverman8fce7482020-01-05 13:18:21 -080074 if ((i2CMXPDigitalHandle1 = HAL_InitializeDIOPort(
Austin Schuh812d0d12021-11-04 20:16:48 -070075 HAL_GetPort(24), false, nullptr, status)) == HAL_kInvalidHandle) {
Brian Silverman8fce7482020-01-05 13:18:21 -080076 return;
77 }
78 if ((i2CMXPDigitalHandle2 = HAL_InitializeDIOPort(
Austin Schuh812d0d12021-11-04 20:16:48 -070079 HAL_GetPort(25), false, nullptr, status)) == HAL_kInvalidHandle) {
Brian Silverman8fce7482020-01-05 13:18:21 -080080 HAL_FreeDIOPort(i2CMXPDigitalHandle1); // free the first port allocated
81 return;
82 }
83 digitalSystem->writeEnableMXPSpecialFunction(
84 digitalSystem->readEnableMXPSpecialFunction(status) | 0xC000, status);
85 int handle = open("/dev/i2c-1", O_RDWR);
86 if (handle < 0) {
Austin Schuh812d0d12021-11-04 20:16:48 -070087 fmt::print("Failed to open MXP i2c bus: {}\n", std::strerror(errno));
Brian Silverman8fce7482020-01-05 13:18:21 -080088 return;
89 }
90 i2CMXPHandle = handle;
91 }
92}
93
94int32_t HAL_TransactionI2C(HAL_I2CPort port, int32_t deviceAddress,
95 const uint8_t* dataToSend, int32_t sendSize,
96 uint8_t* dataReceived, int32_t receiveSize) {
97 if (port < 0 || port > 1) {
Austin Schuh812d0d12021-11-04 20:16:48 -070098 int32_t status = 0;
99 hal::SetLastErrorIndexOutOfRange(&status, "Invalid Index for I2C", 0, 1,
100 port);
Brian Silverman8fce7482020-01-05 13:18:21 -0800101 return -1;
102 }
103
104 struct i2c_msg msgs[2];
105 msgs[0].addr = deviceAddress;
106 msgs[0].flags = 0;
107 msgs[0].len = sendSize;
108 msgs[0].buf = const_cast<uint8_t*>(dataToSend);
109 msgs[1].addr = deviceAddress;
110 msgs[1].flags = I2C_M_RD;
111 msgs[1].len = receiveSize;
112 msgs[1].buf = dataReceived;
113
114 struct i2c_rdwr_ioctl_data rdwr;
115 rdwr.msgs = msgs;
116 rdwr.nmsgs = 2;
117
118 if (port == HAL_I2C_kOnboard) {
119 std::scoped_lock lock(digitalI2COnBoardMutex);
120 return ioctl(i2COnBoardHandle, I2C_RDWR, &rdwr);
121 } else {
122 std::scoped_lock lock(digitalI2CMXPMutex);
123 return ioctl(i2CMXPHandle, I2C_RDWR, &rdwr);
124 }
125}
126
127int32_t HAL_WriteI2C(HAL_I2CPort port, int32_t deviceAddress,
128 const uint8_t* dataToSend, int32_t sendSize) {
129 if (port < 0 || port > 1) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700130 int32_t status = 0;
131 hal::SetLastErrorIndexOutOfRange(&status, "Invalid Index for I2C", 0, 1,
132 port);
Brian Silverman8fce7482020-01-05 13:18:21 -0800133 return -1;
134 }
135
136 struct i2c_msg msg;
137 msg.addr = deviceAddress;
138 msg.flags = 0;
139 msg.len = sendSize;
140 msg.buf = const_cast<uint8_t*>(dataToSend);
141
142 struct i2c_rdwr_ioctl_data rdwr;
143 rdwr.msgs = &msg;
144 rdwr.nmsgs = 1;
145
146 if (port == HAL_I2C_kOnboard) {
147 std::scoped_lock lock(digitalI2COnBoardMutex);
148 return ioctl(i2COnBoardHandle, I2C_RDWR, &rdwr);
149 } else {
150 std::scoped_lock lock(digitalI2CMXPMutex);
151 return ioctl(i2CMXPHandle, I2C_RDWR, &rdwr);
152 }
153}
154
155int32_t HAL_ReadI2C(HAL_I2CPort port, int32_t deviceAddress, uint8_t* buffer,
156 int32_t count) {
157 if (port < 0 || port > 1) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700158 int32_t status = 0;
159 hal::SetLastErrorIndexOutOfRange(&status, "Invalid Index for I2C", 0, 1,
160 port);
Brian Silverman8fce7482020-01-05 13:18:21 -0800161 return -1;
162 }
163
164 struct i2c_msg msg;
165 msg.addr = deviceAddress;
166 msg.flags = I2C_M_RD;
167 msg.len = count;
168 msg.buf = buffer;
169
170 struct i2c_rdwr_ioctl_data rdwr;
171 rdwr.msgs = &msg;
172 rdwr.nmsgs = 1;
173
174 if (port == HAL_I2C_kOnboard) {
175 std::scoped_lock lock(digitalI2COnBoardMutex);
176 return ioctl(i2COnBoardHandle, I2C_RDWR, &rdwr);
177 } else {
178 std::scoped_lock lock(digitalI2CMXPMutex);
179 return ioctl(i2CMXPHandle, I2C_RDWR, &rdwr);
180 }
181}
182
183void HAL_CloseI2C(HAL_I2CPort port) {
184 if (port < 0 || port > 1) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700185 int32_t status = 0;
186 hal::SetLastErrorIndexOutOfRange(&status, "Invalid Index for I2C", 0, 1,
187 port);
Brian Silverman8fce7482020-01-05 13:18:21 -0800188 return;
189 }
190
191 if (port == HAL_I2C_kOnboard) {
192 std::scoped_lock lock(digitalI2COnBoardMutex);
193 if (i2COnboardObjCount-- == 0) {
194 close(i2COnBoardHandle);
195 }
196 } else {
197 std::scoped_lock lock(digitalI2CMXPMutex);
198 if (i2CMXPObjCount-- == 0) {
199 close(i2CMXPHandle);
200 }
201 HAL_FreeDIOPort(i2CMXPDigitalHandle1);
202 HAL_FreeDIOPort(i2CMXPDigitalHandle2);
203 }
204}
205
206} // extern "C"