GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 84.2% 283 / 0 / 336
Functions: 88.0% 44 / 0 / 50
Branches: 72.4% 113 / 0 / 156
Decisions: 76.2% 64 / - / 84

firmware/controllers/system/efi_gpio.cpp
Line Branch Decision Exec Source
1 /**
2 * @file efi_gpio.cpp
3 * @brief EFI-related GPIO code
4 *
5 * @date Sep 26, 2014
6 * @author Andrey Belomutskiy, (c) 2012-2020
7 */
8
9 #include "pch.h"
10 #include "bench_test.h"
11 #include "engine_sniffer.h"
12
13 #include "drivers/gpio/gpio_ext.h"
14
15 #if HW_HELLEN
16 #include "hellen_all_meta.h"
17 #endif // HW_HELLEN
18
19 #if EFI_ELECTRONIC_THROTTLE_BODY
20 #include "electronic_throttle.h"
21 #endif /* EFI_ELECTRONIC_THROTTLE_BODY */
22
23 // todo: clean this mess, this should become 'static'/private
24 EnginePins enginePins;
25
26 static const char* const sparkNames[] = { "Coil 1", "Coil 2", "Coil 3", "Coil 4", "Coil 5", "Coil 6", "Coil 7", "Coil 8",
27 "Coil 9", "Coil 10", "Coil 11", "Coil 12"};
28
29 static const char* const trailNames[] = { "Trail 1", "Trail 2", "Trail 3", "Trail 4", "Trail 5", "Trail 6", "Trail 7", "Trail 8",
30 "Trail 9", "Trail 10", "Trail 11", "Trail 12"};
31
32 static const char* const trailShortNames[] = { "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "rA", "rB", "rD" };
33
34 const char *vvtNames[] = {
35 "VVT1",
36 "VVT2",
37 "VVT3",
38 "VVT4"};
39
40 const char *laNames[] = {
41 "input1",
42 "input2",
43 "input3",
44 "input4"};
45
46 // these short names are part of engine sniffer protocol
47 static const char* const sparkShortNames[] = { PROTOCOL_COIL_SHORT_PREFIX "1", PROTOCOL_COIL_SHORT_PREFIX "2", "c3", "c4", "c5", "c6", "c7", "c8",
48 "c9", "cA", "cB", "cD"};
49
50 static const char* const injectorNames[] = { "Injector 1", "Injector 2", "Injector 3", "Injector 4", "Injector 5", "Injector 6",
51 "Injector 7", "Injector 8", "Injector 9", "Injector 10", "Injector 11", "Injector 12"};
52
53 static const char* const injectorShortNames[] = { PROTOCOL_INJ_SHORT_PREFIX "1", PROTOCOL_INJ_SHORT_PREFIX "2", "i3", "i4", "i5", "i6", "i7", "i8",
54 "i9", "iA", "iB", "iC"};
55
56 static const char* const injectorStage2Names[] = { "Injector Second Stage 1", "Injector Second Stage 2", "Injector Second Stage 3", "Injector Second Stage 4", "Injector Second Stage 5", "Injector Second Stage 6",
57 "Injector Second Stage 7", "Injector Second Stage 8", "Injector Second Stage 9", "Injector Second Stage 10", "Injector Second Stage 11", "Injector Second Stage 12"};
58
59 static const char* const injectorStage2ShortNames[] = { PROTOCOL_INJ_STAGE2_SHORT_PREFIX "1", PROTOCOL_INJ_STAGE2_SHORT_PREFIX "2", "j3", "j4", "j5", "j6", "j7", "j8",
60 "j9", "jA", "jB", "jC"};
61
62 static const char* const auxValveShortNames[] = { "a1", "a2"};
63
64 static RegisteredOutputPin * registeredOutputHead = nullptr;
65
66 1 RegisteredNamedOutputPin::RegisteredNamedOutputPin(const char *p_name, size_t pinOffset,
67 1 size_t pinModeOffset) : RegisteredOutputPin(p_name, pinOffset, pinModeOffset) {
68 1 }
69
70 RegisteredNamedOutputPin::RegisteredNamedOutputPin(const char *p_name, size_t pinOffset) :
71 RegisteredOutputPin(p_name, pinOffset) {
72 }
73
74 17 RegisteredOutputPin::RegisteredOutputPin(const char *p_registrationName, size_t pinOffset,
75 16 size_t pinModeOffset)
76 17 : next(registeredOutputHead)
77 17 , registrationName(p_registrationName)
78 17 , m_pinOffset(static_cast<uint16_t>(pinOffset))
79 17 , m_hasPinMode(true)
80 17 , m_pinModeOffset(static_cast<uint16_t>(pinModeOffset))
81 {
82 // adding into head of the list is so easy and since we do not care about order that's what we shall do
83 17 registeredOutputHead = this;
84 17 }
85
86 1 RegisteredOutputPin::RegisteredOutputPin(const char *p_registrationName, size_t pinOffset)
87 1 : next(registeredOutputHead)
88 1 , registrationName(p_registrationName)
89 1 , m_pinOffset(static_cast<uint16_t>(pinOffset))
90 1 , m_hasPinMode(false)
91 1 , m_pinModeOffset(-1)
92 {
93 // adding into head of the list is so easy and since we do not care about order that's what we shall do
94 1 registeredOutputHead = this;
95 1 }
96
97 51373 bool RegisteredOutputPin::isPinConfigurationChanged() {
98 51373 brain_pin_e curPin = *(brain_pin_e *) ((void *) (&((char*)&activeConfiguration)[m_pinOffset]));
99 51373 brain_pin_e newPin = *(brain_pin_e *) ((void *) (&((char*) engineConfiguration)[m_pinOffset]));
100 51373 bool pinChanged = curPin != newPin;
101
102
2/2
✓ Branch 0 taken 2854 times.
✓ Branch 1 taken 48519 times.
2/2
✓ Decision 'true' taken 2854 times.
✓ Decision 'false' taken 48519 times.
51373 if (!m_hasPinMode) {
103 2854 return pinChanged;
104 }
105
106 48519 pin_output_mode_e curMode = *(pin_output_mode_e *) ((void *) (&((char*)&activeConfiguration)[m_pinModeOffset]));
107 48519 pin_output_mode_e newMode = *(pin_output_mode_e *) ((void *) (&((char*) engineConfiguration)[m_pinModeOffset]));
108
3/4
✓ Branch 0 taken 48399 times.
✓ Branch 1 taken 120 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 48399 times.
48519 return pinChanged || curMode != newMode;
109 }
110
111 25687 void RegisteredOutputPin::init() {
112 25687 brain_pin_e newPin = *(brain_pin_e *) ((void *) (&((char*) engineConfiguration)[m_pinOffset]));
113
114 pin_output_mode_e newMode;
115
2/2
✓ Branch 0 taken 24260 times.
✓ Branch 1 taken 1427 times.
2/2
✓ Decision 'true' taken 24260 times.
✓ Decision 'false' taken 1427 times.
25687 if (m_hasPinMode) {
116 24260 newMode = *(pin_output_mode_e *) ((void *) (&((char*) engineConfiguration)[m_pinModeOffset]));
117 } else {
118 1427 newMode = OM_DEFAULT;
119 }
120
121
2/2
✓ Branch 1 taken 116 times.
✓ Branch 2 taken 25571 times.
2/2
✓ Decision 'true' taken 116 times.
✓ Decision 'false' taken 25571 times.
25687 if (isPinConfigurationChanged()) {
122 116 this->initPin(registrationName, newPin, newMode);
123 }
124 25687 }
125
126 25686 void RegisteredOutputPin::unregister() {
127
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 25682 times.
2/2
✓ Decision 'true' taken 4 times.
✓ Decision 'false' taken 25682 times.
25686 if (isPinConfigurationChanged()) {
128 4 OutputPin::deInit();
129 }
130 25686 }
131
132 #define CONFIG_OFFSET(x) (offsetof(engine_configuration_s, x))
133 // todo: pin and pinMode should be combined into a composite entity
134 // todo: one of the impediments is code generator hints handling (we need custom hints and those are not handled nice for fields of structs?)
135 #define CONFIG_PIN_OFFSETS(x) CONFIG_OFFSET(x##Pin), CONFIG_OFFSET(x##PinMode)
136
137 // offset of X within engineConfiguration, plus offset of Y within X
138 // decltype(engine_configuration_s::x) resolves the typename of the struct X inside engineConfiguration
139 #define CONFIG_OFFSET2(x, y) (offsetof(engine_configuration_s, x) + offsetof(decltype(engine_configuration_s::x), y))
140 #define CONFIG_PIN_OFFSETS2(x, y) CONFIG_OFFSET2(x, y##Pin), CONFIG_OFFSET2(x, y##PinMode)
141
142 1 EnginePins::EnginePins() :
143 // [tag:coding_by_convention] 'mainRelay' member here uses 'mainRelayPin' and 'mainRelayPinMode' configuration fields
144 1 mainRelay("Main Relay", CONFIG_PIN_OFFSETS(mainRelay)),
145 1 hpfpValve("HPFP Valve", CONFIG_PIN_OFFSETS(hpfpValve)),
146 1 starterControl("Starter Relay", CONFIG_PIN_OFFSETS(starterControl)),
147 1 starterRelayDisable("Starter Disable Relay", CONFIG_PIN_OFFSETS(starterRelayDisable)),
148 1 fanRelay("Fan Relay", CONFIG_PIN_OFFSETS(fan)),
149 1 fanRelay2("Fan Relay 2", CONFIG_PIN_OFFSETS(fan2)),
150 1 acRelay("A/C Relay", CONFIG_PIN_OFFSETS(acRelay)),
151 1 fuelPumpRelay("Fuel pump Relay", CONFIG_PIN_OFFSETS(fuelPump)),
152 1 nitrousRelay("Nitrous Relay", CONFIG_PIN_OFFSETS(nitrousRelay)),
153 1 vvlRelay("VVL Relay", CONFIG_PIN_OFFSETS(vvlRelay)),
154 #if EFI_HD_ACR
155 harleyAcr("Harley ACR", CONFIG_OFFSET(acrPin)),
156 harleyAcr2("Harley ACR 2", CONFIG_OFFSET(acrPin2)),
157 #endif // EFI_HD_ACR
158 1 boostPin("Boost", CONFIG_PIN_OFFSETS(boostControl)),
159 1 idleSolenoidPin("Idle Valve", CONFIG_OFFSET2(idle, solenoidPin), CONFIG_OFFSET2(idle, solenoidPinMode)),
160 1 secondIdleSolenoidPin("Idle Valve#2", CONFIG_OFFSET(secondSolenoidPin), CONFIG_OFFSET2(idle, solenoidPinMode)),
161 1 alternatorPin("Alternator control", CONFIG_PIN_OFFSETS(alternatorControl)),
162 1 checkEnginePin("checkEnginePin", CONFIG_PIN_OFFSETS(malfunctionIndicator)),
163 1 tachOut("tachOut", CONFIG_PIN_OFFSETS(tachOutput)),
164 1 triggerDecoderErrorPin("led: trigger debug", CONFIG_PIN_OFFSETS(triggerError)),
165
14/14
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 1 time.
✓ Branch 12 taken 12 times.
✓ Branch 13 taken 1 time.
✓ Branch 15 taken 12 times.
✓ Branch 16 taken 1 time.
✓ Branch 18 taken 12 times.
✓ Branch 19 taken 1 time.
✓ Branch 21 taken 12 times.
✓ Branch 22 taken 1 time.
✓ Branch 24 taken 2 times.
✓ Branch 25 taken 1 time.
✓ Branch 26 taken 5 times.
✓ Branch 27 taken 1 time.
64 speedoOut("speedoOut", CONFIG_OFFSET(speedometerOutputPin))
166 {
167 1 hpfpValve.setName("hpfp");
168 #if EFI_HD_ACR
169 harleyAcr.setName("acr");
170 #endif // EFI_HD_ACR
171
172 static_assert(efi::size(sparkNames) >= MAX_CYLINDER_COUNT, "Too many ignition pins");
173 static_assert(efi::size(trailNames) >= MAX_CYLINDER_COUNT, "Too many ignition pins");
174 static_assert(efi::size(injectorNames) >= MAX_CYLINDER_COUNT, "Too many injection pins");
175
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 1 time.
2/2
✓ Decision 'true' taken 12 times.
✓ Decision 'false' taken 1 time.
13 for (int i = 0; i < MAX_CYLINDER_COUNT;i++) {
176 12 enginePins.coils[i].coilIndex = i;
177 12 enginePins.coils[i].setName(sparkNames[i]);
178 12 enginePins.coils[i].shortName = sparkShortNames[i];
179
180 12 enginePins.trailingCoils[i].setName(trailNames[i]);
181 12 enginePins.trailingCoils[i].shortName = trailShortNames[i];
182
183 12 enginePins.injectors[i].injectorIndex = i;
184 12 enginePins.injectors[i].setName(injectorNames[i]);
185 12 enginePins.injectors[i].shortName = injectorShortNames[i];
186
187 12 enginePins.injectorsStage2[i].injectorIndex = i;
188 12 enginePins.injectorsStage2[i].setName(injectorStage2Names[i]);
189 12 enginePins.injectorsStage2[i].shortName = injectorStage2ShortNames[i];
190 }
191
192 static_assert(efi::size(auxValveShortNames) >= AUX_DIGITAL_VALVE_COUNT, "Too many aux valve pins");
193
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
2/2
✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 1 time.
3 for (int i = 0; i < AUX_DIGITAL_VALVE_COUNT;i++) {
194 2 enginePins.auxValve[i].setName(auxValveShortNames[i]);
195 }
196 1 }
197
198 /**
199 * Sets the value of the pin. On this layer the value is assigned as is, without any conversion.
200 */
201
202 #define unregisterOutputIfPinChanged(output, pin) { \
203 if (isConfigurationChanged(pin)) { \
204 (output).deInit(); \
205 } \
206 }
207
208 #define unregisterOutputIfPinOrModeChanged(output, pin, mode) { \
209 if (isPinOrModeChanged(pin, mode)) { \
210 (output).deInit(); \
211 } \
212 }
213
214 1021 bool EnginePins::stopPins() {
215 1021 bool result = false;
216
2/2
✓ Branch 0 taken 12252 times.
✓ Branch 1 taken 1021 times.
2/2
✓ Decision 'true' taken 12252 times.
✓ Decision 'false' taken 1021 times.
13273 for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
217 12252 result |= coils[i].stop();
218 12252 result |= injectors[i].stop();
219 12252 result |= injectorsStage2[i].stop();
220 12252 result |= trailingCoils[i].stop();
221 }
222
2/2
✓ Branch 0 taken 2042 times.
✓ Branch 1 taken 1021 times.
2/2
✓ Decision 'true' taken 2042 times.
✓ Decision 'false' taken 1021 times.
3063 for (int i = 0; i < AUX_DIGITAL_VALVE_COUNT; i++) {
223 2042 result |= auxValve[i].stop();
224 }
225 1021 return result;
226 }
227
228 1427 void EnginePins::unregisterPins() {
229 1427 stopInjectionPins();
230 1427 stopIgnitionPins();
231 #if EFI_AUX_VALVES
232 1427 stopAuxValves();
233 #endif
234
235 #if EFI_ELECTRONIC_THROTTLE_BODY
236 1427 unregisterEtbPins();
237 #endif /* EFI_ELECTRONIC_THROTTLE_BODY */
238
239 // todo: add pinMode
240
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1427 times.
1427 unregisterOutputIfPinChanged(sdCsPin, sdCardCsPin);
241
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1427 times.
1427 unregisterOutputIfPinChanged(accelerometerCs, accelerometerCsPin);
242
243 1427 RegisteredOutputPin * pin = registeredOutputHead;
244
2/2
✓ Branch 0 taken 25686 times.
✓ Branch 1 taken 1427 times.
2/2
✓ Decision 'true' taken 25686 times.
✓ Decision 'false' taken 1427 times.
27113 while (pin != nullptr) {
245 25686 pin->unregister();
246 25686 pin = pin->next;
247 }
248 1427 }
249
250 void EnginePins::debug() {
251 RegisteredOutputPin * pin = registeredOutputHead;
252 while (pin != nullptr) {
253 efiPrintf("%s %d", pin->getRegistrationName(), pin->currentLogicValue);
254 pin = pin->next;
255 }
256 }
257
258 1427 void EnginePins::startPins() {
259 #if EFI_ENGINE_CONTROL
260 1427 startInjectionPins();
261 1427 startIgnitionPins();
262 #endif /* EFI_ENGINE_CONTROL */
263
264 #if EFI_AUX_VALVES
265 1427 startAuxValves();
266 #endif // EFI_AUX_VALVES
267
268 1427 RegisteredOutputPin * pin = registeredOutputHead;
269
2/2
✓ Branch 0 taken 25686 times.
✓ Branch 1 taken 1427 times.
2/2
✓ Decision 'true' taken 25686 times.
✓ Decision 'false' taken 1427 times.
27113 while (pin != nullptr) {
270 25686 pin->init();
271 25686 pin = pin->next;
272 }
273 1427 }
274
275 1809 void EnginePins::reset() {
276
2/2
✓ Branch 0 taken 21708 times.
✓ Branch 1 taken 1809 times.
2/2
✓ Decision 'true' taken 21708 times.
✓ Decision 'false' taken 1809 times.
23517 for (int i = 0; i < MAX_CYLINDER_COUNT;i++) {
277 21708 injectors[i].reset();
278 21708 coils[i].reset();
279 21708 trailingCoils[i].reset();
280 }
281 1809 }
282
283 1427 void EnginePins::stopIgnitionPins() {
284
2/2
✓ Branch 0 taken 17124 times.
✓ Branch 1 taken 1427 times.
2/2
✓ Decision 'true' taken 17124 times.
✓ Decision 'false' taken 1427 times.
18551 for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
285
2/4
✓ Branch 2 taken 17124 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 17124 times.
17124 unregisterOutputIfPinOrModeChanged(enginePins.coils[i], ignitionPins[i], ignitionPinMode);
286
2/4
✓ Branch 2 taken 17124 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 17124 times.
17124 unregisterOutputIfPinOrModeChanged(enginePins.trailingCoils[i], trailingCoilPins[i], ignitionPinMode);
287 }
288 1427 }
289
290 1427 void EnginePins::stopInjectionPins() {
291
2/2
✓ Branch 0 taken 17124 times.
✓ Branch 1 taken 1427 times.
2/2
✓ Decision 'true' taken 17124 times.
✓ Decision 'false' taken 1427 times.
18551 for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
292
2/4
✓ Branch 2 taken 17124 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 17124 times.
17124 unregisterOutputIfPinOrModeChanged(enginePins.injectors[i], injectionPins[i], injectionPinMode);
293
2/4
✓ Branch 2 taken 17124 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 17124 times.
17124 unregisterOutputIfPinOrModeChanged(enginePins.injectorsStage2[i], injectionPinsStage2[i], injectionPinMode);
294 }
295 1427 }
296
297 #if EFI_AUX_VALVES
298 1427 void EnginePins::stopAuxValves() {
299
2/2
✓ Branch 0 taken 2854 times.
✓ Branch 1 taken 1427 times.
2/2
✓ Decision 'true' taken 2854 times.
✓ Decision 'false' taken 1427 times.
4281 for (int i = 0; i < AUX_DIGITAL_VALVE_COUNT; i++) {
300 2854 NamedOutputPin *output = &enginePins.auxValve[i];
301 // todo: do we need auxValveMode and reuse code?
302
2/2
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2852 times.
2/2
✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 2852 times.
2854 if (isConfigurationChanged(auxValves[i])) {
303 2 (output)->deInit();
304 }
305 }
306 1427 }
307
308 1427 void EnginePins::startAuxValves() {
309 #if EFI_PROD_CODE
310 for (int i = 0; i < AUX_DIGITAL_VALVE_COUNT; i++) {
311 NamedOutputPin *output = &enginePins.auxValve[i];
312 // todo: do we need auxValveMode and reuse code?
313 if (isConfigurationChanged(auxValves[i])) {
314 output->initPin(output->getName(), engineConfiguration->auxValves[i]);
315 }
316 }
317 #endif /* EFI_PROD_CODE */
318 1427 }
319 #endif // EFI_AUX_VALVES
320
321 1427 void EnginePins::startIgnitionPins() {
322 #if EFI_PROD_CODE
323 for (size_t i = 0; i < engineConfiguration->cylindersCount; i++) {
324 NamedOutputPin *trailingOutput = &enginePins.trailingCoils[i];
325 if (isPinOrModeChanged(trailingCoilPins[i], ignitionPinMode)) {
326 trailingOutput->initPin(trailingOutput->getName(), engineConfiguration->trailingCoilPins[i], engineConfiguration->ignitionPinMode);
327 }
328
329 NamedOutputPin *output = &enginePins.coils[i];
330 if (isPinOrModeChanged(ignitionPins[i], ignitionPinMode)) {
331 output->initPin(output->getName(), engineConfiguration->ignitionPins[i], engineConfiguration->ignitionPinMode);
332 }
333 }
334 #endif /* EFI_PROD_CODE */
335 1427 }
336
337 1427 void EnginePins::startInjectionPins() {
338 #if EFI_PROD_CODE
339 // todo: should we move this code closer to the injection logic?
340 for (size_t i = 0; i < engineConfiguration->cylindersCount; i++) {
341 NamedOutputPin *output = &enginePins.injectors[i];
342 if (isPinOrModeChanged(injectionPins[i], injectionPinMode)) {
343 output->initPin(output->getName(), engineConfiguration->injectionPins[i],
344 engineConfiguration->injectionPinMode);
345 }
346
347 output = &enginePins.injectorsStage2[i];
348 if (isPinOrModeChanged(injectionPinsStage2[i], injectionPinMode)) {
349 output->initPin(output->getName(), engineConfiguration->injectionPinsStage2[i],
350 engineConfiguration->injectionPinMode);
351 }
352 }
353 #endif /* EFI_PROD_CODE */
354 1427 }
355
356 OutputPin *EnginePins::getOutputPinForBenchMode(bench_mode_e index) {
357 switch(index) {
358 #if EFI_VVT_PID
359 case BENCH_VVT0_VALVE:
360 return getVvtOutputPin(0);
361 case BENCH_VVT1_VALVE:
362 return getVvtOutputPin(1);
363 case BENCH_VVT2_VALVE:
364 return getVvtOutputPin(2);
365 case BENCH_VVT3_VALVE:
366 return getVvtOutputPin(3);
367 #endif // EFI_VVT_PID
368 case BENCH_MAIN_RELAY:
369 return &mainRelay;
370 case BENCH_HPFP_VALVE:
371 return &hpfpValve;
372 case BENCH_FUEL_PUMP:
373 return &fuelPumpRelay;
374 case BENCH_STARTER_ENABLE_RELAY:
375 return &starterControl;
376 case BENCH_CHECK_ENGINE_LIGHT:
377 return &checkEnginePin;
378 case BENCH_AC_COMPRESSOR_RELAY:
379 return &acRelay;
380 case BENCH_FAN_RELAY:
381 return &fanRelay;
382 #if EFI_HD_ACR
383 case HD_ACR:
384 return &harleyAcr;
385 case HD_ACR2:
386 return &harleyAcr2;
387 #endif
388 case BENCH_IDLE_VALVE:
389 return &idleSolenoidPin;
390 case BENCH_FAN_RELAY_2:
391 return &fanRelay;
392 default:
393 criticalError("Unexpected bench pin %d", index);
394 }
395 return nullptr;
396 }
397
398 #if EFI_UNIT_TEST
399 /*
400 * this function goes through the whole pin repository and sets them all to "GPIO::Unassigned",
401 * this is done as a clean-up for testing, since several motor configurations can have conflicting pins
402 * at the same time the productive de-init uses "isPinConfigurationChanged" to reset only the pins that have been changed,
403 * so in order for it to be properly de-initialized as it is done in prod, all pins are re-configured as unassigned,
404 * previously unused pins by tests will not be de-initialized since the configuration on them will be the same (Unassigned => Unassigned)
405 */
406 602 void EnginePins::resetForUnitTest() {
407 602 RegisteredOutputPin * pin = registeredOutputHead;
408
2/2
✓ Branch 0 taken 10836 times.
✓ Branch 1 taken 602 times.
2/2
✓ Decision 'true' taken 10836 times.
✓ Decision 'false' taken 602 times.
11438 while (pin != nullptr) {
409 10836 pin->brainPin = Gpio::Unassigned;
410 10836 pin = pin->next;
411 }
412 602 }
413 #endif
414
415 56 NamedOutputPin::NamedOutputPin() : OutputPin() {
416 56 }
417
418 1 NamedOutputPin::NamedOutputPin(const char *p_name) : OutputPin() {
419 1 name = p_name;
420 1 }
421
422 56670 const char *NamedOutputPin::getName() const {
423 56670 return name;
424 }
425
426 51 void NamedOutputPin::setName(const char* p_name) {
427 51 name = p_name;
428 51 }
429
430 20995 const char *NamedOutputPin::getShortName() const {
431
2/2
✓ Branch 0 taken 97 times.
✓ Branch 1 taken 20898 times.
20995 return shortName == nullptr ? name : shortName;
432 }
433
434 #if EFI_UNIT_TEST
435 extern bool verboseMode;
436 #endif // EFI_UNIT_TEST
437
438 10193 void NamedOutputPin::setHigh() {
439 10193 setHigh(nullptr);
440 10193 }
441
442 10212 void NamedOutputPin::setHigh(const char *msg) {
443 #if EFI_UNIT_TEST
444
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10212 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 10212 times.
10212 if (verboseMode) {
445 efiPrintf("pin %s goes high", name);
446 }
447 #endif // EFI_UNIT_TEST
448 #if EFI_DETAILED_LOGGING
449 // signal->hi_time = hTimeNow();
450 #endif /* EFI_DETAILED_LOGGING */
451
452 // turn the output level ACTIVE
453 10212 setValue(msg, true);
454
455 #if EFI_ENGINE_SNIFFER
456 10212 addEngineSnifferOutputPinEvent(this, FrontDirection::UP);
457 #endif /* EFI_ENGINE_SNIFFER */
458 10212 }
459
460 10765 void NamedOutputPin::setLow() {
461 10765 setLow(nullptr);
462 10765 }
463
464 10783 void NamedOutputPin::setLow(const char *msg) {
465 #if EFI_UNIT_TEST
466
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10783 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 10783 times.
10783 if (verboseMode) {
467 efiPrintf("pin %s goes low", name);
468 }
469 #endif // EFI_UNIT_TEST
470
471 // turn off the output
472 10783 setValue(msg, false);
473
474 #if EFI_ENGINE_SNIFFER
475 10783 addEngineSnifferOutputPinEvent(this, FrontDirection::DOWN);
476 #endif /* EFI_ENGINE_SNIFFER */
477 10783 }
478
479 51050 bool NamedOutputPin::stop() {
480 #if EFI_GPIO_HARDWARE
481
5/6
✓ Branch 1 taken 51050 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 51045 times.
✓ Branch 6 taken 5 times.
✓ Branch 7 taken 51045 times.
2/2
✓ Decision 'true' taken 5 times.
✓ Decision 'false' taken 51045 times.
51050 if (isInitialized() && getLogicValue()) {
482 5 setValue("stop", false);
483 5 efiPrintf("turning off %s", name);
484 5 return true;
485 }
486 #endif /* EFI_GPIO_HARDWARE */
487 51045 return false;
488 }
489
490 24857 void InjectorOutputPin::reset() {
491 // If this injector was open, close it and reset state
492
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 24809 times.
2/2
✓ Decision 'true' taken 48 times.
✓ Decision 'false' taken 24809 times.
24857 if (overlappingCounter != 0) {
493 48 overlappingCounter = 0;
494 48 setValue("reset", 0);
495 }
496
497 // todo: this could be refactored by calling some super-reset method
498 24857 currentLogicValue = 0;
499 24857 }
500
501 24 IgnitionOutputPin::IgnitionOutputPin() {
502 24 reset();
503 24 }
504
505 6395 void IgnitionOutputPin::setHigh() {
506 6395 NamedOutputPin::setHigh();
507 // this is NASTY but what's the better option? bytes? At cost of 22 extra bytes in output status packet?
508
6/7
✓ Branch 0 taken 5792 times.
✓ Branch 1 taken 137 times.
✓ Branch 2 taken 292 times.
✓ Branch 3 taken 67 times.
✓ Branch 4 taken 56 times.
✓ Branch 5 taken 51 times.
✗ Branch 6 not taken.
6395 switch (coilIndex) {
509
1/1
✓ Decision 'true' taken 5792 times.
5792 case 0:
510 5792 engine->outputChannels.coilState1 = true;
511 5792 break;
512
1/1
✓ Decision 'true' taken 137 times.
137 case 1:
513 137 engine->outputChannels.coilState2 = true;
514 137 break;
515
1/1
✓ Decision 'true' taken 292 times.
292 case 2:
516 292 engine->outputChannels.coilState3 = true;
517 292 break;
518
1/1
✓ Decision 'true' taken 67 times.
67 case 3:
519 67 engine->outputChannels.coilState4 = true;
520 67 break;
521
1/1
✓ Decision 'true' taken 56 times.
56 case 4:
522 56 engine->outputChannels.coilState5 = true;
523 56 break;
524
1/1
✓ Decision 'true' taken 51 times.
51 case 5:
525 51 engine->outputChannels.coilState6 = true;
526 51 break;
527 }
528 6395 }
529
530 6984 void IgnitionOutputPin::setLow() {
531 6984 NamedOutputPin::setLow();
532 // this is NASTY but what's the better option? bytes? At cost of 22 extra bytes in output status packet?
533
6/7
✓ Branch 0 taken 6350 times.
✓ Branch 1 taken 141 times.
✓ Branch 2 taken 311 times.
✓ Branch 3 taken 69 times.
✓ Branch 4 taken 57 times.
✓ Branch 5 taken 56 times.
✗ Branch 6 not taken.
6984 switch (coilIndex) {
534
1/1
✓ Decision 'true' taken 6350 times.
6350 case 0:
535 6350 engine->outputChannels.coilState1 = false;
536 6350 break;
537
1/1
✓ Decision 'true' taken 141 times.
141 case 1:
538 141 engine->outputChannels.coilState2 = false;
539 141 break;
540
1/1
✓ Decision 'true' taken 311 times.
311 case 2:
541 311 engine->outputChannels.coilState3 = false;
542 311 break;
543
1/1
✓ Decision 'true' taken 69 times.
69 case 3:
544 69 engine->outputChannels.coilState4 = false;
545 69 break;
546
1/1
✓ Decision 'true' taken 57 times.
57 case 4:
547 57 engine->outputChannels.coilState5 = false;
548 57 break;
549
1/1
✓ Decision 'true' taken 56 times.
56 case 5:
550 56 engine->outputChannels.coilState6 = false;
551 56 break;
552 }
553 6984 }
554
555 43440 void IgnitionOutputPin::reset() {
556 43440 signalFallSparkId = 0;
557 43440 }
558
559 59756 bool OutputPin::isInitialized() const {
560 #if EFI_GPIO_HARDWARE && EFI_PROD_CODE
561 #if (BOARD_EXT_GPIOCHIPS > 0)
562 if (ext)
563 return true;
564 #endif /* (BOARD_EXT_GPIOCHIPS > 0) */
565 return m_port != NULL;
566 #else /* EFI_GPIO_HARDWARE */
567 59756 return true;
568 #endif /* EFI_GPIO_HARDWARE */
569 }
570
571 6311 void OutputPin::toggle() {
572 6311 setValue("toggle", !getLogicValue());
573 6311 }
574
575 2 bool OutputPin::getAndSet(int logicValue) {
576 2 bool oldValue = getLogicValue();
577 2 setValue(logicValue);
578 2 return oldValue;
579 }
580
581 // This function is only used on real hardware
582 #if EFI_PROD_CODE
583 void OutputPin::setOnchipValue(int electricalValue) {
584 if (brainPin == Gpio::Unassigned || brainPin == Gpio::Invalid) {
585 // todo: make 'setOnchipValue' or 'reportsetOnchipValueError' virtual and override for NamedOutputPin?
586 warning(ObdCode::CUSTOM_ERR_6586, "attempting to change unassigned pin");
587 return;
588 }
589 palWritePad(m_port, m_pin, electricalValue);
590 }
591 #endif // EFI_PROD_CODE
592
593 19162 void OutputPin::setValue(int logicValue, bool isForce) {
594 19162 setValue(nullptr, logicValue, isForce);
595 19162 }
596
597 #if EFI_SIMULATOR
598 void OutputPin::resetToggleStats() {
599 durationsInStateMs[0] = durationsInStateMs[1] = 0;
600 pinToggleCounter = 0;
601 }
602 #endif // EFI_SIMULATOR
603
604 48735 void OutputPin::setValue(const char *msg, int logicValue, bool isForce) {
605 UNUSED(msg);
606
3/8
✓ Branch 1 taken 48735 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 48735 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 48735 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 48735 times.
48735 if ((isHwQcMode() || getOutputOnTheBenchTest() == this) && !isForce) {
607 return;
608 }
609
610 #if ENABLE_PERF_TRACE
611 // todo: https://github.com/rusefi/rusefi/issues/1638
612 // ScopePerf perf(PE::OutputPinSetValue);
613 #endif // ENABLE_PERF_TRACE
614
615 #if EFI_UNIT_TEST
616
2/2
✓ Branch 0 taken 30265 times.
✓ Branch 1 taken 18470 times.
2/2
✓ Decision 'true' taken 30265 times.
✓ Decision 'false' taken 18470 times.
48735 if (currentLogicValue != logicValue) {
617 30265 pinToggleCounter++;
618 }
619 #endif // EFI_UNIT_TEST
620
621 #if EFI_SIMULATOR
622 if (currentLogicValue != logicValue) {
623 if (pinToggleCounter > 0) {
624 durationsInStateMs[0] = durationsInStateMs[1];
625 durationsInStateMs[1] = pinToggleTimer.getElapsedUs() / 1000;
626 }
627 pinToggleCounter++;
628 pinToggleTimer.reset();
629 }
630 #endif // EFI_SIMULATOR
631
632 #if EFI_UNIT_TEST
633
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48735 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 48735 times.
48735 if (verboseMode) {
634 efiPrintf("pin goes %d", logicValue);
635 }
636 #endif // EFI_UNIT_TEST
637
638 // Always store the current logical value of the pin (so it can be
639 // used internally even if not connected to a real hardware pin)
640 48735 currentLogicValue = logicValue;
641
642 // Nothing else to do if not configured
643
2/2
✓ Branch 1 taken 42799 times.
✓ Branch 2 taken 5936 times.
2/2
✓ Decision 'true' taken 42799 times.
✓ Decision 'false' taken 5936 times.
48735 if (!isBrainPinValid(brainPin)) {
644 42799 return;
645 }
646
647
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5936 times.
5936 efiAssertVoid(ObdCode::CUSTOM_ERR_6622, mode <= OM_OPENDRAIN_INVERTED, "invalid pin_output_mode_e");
648
8/10
✓ Branch 0 taken 1711 times.
✓ Branch 1 taken 4225 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1710 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 4222 times.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 4222 times.
5936 int electricalValue = getElectricalValue(logicValue, mode);
649
650 #if EFI_PROD_CODE
651 #if (BOARD_EXT_GPIOCHIPS > 0)
652 if (!this->ext) {
653 setOnchipValue(electricalValue);
654 } else {
655 /* external pin */
656 gpiochips_writePad(this->brainPin, logicValue);
657 /* TODO: check return value */
658 }
659 #else
660 setOnchipValue(electricalValue);
661 #endif
662 #else /* EFI_PROD_CODE */
663 5936 setMockState(brainPin, electricalValue);
664 #endif /* EFI_PROD_CODE */
665 }
666
667 1876989 bool OutputPin::getLogicValue() const {
668 // Compare against 1 since it could also be INITIAL_PIN_STATE (which means logical 0, but we haven't initialized the pin yet)
669 1876989 return currentLogicValue == 1;
670 }
671
672 139 void OutputPin::setDefaultPinState(pin_output_mode_e outputMode) {
673
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 139 times.
139 assertOMode(mode);
674 139 this->mode = outputMode;
675 139 setValue(false, /*force*/true); // initial state
676 }
677
678 brain_pin_diag_e OutputPin::getDiag() const {
679 #if EFI_PROD_CODE
680 #if BOARD_EXT_GPIOCHIPS > 0
681 if (!brain_pin_is_onchip(brainPin)) {
682 return gpiochips_getDiag(brainPin);
683 }
684 #endif
685 #endif /* EFI_PROD_CODE */
686 // TODO: add hook to board code for custom diagnostic, like it is done on S105
687 return PIN_UNKNOWN;
688 }
689
690 602 void initMiscOutputPins() {
691 #if EFI_GPIO_HARDWARE
692
693 #if HAL_USE_SPI
694 enginePins.sdCsPin.initPin("SD CS", engineConfiguration->sdCardCsPin);
695 #endif /* HAL_USE_SPI */
696
697 #if EFI_SHAFT_POSITION_INPUT
698 // todo: migrate remaining OutputPin to RegisteredOutputPin in order to get consistent dynamic pin init/deinit
699 602 enginePins.debugTriggerSync.initPin("debug: sync", engineConfiguration->debugTriggerSync);
700 #endif // EFI_SHAFT_POSITION_INPUT
701
702 602 enginePins.o2heater.initPin("O2 heater", engineConfiguration->o2heaterPin);
703
704 #endif /* EFI_GPIO_HARDWARE */
705 602 }
706
707 1215 void OutputPin::initPin(const char *p_msg, brain_pin_e p_brainPin) {
708 1215 initPin(p_msg, p_brainPin, OM_DEFAULT);
709 1214 }
710
711 1367 void OutputPin::initPin(const char *msg, brain_pin_e p_brainPin, pin_output_mode_e outputMode, bool forceInitWithFatalError) {
712 #if EFI_UNIT_TEST
713 1367 pinToggleCounter = 0;
714 #endif
715
716
2/2
✓ Branch 1 taken 1227 times.
✓ Branch 2 taken 140 times.
2/2
✓ Decision 'true' taken 1227 times.
✓ Decision 'false' taken 140 times.
1367 if (!isBrainPinValid(p_brainPin)) {
717 1227 return;
718 }
719
720 // Enter a critical section so that other threads can't change the pin state out from underneath us
721 chibios_rt::CriticalSectionLocker csl;
722
723
2/4
✓ Branch 0 taken 140 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 140 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 140 times.
140 if (!forceInitWithFatalError && hasFirmwareError()) {
724 // Don't allow initializing more pins if we have a fatal error.
725 // Pins should have just been reset, so we shouldn't try to init more.
726 return;
727 }
728
729 // Check that this OutputPin isn't already assigned to another pin (reinit is allowed to change mode)
730 // To avoid this error, call deInit() first
731
6/6
✓ Branch 1 taken 71 times.
✓ Branch 2 taken 69 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 70 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 139 times.
2/2
✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 139 times.
140 if (isBrainPinValid(brainPin) && brainPin != p_brainPin) {
732 1 firmwareError(ObdCode::CUSTOM_OBD_PIN_CONFLICT, "outputPin [%s] already assigned, cannot reassign without unregister first", msg);
733 return;
734 }
735
736
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 139 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 139 times.
139 if (outputMode > OM_OPENDRAIN_INVERTED) {
737 firmwareError(ObdCode::CUSTOM_INVALID_MODE_SETTING, "%s invalid pin_output_mode_e %d %s",
738 msg,
739 outputMode,
740 hwPortname(p_brainPin)
741 );
742 return;
743 }
744
745 #if EFI_GPIO_HARDWARE && EFI_PROD_CODE
746 iomode_t l_mode = (outputMode == OM_DEFAULT || outputMode == OM_INVERTED) ?
747 PAL_MODE_OUTPUT_PUSHPULL : PAL_MODE_OUTPUT_OPENDRAIN;
748
749 #if (BOARD_EXT_GPIOCHIPS > 0)
750 this->ext = false;
751 #endif
752 if (brain_pin_is_onchip(p_brainPin)) {
753 m_port = getHwPort(msg, p_brainPin);
754 m_pin = getHwPin(msg, p_brainPin);
755
756 // Validate port
757 if (!m_port) {
758 criticalError("OutputPin::initPin got invalid port for pin idx %d", static_cast<int>(p_brainPin));
759 return;
760 }
761 }
762 #if (BOARD_EXT_GPIOCHIPS > 0)
763 else {
764 this->ext = true;
765 }
766 #endif
767 #endif // briefly leave the include guard because we need to set default state in tests
768
769 139 brainPin = p_brainPin;
770
771 // The order of the next two calls may look strange, which is a good observation.
772 // We call them in this order so that the pin is set to a known state BEFORE
773 // it's enabled. Enabling the pin then setting it could result in a (brief)
774 // mystery state being driven on the pin (potentially dangerous).
775 139 setDefaultPinState(outputMode);
776
777 #if EFI_GPIO_HARDWARE && EFI_PROD_CODE
778 efiSetPadMode(msg, brainPin, l_mode);
779 if (brain_pin_is_onchip(brainPin)) {
780 // todo: handle OM_OPENDRAIN and OM_OPENDRAIN_INVERTED as well
781 if (outputMode == OM_DEFAULT || outputMode == OM_INVERTED) {
782 #ifndef DISABLE_PIN_STATE_VALIDATION
783 int actualValue = palReadPad(m_port, m_pin);
784 // we had enough drama with pin configuration in board.h and else that we shall self-check
785
786 const int logicalValue =
787 (outputMode == OM_INVERTED)
788 ? !actualValue
789 : actualValue;
790
791 // if the pin was set to logical 1, then set an error and disable the pin so that things don't catch fire
792 if (logicalValue) {
793 criticalError("HARDWARE VALIDATION FAILED %s: unexpected startup pin state %s actual value=%d logical value=%d mode=%s", msg, hwPortname(brainPin), actualValue, logicalValue, getPin_output_mode_e(outputMode));
794 OutputPin::deInit();
795 }
796 #endif
797 }
798 }
799 #endif /* EFI_GPIO_HARDWARE */
800 }
801
802 7 void OutputPin::deInit() {
803 7 efiPrintf("unregistering %s", hwPortname(brainPin));
804
805 // Unregister under lock - we don't want other threads mucking with the pin while we're trying to turn it off
806 chibios_rt::CriticalSectionLocker csl;
807
808 // nothing to do if not registered in the first place
809
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 2 times.
2/2
✓ Decision 'true' taken 5 times.
✓ Decision 'false' taken 2 times.
7 if (!isBrainPinValid(brainPin)) {
810 5 return;
811 }
812
813 #if (BOARD_EXT_GPIOCHIPS > 0)
814 2 ext = false;
815 #endif // (BOARD_EXT_GPIOCHIPS > 0)
816
817 #if EFI_GPIO_HARDWARE && EFI_PROD_CODE
818 efiSetPadUnused(brainPin);
819 #endif /* EFI_GPIO_HARDWARE */
820
821 // Clear the pin so that it won't get set any more
822 2 brainPin = Gpio::Unassigned;
823 }
824
825 #if EFI_GPIO_HARDWARE
826
827 // questionable trick: we avoid using 'getHwPort' and 'getHwPin' in case of errors in order to increase the changes of turning the LED
828 // by reducing stack requirement
829 ioportid_t criticalErrorLedPort;
830 ioportmask_t criticalErrorLedPin;
831 uint8_t criticalErrorLedState;
832
833 #if EFI_PROD_CODE
834 static void initErrorLed(Gpio led) {
835 enginePins.errorLedPin.initPin("led: CRITICAL status", led, (LED_PIN_MODE));
836 criticalErrorLedPort = getHwPort("CRITICAL", led);
837 criticalErrorLedPin = getHwPin("CRITICAL", led);
838 criticalErrorLedState = (LED_PIN_MODE == OM_INVERTED) ? 0 : 1;
839 }
840 #endif /* EFI_PROD_CODE */
841
842 void initPrimaryPins() {
843 #if EFI_PROD_CODE
844 initErrorLed(LED_CRITICAL_ERROR_BRAIN_PIN);
845
846 addConsoleAction("gpio_pins", EnginePins::debug);
847 #endif /* EFI_PROD_CODE */
848 }
849
850 /**
851 * This method is part of fatal error handling.
852 * The whole method is pretty naive, but that's at least something.
853 */
854 void turnAllPinsOff() {
855 for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
856 enginePins.injectors[i].setValue(false);
857 enginePins.coils[i].setValue(false);
858 enginePins.trailingCoils[i].setValue(false);
859 }
860 enginePins.mainRelay.setValue(false);
861 enginePins.fuelPumpRelay.setValue(false);
862 enginePins.checkEnginePin.setValue(true); // yes this one can go ON
863 #if EFI_PROD_CODE && HW_HELLEN
864 hellenDisableEnSilently();
865 #endif
866 }
867 #endif /* EFI_GPIO_HARDWARE */
868
869