blob: b72e25eff13b8ecb581007e5987efbee3ccba973 [file] [log] [blame]
Brian Silverman8fce7482020-01-05 13:18:21 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) 2016-2019 FIRST. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "hal/I2C.h"
9
10#include <fcntl.h>
11#include <linux/i2c-dev.h>
12#include <linux/i2c.h>
13#include <sys/ioctl.h>
14#include <unistd.h>
15
16#include <cstring>
17
18#include "DigitalInternal.h"
19#include "HALInitializer.h"
20#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
36namespace hal {
37namespace init {
38void InitializeI2C() {}
39} // namespace init
40} // namespace hal
41
42extern "C" {
43
44void HAL_InitializeI2C(HAL_I2CPort port, int32_t* status) {
45 hal::init::CheckInit();
46 initializeDigital(status);
47 if (*status != 0) return;
48
49 if (port < 0 || port > 1) {
50 // Set port out of range error here
51 return;
52 }
53
54 if (port == HAL_I2C_kOnboard) {
55 std::scoped_lock lock(digitalI2COnBoardMutex);
56 i2COnboardObjCount++;
57 if (i2COnboardObjCount > 1) return;
58 int handle = open("/dev/i2c-2", O_RDWR);
59 if (handle < 0) {
60 std::printf("Failed to open onboard i2c bus: %s\n", std::strerror(errno));
61 return;
62 }
63 i2COnBoardHandle = handle;
64 } else {
65 std::scoped_lock lock(digitalI2CMXPMutex);
66 i2CMXPObjCount++;
67 if (i2CMXPObjCount > 1) return;
68 if ((i2CMXPDigitalHandle1 = HAL_InitializeDIOPort(
69 HAL_GetPort(24), false, status)) == HAL_kInvalidHandle) {
70 return;
71 }
72 if ((i2CMXPDigitalHandle2 = HAL_InitializeDIOPort(
73 HAL_GetPort(25), false, status)) == HAL_kInvalidHandle) {
74 HAL_FreeDIOPort(i2CMXPDigitalHandle1); // free the first port allocated
75 return;
76 }
77 digitalSystem->writeEnableMXPSpecialFunction(
78 digitalSystem->readEnableMXPSpecialFunction(status) | 0xC000, status);
79 int handle = open("/dev/i2c-1", O_RDWR);
80 if (handle < 0) {
81 std::printf("Failed to open MXP i2c bus: %s\n", std::strerror(errno));
82 return;
83 }
84 i2CMXPHandle = handle;
85 }
86}
87
88int32_t HAL_TransactionI2C(HAL_I2CPort port, int32_t deviceAddress,
89 const uint8_t* dataToSend, int32_t sendSize,
90 uint8_t* dataReceived, int32_t receiveSize) {
91 if (port < 0 || port > 1) {
92 // Set port out of range error here
93 return -1;
94 }
95
96 struct i2c_msg msgs[2];
97 msgs[0].addr = deviceAddress;
98 msgs[0].flags = 0;
99 msgs[0].len = sendSize;
100 msgs[0].buf = const_cast<uint8_t*>(dataToSend);
101 msgs[1].addr = deviceAddress;
102 msgs[1].flags = I2C_M_RD;
103 msgs[1].len = receiveSize;
104 msgs[1].buf = dataReceived;
105
106 struct i2c_rdwr_ioctl_data rdwr;
107 rdwr.msgs = msgs;
108 rdwr.nmsgs = 2;
109
110 if (port == HAL_I2C_kOnboard) {
111 std::scoped_lock lock(digitalI2COnBoardMutex);
112 return ioctl(i2COnBoardHandle, I2C_RDWR, &rdwr);
113 } else {
114 std::scoped_lock lock(digitalI2CMXPMutex);
115 return ioctl(i2CMXPHandle, I2C_RDWR, &rdwr);
116 }
117}
118
119int32_t HAL_WriteI2C(HAL_I2CPort port, int32_t deviceAddress,
120 const uint8_t* dataToSend, int32_t sendSize) {
121 if (port < 0 || port > 1) {
122 // Set port out of range error here
123 return -1;
124 }
125
126 struct i2c_msg msg;
127 msg.addr = deviceAddress;
128 msg.flags = 0;
129 msg.len = sendSize;
130 msg.buf = const_cast<uint8_t*>(dataToSend);
131
132 struct i2c_rdwr_ioctl_data rdwr;
133 rdwr.msgs = &msg;
134 rdwr.nmsgs = 1;
135
136 if (port == HAL_I2C_kOnboard) {
137 std::scoped_lock lock(digitalI2COnBoardMutex);
138 return ioctl(i2COnBoardHandle, I2C_RDWR, &rdwr);
139 } else {
140 std::scoped_lock lock(digitalI2CMXPMutex);
141 return ioctl(i2CMXPHandle, I2C_RDWR, &rdwr);
142 }
143}
144
145int32_t HAL_ReadI2C(HAL_I2CPort port, int32_t deviceAddress, uint8_t* buffer,
146 int32_t count) {
147 if (port < 0 || port > 1) {
148 // Set port out of range error here
149 return -1;
150 }
151
152 struct i2c_msg msg;
153 msg.addr = deviceAddress;
154 msg.flags = I2C_M_RD;
155 msg.len = count;
156 msg.buf = buffer;
157
158 struct i2c_rdwr_ioctl_data rdwr;
159 rdwr.msgs = &msg;
160 rdwr.nmsgs = 1;
161
162 if (port == HAL_I2C_kOnboard) {
163 std::scoped_lock lock(digitalI2COnBoardMutex);
164 return ioctl(i2COnBoardHandle, I2C_RDWR, &rdwr);
165 } else {
166 std::scoped_lock lock(digitalI2CMXPMutex);
167 return ioctl(i2CMXPHandle, I2C_RDWR, &rdwr);
168 }
169}
170
171void HAL_CloseI2C(HAL_I2CPort port) {
172 if (port < 0 || port > 1) {
173 // Set port out of range error here
174 return;
175 }
176
177 if (port == HAL_I2C_kOnboard) {
178 std::scoped_lock lock(digitalI2COnBoardMutex);
179 if (i2COnboardObjCount-- == 0) {
180 close(i2COnBoardHandle);
181 }
182 } else {
183 std::scoped_lock lock(digitalI2CMXPMutex);
184 if (i2CMXPObjCount-- == 0) {
185 close(i2CMXPHandle);
186 }
187 HAL_FreeDIOPort(i2CMXPDigitalHandle1);
188 HAL_FreeDIOPort(i2CMXPDigitalHandle2);
189 }
190}
191
192} // extern "C"