rusEFI
The most advanced open source ECU
efi_gpio.cpp
Go to the documentation of this file.
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_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
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  PROTOCOL_VVT1_NAME,
36  PROTOCOL_VVT2_NAME,
37  PROTOCOL_VVT3_NAME,
38  PROTOCOL_VVT4_NAME};
39 
40 const char *laNames[] = {
41  PROTOCOL_WA_CHANNEL_1,
42  PROTOCOL_WA_CHANNEL_2,
43  PROTOCOL_WA_CHANNEL_3,
44  PROTOCOL_WA_CHANNEL_4};
45 
46 // these short names are part of engine sniffer protocol
47 static const char* const sparkShortNames[] = { PROTOCOL_COIL1_SHORT_NAME, "c2", "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_INJ1_SHORT_NAME, "i2", "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_INJ1_STAGE2_SHORT_NAME, "j2", "j3", "j4", "j5", "j6", "j7", "j8",
60  "j9", "jA", "jB", "jC"};
61 
62 static const char* const auxValveShortNames[] = { "a1", "a2"};
63 
65 
66 RegisteredNamedOutputPin::RegisteredNamedOutputPin(const char *p_name, size_t pinOffset,
67  size_t pinModeOffset) : RegisteredOutputPin(p_name, pinOffset, pinModeOffset) {
68 }
69 
70 RegisteredNamedOutputPin::RegisteredNamedOutputPin(const char *p_name, size_t pinOffset) :
71  RegisteredOutputPin(p_name, pinOffset) {
72 }
73 
74 RegisteredOutputPin::RegisteredOutputPin(const char *p_registrationName, size_t pinOffset,
75  size_t pinModeOffset)
76  : next(registeredOutputHead)
77  , registrationName(p_registrationName)
78  , m_pinOffset(static_cast<uint16_t>(pinOffset))
79  , m_hasPinMode(true)
80  , 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  registeredOutputHead = this;
84 }
85 
86 RegisteredOutputPin::RegisteredOutputPin(const char *p_registrationName, size_t pinOffset)
87  : next(registeredOutputHead)
88  , registrationName(p_registrationName)
89  , m_pinOffset(static_cast<uint16_t>(pinOffset))
90  , m_hasPinMode(false)
91  , 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  registeredOutputHead = this;
95 }
96 
98 #if EFI_PROD_CODE
99  brain_pin_e curPin = *(brain_pin_e *) ((void *) (&((char*)&activeConfiguration)[m_pinOffset]));
100  brain_pin_e newPin = *(brain_pin_e *) ((void *) (&((char*) engineConfiguration)[m_pinOffset]));
101  bool pinChanged = curPin != newPin;
102 
103  if (!m_hasPinMode) {
104  return pinChanged;
105  }
106 
107  pin_output_mode_e curMode = *(pin_output_mode_e *) ((void *) (&((char*)&activeConfiguration)[m_pinModeOffset]));
108  pin_output_mode_e newMode = *(pin_output_mode_e *) ((void *) (&((char*) engineConfiguration)[m_pinModeOffset]));
109  return pinChanged || curMode != newMode;
110 #else
111  return true;
112 #endif // EFI_PROD_CODE
113 }
114 
116  brain_pin_e newPin = *(brain_pin_e *) ((void *) (&((char*) engineConfiguration)[m_pinOffset]));
117 
118  pin_output_mode_e newMode;
119  if (m_hasPinMode) {
120  newMode = *(pin_output_mode_e *) ((void *) (&((char*) engineConfiguration)[m_pinModeOffset]));
121  } else {
122  newMode = OM_DEFAULT;
123  }
124 
126  this->initPin(registrationName, newPin, newMode);
127  }
128 }
129 
133  }
134 }
135 
136 #define CONFIG_OFFSET(x) (offsetof(engine_configuration_s, x))
137 // todo: pin and pinMode should be combined into a composite entity
138 // todo: one of the impediments is code generator hints handling (we need custom hints and those are not handled nice for fields of structs?)
139 #define CONFIG_PIN_OFFSETS(x) CONFIG_OFFSET(x##Pin), CONFIG_OFFSET(x##PinMode)
140 
141 // offset of X within engineConfiguration, plus offset of Y within X
142 // decltype(engine_configuration_s::x) resolves the typename of the struct X inside engineConfiguration
143 #define CONFIG_OFFSET2(x, y) (offsetof(engine_configuration_s, x) + offsetof(decltype(engine_configuration_s::x), y))
144 #define CONFIG_PIN_OFFSETS2(x, y) CONFIG_OFFSET2(x, y##Pin), CONFIG_OFFSET2(x, y##PinMode)
145 
147  mainRelay("Main Relay", CONFIG_PIN_OFFSETS(mainRelay)),
148  hpfpValve("HPFP Valve", CONFIG_PIN_OFFSETS(hpfpValve)),
149  starterControl("Starter Relay", CONFIG_PIN_OFFSETS(starterControl)),
150  starterRelayDisable("Starter Disable Relay", CONFIG_PIN_OFFSETS(starterRelayDisable)),
151  fanRelay("Fan Relay", CONFIG_PIN_OFFSETS(fan)),
152  fanRelay2("Fan Relay 2", CONFIG_PIN_OFFSETS(fan2)),
153  acRelay("A/C Relay", CONFIG_PIN_OFFSETS(acRelay)),
154  fuelPumpRelay("Fuel pump Relay", CONFIG_PIN_OFFSETS(fuelPump)),
155 #if EFI_HD_ACR
156  harleyAcr("Harley ACR", CONFIG_OFFSET(acrPin)),
157  harleyAcr2("Harley ACR 2", CONFIG_OFFSET(acrPin2)),
158 #endif // EFI_HD_ACR
159  boostPin("Boost", CONFIG_PIN_OFFSETS(boostControl)),
160  idleSolenoidPin("Idle Valve", CONFIG_OFFSET2(idle, solenoidPin), CONFIG_OFFSET2(idle, solenoidPinMode)),
161  secondIdleSolenoidPin("Idle Valve#2", CONFIG_OFFSET(secondSolenoidPin), CONFIG_OFFSET2(idle, solenoidPinMode)),
162  alternatorPin("Alternator control", CONFIG_PIN_OFFSETS(alternatorControl)),
163  checkEnginePin("checkEnginePin", CONFIG_PIN_OFFSETS(malfunctionIndicator)),
164  tachOut("tachOut", CONFIG_PIN_OFFSETS(tachOutput)),
165  triggerDecoderErrorPin("led: trigger debug", CONFIG_PIN_OFFSETS(triggerError)),
166  speedoOut("speedoOut", CONFIG_OFFSET(speedometerOutputPin))
167 {
168  hpfpValve.setName(PROTOCOL_HPFP_NAME);
169 #if EFI_HD_ACR
170  harleyAcr.setName(PROTOCOL_ACR_NAME);
171 #endif // EFI_HD_ACR
172 
173  static_assert(efi::size(sparkNames) >= MAX_CYLINDER_COUNT, "Too many ignition pins");
174  static_assert(efi::size(trailNames) >= MAX_CYLINDER_COUNT, "Too many ignition pins");
175  static_assert(efi::size(injectorNames) >= MAX_CYLINDER_COUNT, "Too many injection pins");
176  for (int i = 0; i < MAX_CYLINDER_COUNT;i++) {
177  enginePins.coils[i].coilIndex = i;
180 
183 
187 
191  }
192 
193  static_assert(efi::size(auxValveShortNames) >= AUX_DIGITAL_VALVE_COUNT, "Too many aux valve pins");
194  for (int i = 0; i < AUX_DIGITAL_VALVE_COUNT;i++) {
196  }
197 }
198 
199 /**
200  * Sets the value of the pin. On this layer the value is assigned as is, without any conversion.
201  */
202 
203 #define unregisterOutputIfPinChanged(output, pin) { \
204  if (isConfigurationChanged(pin)) { \
205  (output).deInit(); \
206  } \
207 }
208 
209 #define unregisterOutputIfPinOrModeChanged(output, pin, mode) { \
210  if (isPinOrModeChanged(pin, mode)) { \
211  (output).deInit(); \
212  } \
213 }
214 
216  bool result = false;
217  for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
218  result |= coils[i].stop();
219  result |= injectors[i].stop();
220  result |= injectorsStage2[i].stop();
221  result |= trailingCoils[i].stop();
222  }
223  for (int i = 0; i < AUX_DIGITAL_VALVE_COUNT; i++) {
224  result |= auxValve[i].stop();
225  }
226  return result;
227 }
228 
232  stopAuxValves();
233 
234 #if EFI_ELECTRONIC_THROTTLE_BODY
236 #endif /* EFI_ELECTRONIC_THROTTLE_BODY */
237 
238  // todo: add pinMode
239  unregisterOutputIfPinChanged(sdCsPin, sdCardCsPin);
240  unregisterOutputIfPinChanged(accelerometerCs, accelerometerCsPin);
241 
243  while (pin != nullptr) {
244  pin->unregister();
245  pin = pin->next;
246  }
247 }
248 
251  while (pin != nullptr) {
252  efiPrintf("%s %d", pin->getRegistrationName(), pin->currentLogicValue);
253  pin = pin->next;
254  }
255 }
256 
258 #if EFI_ENGINE_CONTROL
261  startAuxValves();
262 #endif /* EFI_ENGINE_CONTROL */
263 
265  while (pin != nullptr) {
266  pin->init();
267  pin = pin->next;
268  }
269 }
270 
272  for (int i = 0; i < MAX_CYLINDER_COUNT;i++) {
273  injectors[i].reset();
274  coils[i].reset();
275  trailingCoils[i].reset();
276  }
277 }
278 
280  for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
281  unregisterOutputIfPinOrModeChanged(enginePins.coils[i], ignitionPins[i], ignitionPinMode);
282  unregisterOutputIfPinOrModeChanged(enginePins.trailingCoils[i], trailingCoilPins[i], ignitionPinMode);
283  }
284 }
285 
287  for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
288  unregisterOutputIfPinOrModeChanged(enginePins.injectors[i], injectionPins[i], injectionPinMode);
289  unregisterOutputIfPinOrModeChanged(enginePins.injectorsStage2[i], injectionPinsStage2[i], injectionPinMode);
290  }
291 }
292 
294  for (int i = 0; i < AUX_DIGITAL_VALVE_COUNT; i++) {
295  NamedOutputPin *output = &enginePins.auxValve[i];
296  // todo: do we need auxValveMode and reuse code?
297  if (isConfigurationChanged(auxValves[i])) {
298  (output)->deInit();
299  }
300  }
301 }
302 
304 #if EFI_PROD_CODE
305  for (int i = 0; i < AUX_DIGITAL_VALVE_COUNT; i++) {
306  NamedOutputPin *output = &enginePins.auxValve[i];
307  // todo: do we need auxValveMode and reuse code?
308  if (isConfigurationChanged(auxValves[i])) {
309  output->initPin(output->getName(), engineConfiguration->auxValves[i]);
310  }
311  }
312 #endif /* EFI_PROD_CODE */
313 }
314 
316 #if EFI_PROD_CODE
317  for (size_t i = 0; i < engineConfiguration->cylindersCount; i++) {
318  NamedOutputPin *trailingOutput = &enginePins.trailingCoils[i];
319  if (isPinOrModeChanged(trailingCoilPins[i], ignitionPinMode)) {
321  }
322 
323  NamedOutputPin *output = &enginePins.coils[i];
324  if (isPinOrModeChanged(ignitionPins[i], ignitionPinMode)) {
326  }
327  }
328 #endif /* EFI_PROD_CODE */
329 }
330 
332 #if EFI_PROD_CODE
333  // todo: should we move this code closer to the injection logic?
334  for (size_t i = 0; i < engineConfiguration->cylindersCount; i++) {
335  NamedOutputPin *output = &enginePins.injectors[i];
336  if (isPinOrModeChanged(injectionPins[i], injectionPinMode)) {
337  output->initPin(output->getName(), engineConfiguration->injectionPins[i],
339  }
340 
341  output = &enginePins.injectorsStage2[i];
342  if (isPinOrModeChanged(injectionPinsStage2[i], injectionPinMode)) {
345  }
346  }
347 #endif /* EFI_PROD_CODE */
348 }
349 
351  switch(index) {
352 #if EFI_VVT_PID
353  case BENCH_VVT0_VALVE:
354  return getVvtOutputPin(0);
355  case BENCH_VVT1_VALVE:
356  return getVvtOutputPin(1);
357  case BENCH_VVT2_VALVE:
358  return getVvtOutputPin(2);
359  case BENCH_VVT3_VALVE:
360  return getVvtOutputPin(3);
361 #endif // EFI_VVT_PID
362  case BENCH_MAIN_RELAY:
363  return &mainRelay;
364  case BENCH_HPFP_VALVE:
365  return &hpfpValve;
366  case BENCH_FUEL_PUMP:
367  return &fuelPumpRelay;
369  return &starterControl;
371  return &checkEnginePin;
373  return &acRelay;
374  case BENCH_FAN_RELAY:
375  return &fanRelay;
376 #if EFI_HD_ACR
377  case HD_ACR:
378  return &harleyAcr;
379  case HD_ACR2:
380  return &harleyAcr2;
381 #endif
382  case BENCH_IDLE_VALVE:
383  return &idleSolenoidPin;
384  case BENCH_FAN_RELAY_2:
385  return &fanRelay;
386  default:
387  criticalError("Unexpected bench pin %d", index);
388  }
389  return nullptr;
390 }
391 
393 }
394 
395 NamedOutputPin::NamedOutputPin(const char *p_name) : OutputPin() {
396  name = p_name;
397 }
398 
399 const char *NamedOutputPin::getName() const {
400  return name;
401 }
402 
403 void NamedOutputPin::setName(const char* p_name) {
404  name = p_name;
405 }
406 
407 const char *NamedOutputPin::getShortName() const {
408  return shortName == nullptr ? name : shortName;
409 }
410 
411 #if EFI_UNIT_TEST
412 extern bool verboseMode;
413 #endif // EFI_UNIT_TEST
414 
416  setHigh(nullptr);
417 }
418 
419 void NamedOutputPin::setHigh(const char *msg) {
420 #if EFI_UNIT_TEST
421  if (verboseMode) {
422  efiPrintf("pin %s goes high", name);
423  }
424 #endif // EFI_UNIT_TEST
425 #if EFI_DEFAILED_LOGGING
426 // signal->hi_time = hTimeNow();
427 #endif /* EFI_DEFAILED_LOGGING */
428 
429  // turn the output level ACTIVE
430  setValue(msg, true);
431 
432 #if EFI_ENGINE_SNIFFER
434 #endif /* EFI_ENGINE_SNIFFER */
435 }
436 
438  setLow(nullptr);
439 }
440 
441 void NamedOutputPin::setLow(const char *msg) {
442 #if EFI_UNIT_TEST
443  if (verboseMode) {
444  efiPrintf("pin %s goes low", name);
445  }
446 #endif // EFI_UNIT_TEST
447 
448  // turn off the output
449  setValue(msg, false);
450 
451 #if EFI_ENGINE_SNIFFER
453 #endif /* EFI_ENGINE_SNIFFER */
454 }
455 
457 #if EFI_GPIO_HARDWARE
458  if (isInitialized() && getLogicValue()) {
459  setValue("stop", false);
460  efiPrintf("turning off %s", name);
461  return true;
462  }
463 #endif /* EFI_GPIO_HARDWARE */
464  return false;
465 }
466 
468  // If this injector was open, close it and reset state
469  if (overlappingCounter != 0) {
470  overlappingCounter = 0;
471  setValue("reset", 0);
472  }
473 
474  // todo: this could be refactored by calling some super-reset method
475  currentLogicValue = 0;
476 }
477 
479  reset();
480 }
481 
484  // this is NASTY but what's the better option? bytes? At cost of 22 extra bytes in output status packet?
485  switch (coilIndex) {
486  case 0:
488  break;
489  case 1:
491  break;
492  case 2:
494  break;
495  case 3:
497  break;
498  case 4:
500  break;
501  case 5:
503  break;
504  }
505 }
506 
509  // this is NASTY but what's the better option? bytes? At cost of 22 extra bytes in output status packet?
510  switch (coilIndex) {
511  case 0:
513  break;
514  case 1:
516  break;
517  case 2:
519  break;
520  case 3:
522  break;
523  case 4:
525  break;
526  case 5:
528  break;
529  }
530 }
531 
533  signalFallSparkId = 0;
534 }
535 
537 #if EFI_GPIO_HARDWARE && EFI_PROD_CODE
538 #if (BOARD_EXT_GPIOCHIPS > 0)
539  if (ext)
540  return true;
541 #endif /* (BOARD_EXT_GPIOCHIPS > 0) */
542  return m_port != NULL;
543 #else /* EFI_GPIO_HARDWARE */
544  return true;
545 #endif /* EFI_GPIO_HARDWARE */
546 }
547 
549  setValue("toggle", !getLogicValue());
550 }
551 
552 bool OutputPin::getAndSet(int logicValue) {
553  bool oldValue = getLogicValue();
554  setValue(logicValue);
555  return oldValue;
556 }
557 
558 // This function is only used on real hardware
559 #if EFI_PROD_CODE
560 void OutputPin::setOnchipValue(int electricalValue) {
562  // todo: make 'setOnchipValue' or 'reportsetOnchipValueError' virtual and override for NamedOutputPin?
563  warning(ObdCode::CUSTOM_ERR_6586, "attempting to change unassigned pin");
564  return;
565  }
566  palWritePad(m_port, m_pin, electricalValue);
567 }
568 #endif // EFI_PROD_CODE
569 
570 void OutputPin::setValue(int logicValue, bool isForce) {
571  setValue(nullptr, logicValue, isForce);
572 }
573 
574 #if EFI_SIMULATOR
577  pinToggleCounter = 0;
578 }
579 #endif // EFI_SIMULATOR
580 
581 void OutputPin::setValue(const char *msg, int logicValue, bool isForce) {
582 extern bool qcDirectPinControlMode;
583  UNUSED(msg);
584  if ((qcDirectPinControlMode || getOutputOnTheBenchTest() == this) && !isForce) {
585  return;
586  }
587 
588 #if ENABLE_PERF_TRACE
589 // todo: https://github.com/rusefi/rusefi/issues/1638
590 // ScopePerf perf(PE::OutputPinSetValue);
591 #endif // ENABLE_PERF_TRACE
592 
593 #if EFI_UNIT_TEST
594  if (currentLogicValue != logicValue) {
596  }
597 #endif // EFI_UNIT_TEST
598 
599 #if EFI_SIMULATOR
600  if (currentLogicValue != logicValue) {
601  if (pinToggleCounter > 0) {
603  durationsInStateMs[1] = pinToggleTimer.getElapsedUs() / 1000;
604  }
606  pinToggleTimer.reset();
607  }
608 #endif // EFI_SIMULATOR
609 
610 #if EFI_UNIT_TEST
611  if (verboseMode) {
612  efiPrintf("pin goes %d", logicValue);
613  }
614 #endif // EFI_UNIT_TEST
615 
616  // Always store the current logical value of the pin (so it can be
617  // used internally even if not connected to a real hardware pin)
618  currentLogicValue = logicValue;
619 
620  // Nothing else to do if not configured
621  if (!isBrainPinValid(brainPin)) {
622  return;
623  }
624 
625  efiAssertVoid(ObdCode::CUSTOM_ERR_6622, mode <= OM_OPENDRAIN_INVERTED, "invalid pin_output_mode_e");
626  int electricalValue = getElectricalValue(logicValue, mode);
627 
628 #if EFI_PROD_CODE
629  #if (BOARD_EXT_GPIOCHIPS > 0)
630  if (!this->ext) {
631  setOnchipValue(electricalValue);
632  } else {
633  /* external pin */
634  gpiochips_writePad(this->brainPin, logicValue);
635  /* TODO: check return value */
636  }
637  #else
638  setOnchipValue(electricalValue);
639  #endif
640 #else /* EFI_PROD_CODE */
641  setMockState(brainPin, electricalValue);
642 #endif /* EFI_PROD_CODE */
643 }
644 
646  // Compare against 1 since it could also be INITIAL_PIN_STATE (which means logical 0, but we haven't initialized the pin yet)
647  return currentLogicValue == 1;
648 }
649 
651  assertOMode(mode);
652  this->mode = outputMode;
653  setValue(false, /*force*/true); // initial state
654 }
655 
657 #if EFI_PROD_CODE
658 #if BOARD_EXT_GPIOCHIPS > 0
660  return gpiochips_getDiag(brainPin);
661  }
662 #endif
663 #endif /* EFI_PROD_CODE */
664  // TODO: add hook to board code for custom diagnostic, like it is done on S105
665  return PIN_UNKNOWN;
666 }
667 
669 #if EFI_GPIO_HARDWARE
670 
671 #if HAL_USE_SPI
673 #endif /* HAL_USE_SPI */
674 
675 #if EFI_SHAFT_POSITION_INPUT
676  // todo: migrate remaining OutputPin to RegisteredOutputPin in order to get consistent dynamic pin init/deinit
678 #endif // EFI_SHAFT_POSITION_INPUT
679 
681 
682 #endif /* EFI_GPIO_HARDWARE */
683 }
684 
685 void OutputPin::initPin(const char *p_msg, brain_pin_e p_brainPin) {
686  initPin(p_msg, p_brainPin, OM_DEFAULT);
687 }
688 
689 void OutputPin::initPin(const char *msg, brain_pin_e p_brainPin, pin_output_mode_e outputMode, bool forceInitWithFatalError) {
690 #if EFI_UNIT_TEST
691  pinToggleCounter = 0;
692 #endif
693 
694  if (!isBrainPinValid(p_brainPin)) {
695  return;
696  }
697 
698  // Enter a critical section so that other threads can't change the pin state out from underneath us
699  chibios_rt::CriticalSectionLocker csl;
700 
701  if (!forceInitWithFatalError && hasFirmwareError()) {
702  // Don't allow initializing more pins if we have a fatal error.
703  // Pins should have just been reset, so we shouldn't try to init more.
704  return;
705  }
706 
707  // Check that this OutputPin isn't already assigned to another pin (reinit is allowed to change mode)
708  // To avoid this error, call deInit() first
709  if (isBrainPinValid(brainPin) && brainPin != p_brainPin) {
710  firmwareError(ObdCode::CUSTOM_OBD_PIN_CONFLICT, "outputPin [%s] already assigned, cannot reassign without unregister first", msg);
711  return;
712  }
713 
714  if (outputMode > OM_OPENDRAIN_INVERTED) {
715  firmwareError(ObdCode::CUSTOM_INVALID_MODE_SETTING, "%s invalid pin_output_mode_e %d %s",
716  msg,
717  outputMode,
718  hwPortname(p_brainPin)
719  );
720  return;
721  }
722 
723 #if EFI_GPIO_HARDWARE && EFI_PROD_CODE
724  iomode_t l_mode = (outputMode == OM_DEFAULT || outputMode == OM_INVERTED) ?
725  PAL_MODE_OUTPUT_PUSHPULL : PAL_MODE_OUTPUT_OPENDRAIN;
726 
727  #if (BOARD_EXT_GPIOCHIPS > 0)
728  this->ext = false;
729  #endif
730  if (brain_pin_is_onchip(p_brainPin)) {
731  m_port = getHwPort(msg, p_brainPin);
732  m_pin = getHwPin(msg, p_brainPin);
733 
734  // Validate port
735  if (m_port == GPIO_NULL) {
736  criticalError("OutputPin::initPin got invalid port for pin idx %d", static_cast<int>(p_brainPin));
737  return;
738  }
739  }
740  #if (BOARD_EXT_GPIOCHIPS > 0)
741  else {
742  this->ext = true;
743  }
744  #endif
745 #endif // briefly leave the include guard because we need to set default state in tests
746 
747  brainPin = p_brainPin;
748 
749  // The order of the next two calls may look strange, which is a good observation.
750  // We call them in this order so that the pin is set to a known state BEFORE
751  // it's enabled. Enabling the pin then setting it could result in a (brief)
752  // mystery state being driven on the pin (potentially dangerous).
753  setDefaultPinState(outputMode);
754 
755 #if EFI_GPIO_HARDWARE && EFI_PROD_CODE
756  efiSetPadMode(msg, brainPin, l_mode);
758  // todo: handle OM_OPENDRAIN and OM_OPENDRAIN_INVERTED as well
759  if (outputMode == OM_DEFAULT || outputMode == OM_INVERTED) {
760 #ifndef DISABLE_PIN_STATE_VALIDATION
761  int actualValue = palReadPad(m_port, m_pin);
762  // we had enough drama with pin configuration in board.h and else that we shall self-check
763 
764  const int logicalValue =
765  (outputMode == OM_INVERTED)
766  ? !actualValue
767  : actualValue;
768 
769  // if the pin was set to logical 1, then set an error and disable the pin so that things don't catch fire
770  if (logicalValue) {
771  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));
773  }
774 #endif
775  }
776  }
777 #endif /* EFI_GPIO_HARDWARE */
778 }
779 
781  // Unregister under lock - we don't want other threads mucking with the pin while we're trying to turn it off
782  chibios_rt::CriticalSectionLocker csl;
783 
784  // nothing to do if not registered in the first place
785  if (!isBrainPinValid(brainPin)) {
786  return;
787  }
788 
789 #if (BOARD_EXT_GPIOCHIPS > 0)
790  ext = false;
791 #endif // (BOARD_EXT_GPIOCHIPS > 0)
792 
793  efiPrintf("unregistering %s", hwPortname(brainPin));
794 
795 #if EFI_GPIO_HARDWARE && EFI_PROD_CODE
797 #endif /* EFI_GPIO_HARDWARE */
798 
799  // Clear the pin so that it won't get set any more
801 }
802 
803 #if EFI_GPIO_HARDWARE
804 
805 // questionable trick: we avoid using 'getHwPort' and 'getHwPin' in case of errors in order to increase the changes of turning the LED
806 // by reducing stack requirement
810 
811 #if EFI_PROD_CODE
812 static void initErrorLed(Gpio led) {
813  enginePins.errorLedPin.initPin("led: CRITICAL status", led, (LED_PIN_MODE));
814  criticalErrorLedPort = getHwPort("CRITICAL", led);
815  criticalErrorLedPin = getHwPin("CRITICAL", led);
816  criticalErrorLedState = (LED_PIN_MODE == OM_INVERTED) ? 0 : 1;
817 }
818 #endif /* EFI_PROD_CODE */
819 
821 #if EFI_PROD_CODE
822  initErrorLed(LED_CRITICAL_ERROR_BRAIN_PIN);
823 
824  addConsoleAction("gpio_pins", EnginePins::debug);
825 #endif /* EFI_PROD_CODE */
826 }
827 
828 /**
829  * This method is part of fatal error handling.
830  * The whole method is pretty naive, but that's at least something.
831  */
832 void turnAllPinsOff(void) {
833  for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
834  enginePins.injectors[i].setValue(false);
835  enginePins.coils[i].setValue(false);
837  }
840  enginePins.checkEnginePin.setValue(true); // yes this one can go ON
841 }
842 #endif /* EFI_GPIO_HARDWARE */
843 
static SimplePwm alternatorControl("alt")
const char * getPin_output_mode_e(pin_output_mode_e value)
const OutputPin * getOutputOnTheBenchTest()
Definition: bench_test.cpp:32
Utility methods related to bench testing.
void efiSetPadMode(const char *msg, brain_pin_e brainPin, iomode_t mode)
bool qcDirectPinControlMode
TunerStudioOutputChannels outputChannels
Definition: engine.h:99
RegisteredOutputPin harleyAcr2
Definition: efi_gpio.h:94
RegisteredNamedOutputPin harleyAcr
Definition: efi_gpio.h:93
OutputPin accelerometerCs
Definition: efi_gpio.h:123
static void debug()
Definition: efi_gpio.cpp:249
void startPins()
Definition: efi_gpio.cpp:257
InjectorOutputPin injectorsStage2[MAX_CYLINDER_COUNT]
Definition: efi_gpio.h:126
RegisteredOutputPin mainRelay
Definition: efi_gpio.h:76
NamedOutputPin auxValve[AUX_DIGITAL_VALVE_COUNT]
Definition: efi_gpio.h:129
void startAuxValves()
Definition: efi_gpio.cpp:303
void startIgnitionPins()
Definition: efi_gpio.cpp:315
OutputPin o2heater
Definition: efi_gpio.h:97
RegisteredOutputPin fanRelay
Definition: efi_gpio.h:86
IgnitionOutputPin trailingCoils[MAX_CYLINDER_COUNT]
Definition: efi_gpio.h:128
OutputPin errorLedPin
Definition: efi_gpio.h:103
RegisteredOutputPin starterControl
Definition: efi_gpio.h:82
OutputPin debugTriggerSync
Definition: efi_gpio.h:108
void startInjectionPins()
Definition: efi_gpio.cpp:331
void stopInjectionPins()
Definition: efi_gpio.cpp:286
void reset()
Definition: efi_gpio.cpp:271
InjectorOutputPin injectors[MAX_CYLINDER_COUNT]
Definition: efi_gpio.h:125
void stopAuxValves()
Definition: efi_gpio.cpp:293
bool stopPins()
Definition: efi_gpio.cpp:215
RegisteredOutputPin fuelPumpRelay
Definition: efi_gpio.h:91
void unregisterPins()
Definition: efi_gpio.cpp:229
RegisteredOutputPin acRelay
Definition: efi_gpio.h:90
OutputPin * getOutputPinForBenchMode(bench_mode_e idx)
Definition: efi_gpio.cpp:350
IgnitionOutputPin coils[MAX_CYLINDER_COUNT]
Definition: efi_gpio.h:127
OutputPin sdCsPin
Definition: efi_gpio.h:122
RegisteredOutputPin checkEnginePin
Definition: efi_gpio.h:116
RegisteredOutputPin idleSolenoidPin
Definition: efi_gpio.h:110
RegisteredNamedOutputPin hpfpValve
Definition: efi_gpio.h:80
void stopIgnitionPins()
Definition: efi_gpio.cpp:279
void setHigh() override
Definition: efi_gpio.cpp:482
void setLow() override
Definition: efi_gpio.cpp:507
int signalFallSparkId
Definition: efi_gpio.h:35
int8_t coilIndex
Definition: efi_gpio.h:36
const char * getName() const
Definition: efi_gpio.cpp:399
virtual void setLow()
Definition: efi_gpio.cpp:437
void setName(const char *)
Definition: efi_gpio.cpp:403
const char * shortName
Definition: efi_output.h:129
const char * name
Definition: efi_output.h:133
virtual void setHigh()
Definition: efi_gpio.cpp:415
const char * getShortName() const
Definition: efi_gpio.cpp:407
Single output pin reference and state.
Definition: efi_output.h:50
Timer pinToggleTimer
Definition: efi_output.h:81
brain_pin_diag_e getDiag() const
Definition: efi_gpio.cpp:656
uint32_t durationsInStateMs[2]
Definition: efi_output.h:82
bool ext
Definition: efi_output.h:91
void resetToggleStats()
Definition: efi_gpio.cpp:575
void deInit()
Definition: efi_gpio.cpp:780
void initPin(const char *msg, brain_pin_e brainPin, pin_output_mode_e outputMode, bool forceInitWithFatalError=false)
Definition: efi_gpio.cpp:689
bool getLogicValue() const
Definition: efi_gpio.cpp:645
pin_output_mode_e mode
Definition: efi_output.h:105
void setOnchipValue(int electricalValue)
Definition: efi_gpio.cpp:560
bool getAndSet(int logicValue)
Definition: efi_gpio.cpp:552
void setValue(const char *msg, int logicValue, bool isForce=false)
Definition: efi_gpio.cpp:581
void toggle()
Definition: efi_gpio.cpp:548
int8_t currentLogicValue
Definition: efi_output.h:94
ioportid_t m_port
Definition: efi_output.h:72
bool isInitialized() const
Definition: efi_gpio.cpp:536
int pinToggleCounter
Definition: efi_output.h:77
uint8_t m_pin
Definition: efi_output.h:73
brain_pin_e brainPin
Definition: efi_output.h:87
void setDefaultPinState(pin_output_mode_e mode)
Definition: efi_gpio.cpp:650
RegisteredNamedOutputPin(const char *name, size_t pinOffset, size_t pinModeOffset)
Definition: efi_gpio.cpp:66
const char *const registrationName
Definition: efi_gpio.h:53
RegisteredOutputPin(const char *registrationName, size_t pinOffset, size_t pinModeOffset)
Definition: efi_gpio.cpp:74
const uint16_t m_pinModeOffset
Definition: efi_gpio.h:56
const bool m_hasPinMode
Definition: efi_gpio.h:55
bool isPinConfigurationChanged()
Definition: efi_gpio.cpp:97
const uint16_t m_pinOffset
Definition: efi_gpio.h:54
void addConsoleAction(const char *token, Void callback)
Register console action without parameters.
Gpio
@ Unassigned
@ Invalid
int gpiochips_writePad(brain_pin_e pin, int value)
Set value to gpio of gpiochip.
Definition: core.cpp:335
brain_pin_diag_e gpiochips_getDiag(brain_pin_e pin)
Get diagnostic for given gpio.
Definition: core.cpp:373
ioportmask_t criticalErrorLedPin
Definition: efi_gpio.cpp:808
bool verboseMode
const char * laNames[]
Definition: efi_gpio.cpp:40
static const char *const auxValveShortNames[]
Definition: efi_gpio.cpp:62
static const char *const trailNames[]
Definition: efi_gpio.cpp:29
static const char *const injectorNames[]
Definition: efi_gpio.cpp:50
static RegisteredOutputPin * registeredOutputHead
Definition: efi_gpio.cpp:64
void initPrimaryPins()
Definition: efi_gpio.cpp:820
ioportid_t criticalErrorLedPort
Definition: efi_gpio.cpp:807
static const char *const sparkShortNames[]
Definition: efi_gpio.cpp:47
uint8_t criticalErrorLedState
Definition: efi_gpio.cpp:809
static const char *const injectorStage2Names[]
Definition: efi_gpio.cpp:56
EnginePins enginePins
Definition: efi_gpio.cpp:24
static const char *const sparkNames[]
Definition: efi_gpio.cpp:26
static const char *const injectorShortNames[]
Definition: efi_gpio.cpp:53
static const char *const injectorStage2ShortNames[]
Definition: efi_gpio.cpp:59
const char * vvtNames[]
Definition: efi_gpio.cpp:34
void turnAllPinsOff(void)
Definition: efi_gpio.cpp:832
static void initErrorLed(Gpio led)
Definition: efi_gpio.cpp:812
void initMiscOutputPins()
Definition: efi_gpio.cpp:668
static const char *const trailShortNames[]
Definition: efi_gpio.cpp:32
ioportid_t getHwPort(const char *msg, brain_pin_e brainPin)
ioportmask_t getHwPin(const char *msg, brain_pin_e brainPin)
void unregisterEtbPins()
engine_configuration_s & activeConfiguration
Engine * engine
void addEngineSnifferOutputPinEvent(NamedOutputPin *pin, FrontDirection frontDirection)
rusEfi console wave sniffer
bench_mode_e
Definition: engine_types.h:494
@ BENCH_AC_COMPRESSOR_RELAY
Definition: engine_types.h:501
@ BENCH_FAN_RELAY
Definition: engine_types.h:499
@ BENCH_VVT0_VALVE
Definition: engine_types.h:511
@ BENCH_MAIN_RELAY
Definition: engine_types.h:495
@ HD_ACR2
Definition: engine_types.h:524
@ BENCH_IDLE_VALVE
Definition: engine_types.h:503
@ HD_ACR
Definition: engine_types.h:523
@ BENCH_CHECK_ENGINE_LIGHT
Definition: engine_types.h:502
@ BENCH_VVT3_VALVE
Definition: engine_types.h:514
@ BENCH_VVT1_VALVE
Definition: engine_types.h:512
@ BENCH_FAN_RELAY_2
Definition: engine_types.h:500
@ BENCH_HPFP_VALVE
Definition: engine_types.h:504
@ BENCH_FUEL_PUMP
Definition: engine_types.h:496
@ BENCH_STARTER_ENABLE_RELAY
Definition: engine_types.h:497
@ BENCH_VVT2_VALVE
Definition: engine_types.h:513
bool warning(ObdCode code, const char *fmt,...)
void firmwareError(ObdCode code, const char *fmt,...)
uint32_t ioportmask_t
Digital I/O port sized unsigned type.
Definition: hal_pal_lld.h:78
GPIO_TypeDef * ioportid_t
Port Identifier.
Definition: hal_pal_lld.h:102
uint32_t iomode_t
Digital I/O modes.
Definition: hal_pal_lld.h:83
void efiSetPadUnused(brain_pin_e brainPin)
Definition: io_pins.cpp:20
void setMockState(brain_pin_e pin, bool state)
Definition: io_pins.cpp:129
UNUSED(samplingTimeSeconds)
@ CUSTOM_INVALID_MODE_SETTING
@ CUSTOM_OBD_PIN_CONFLICT
@ CUSTOM_ERR_6622
@ CUSTOM_ERR_6586
engine_configuration_s * engineConfiguration
const char * hwPortname(brain_pin_e brainPin)
bool brain_pin_is_onchip(brain_pin_e brainPin)
bool isBrainPinValid(brain_pin_e brainPin)
brain_pin_diag_e
Definition: rusefi_enums.h:43
pin_output_mode_e
Definition: rusefi_enums.h:238
starterRelayDisable("starterRelayDisable", SensorCategory.SENSOR_INPUTS, FieldType.INT8, 389, 1.0, -10000.0, 10000.0, "")
brain_pin_e pin
Definition: stm32_adc.cpp:15
composite packet size
OutputPin * getVvtOutputPin(int index)
Definition: vvt.cpp:145