| Line | Branch | Decision | Exec | Source |
|---|---|---|---|---|
| 1 | /** | |||
| 2 | * @file pin_repository.cpp | |||
| 3 | * @brief I/O pin registry code | |||
| 4 | * | |||
| 5 | * This job of this class is to make sure that we are not using same hardware pin for two | |||
| 6 | * different purposes. | |||
| 7 | * | |||
| 8 | * @date Jan 15, 2013 | |||
| 9 | * @author Andrey Belomutskiy, (c) 2012-2020 | |||
| 10 | */ | |||
| 11 | ||||
| 12 | #include "pch.h" | |||
| 13 | ||||
| 14 | PinRepository pinRepository CCM_OPTIONAL; | |||
| 15 | ||||
| 16 | 2 | static size_t getBrainPinTotalNum() { | ||
| 17 | 2 | return BRAIN_PIN_TOTAL_PINS; | ||
| 18 | } | |||
| 19 | ||||
| 20 | 2 | const char* & getBrainUsedPin(size_t index) { | ||
| 21 | 2 | return pinRepository.getBrainUsedPin(index); | ||
| 22 | } | |||
| 23 | ||||
| 24 | /* Common for firmware and unit tests */ | |||
| 25 | 66006 | bool isBrainPinValid(brain_pin_e brainPin) { | ||
| 26 |
3/4✓ Branch 0 taken 2047 times.
✓ Branch 1 taken 63959 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2047 times.
|
2/2✓ Decision 'true' taken 63959 times.
✓ Decision 'false' taken 2047 times.
|
66006 | if ((brainPin == Gpio::Unassigned) || (brainPin == Gpio::Invalid)) |
| 27 | 63959 | return false; | ||
| 28 | ||||
| 29 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2047 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 2047 times.
|
2047 | if (brainPin > BRAIN_PIN_LAST) |
| 30 | /* something terribly wrong */ | |||
| 31 | ✗ | return false; | ||
| 32 | ||||
| 33 | 2047 | return true; | ||
| 34 | } | |||
| 35 | ||||
| 36 | 1326 | int brainPin_to_index(Gpio brainPin) { | ||
| 37 |
2/2✓ Branch 0 taken 1324 times.
✓ Branch 1 taken 2 times.
|
2/2✓ Decision 'true' taken 1324 times.
✓ Decision 'false' taken 2 times.
|
1326 | if (brainPin < Gpio::A0) |
| 38 | 1324 | return -1; | ||
| 39 | ||||
| 40 | 2 | size_t i = brainPin - Gpio::A0; | ||
| 41 | ||||
| 42 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 2 times.
|
2 | if (i >= getBrainPinTotalNum()) |
| 43 | ✗ | return -1; | ||
| 44 | ||||
| 45 | 2 | return i; | ||
| 46 | } | |||
| 47 | ||||
| 48 | /** | |||
| 49 | * See also brain_pin_markUnused() | |||
| 50 | * @return true if this pin was already used, false otherwise | |||
| 51 | */ | |||
| 52 | ||||
| 53 | ✗ | bool brain_pin_markUsed(Gpio brainPin, const char *msg) { | ||
| 54 | #ifndef EFI_BOOTLOADER | |||
| 55 | // efiPrintf("pin_markUsed: %s on %s", msg, hwPortname(brainPin)); | |||
| 56 | #endif | |||
| 57 | ||||
| 58 | ✗ | int index = brainPin_to_index(brainPin); | ||
| 59 | ✗ | if (index < 0) | ||
| 60 | ✗ | return true; | ||
| 61 | ||||
| 62 | ✗ | if (pinRepository.getBrainUsedPin(index) != nullptr) { | ||
| 63 | // hwPortname and share a buffer behind the scenes, even while they probably never use it for different | |||
| 64 | // values here let's have an explicit second buffer to make this more reliable | |||
| 65 | ✗ | char physicalPinName[32]; | ||
| 66 | ✗ | strncpy(physicalPinName, hwPhysicalPinName(brainPin), sizeof(physicalPinName) - 1); | ||
| 67 | ✗ | criticalError("Pin \"%s\" (%s) required by \"%s\" but is used by \"%s\"", | ||
| 68 | hwPortname(brainPin), | |||
| 69 | physicalPinName, | |||
| 70 | msg, | |||
| 71 | getBrainUsedPin(index)); | |||
| 72 | ✗ | return true; | ||
| 73 | } | |||
| 74 | ||||
| 75 | ✗ | getBrainUsedPin(index) = msg; | ||
| 76 | ✗ | return false; | ||
| 77 | } | |||
| 78 | ||||
| 79 | /** | |||
| 80 | * See also brain_pin_markUsed() | |||
| 81 | */ | |||
| 82 | ||||
| 83 | 1326 | void brain_pin_markUnused(brain_pin_e brainPin) { | ||
| 84 | #ifndef EFI_BOOTLOADER | |||
| 85 | // efiPrintf("pin_markUnused: %s", hwPortname(brainPin)); | |||
| 86 | #endif | |||
| 87 | 1326 | int index = brainPin_to_index(brainPin); | ||
| 88 |
2/2✓ Branch 0 taken 1324 times.
✓ Branch 1 taken 2 times.
|
2/2✓ Decision 'true' taken 1324 times.
✓ Decision 'false' taken 2 times.
|
1326 | if (index < 0) |
| 89 | 1324 | return; | ||
| 90 | ||||
| 91 | 2 | getBrainUsedPin(index) = nullptr; | ||
| 92 | } | |||
| 93 | ||||
| 94 | #if EFI_PROD_CODE | |||
| 95 | #include "memstreams.h" | |||
| 96 | static MemoryStream portNameStream; | |||
| 97 | static char portNameBuffer[20]; | |||
| 98 | #endif /* EFI_PROD_CODE */ | |||
| 99 | ||||
| 100 | 585 | PinRepository::PinRepository() { | ||
| 101 | #if EFI_PROD_CODE | |||
| 102 | msObjectInit(&portNameStream, (uint8_t*) portNameBuffer, sizeof(portNameBuffer), 0); | |||
| 103 | #endif /* EFI_PROD_CODE */ | |||
| 104 | ||||
| 105 | 585 | memset(PIN_USED, 0, sizeof(PIN_USED)); | ||
| 106 | 585 | } | ||
| 107 | ||||
| 108 | #if EFI_PROD_CODE | |||
| 109 | ||||
| 110 | #include "eficonsole.h" | |||
| 111 | #include "drivers/gpio/gpio_ext.h" | |||
| 112 | #include "smart_gpio.h" | |||
| 113 | #include "hardware.h" | |||
| 114 | ||||
| 115 | void pinDiag2string(char *buffer, size_t size, brain_pin_diag_e pin_diag) { | |||
| 116 | /* use autogeneraged helpers here? */ | |||
| 117 | if (pin_diag == PIN_OK) { | |||
| 118 | chsnprintf(buffer, size, "Ok"); | |||
| 119 | } else if (pin_diag != PIN_UNKNOWN) { | |||
| 120 | chsnprintf(buffer, size, "%s%s%s%s%s%s", | |||
| 121 | pin_diag & PIN_DRIVER_OFF ? "driver_off " : "", | |||
| 122 | pin_diag & PIN_OPEN ? "open_load " : "", | |||
| 123 | pin_diag & PIN_SHORT_TO_GND ? "short_to_gnd " : "", | |||
| 124 | pin_diag & PIN_SHORT_TO_BAT ? "short_to_bat " : "", | |||
| 125 | pin_diag & PIN_OVERLOAD ? "overload " : "", | |||
| 126 | pin_diag & PIN_DRIVER_OVERTEMP ? "overtemp": ""); | |||
| 127 | } else { | |||
| 128 | chsnprintf(buffer, size, "INVALID"); | |||
| 129 | } | |||
| 130 | } | |||
| 131 | ||||
| 132 | static brain_pin_e index_to_brainPin(unsigned int i) | |||
| 133 | { | |||
| 134 | if (i < getBrainPinTotalNum()) | |||
| 135 | return Gpio::A0 + i; | |||
| 136 | ||||
| 137 | return Gpio::Invalid; | |||
| 138 | } | |||
| 139 | ||||
| 140 | static void reportPins() { | |||
| 141 | int totalPinsUsed = 0; | |||
| 142 | ||||
| 143 | for (unsigned int i = 0; i < getBrainPinOnchipNum(); i++) { | |||
| 144 | const char *pin_user = getBrainUsedPin(i); | |||
| 145 | ||||
| 146 | /* show used pins */ | |||
| 147 | if (pin_user) { | |||
| 148 | static char pin_state[64]; | |||
| 149 | brain_pin_e brainPin = index_to_brainPin(i); | |||
| 150 | int pin = getBrainPinIndex(brainPin); | |||
| 151 | ioportid_t port = getBrainPinPort(brainPin); | |||
| 152 | debugBrainPin(pin_state, sizeof(pin_state), brainPin); | |||
| 153 | ||||
| 154 | const char *boardPinName = getBoardSpecificPinName(brainPin); | |||
| 155 | efiPrintf("pin %s%d (%s): %s %s", portname(port), pin, boardPinName, pin_user, pin_state); | |||
| 156 | totalPinsUsed++; | |||
| 157 | } | |||
| 158 | } | |||
| 159 | ||||
| 160 | #if (BOARD_EXT_GPIOCHIPS > 0) | |||
| 161 | for (unsigned int i = getBrainPinOnchipNum() ; i < getBrainPinTotalNum(); i++) { | |||
| 162 | static char pin_error[64]; | |||
| 163 | brain_pin_e brainPin = index_to_brainPin(i); | |||
| 164 | ||||
| 165 | const char *pin_name = gpiochips_getPinName(brainPin); | |||
| 166 | const char *pin_user = getBrainUsedPin(i); | |||
| 167 | brain_pin_diag_e pin_diag = gpiochips_getDiag(brainPin); | |||
| 168 | ||||
| 169 | pinDiag2string(pin_error, sizeof(pin_error), pin_diag); | |||
| 170 | ||||
| 171 | /* here show all pins, unused too */ | |||
| 172 | if (pin_name) { | |||
| 173 | // this probably uses a lot of output buffer! | |||
| 174 | efiPrintf("ext %s: %s diagnostic: %s", | |||
| 175 | pin_name, pin_user ? pin_user : "free", pin_error); | |||
| 176 | } else { | |||
| 177 | const char *chip_name = gpiochips_getChipName(brainPin); | |||
| 178 | /* if chip exist */ | |||
| 179 | if (chip_name) { | |||
| 180 | efiPrintf("ext %s.%d: %s diagnostic: %s", | |||
| 181 | chip_name, gpiochips_getPinOffset(brainPin), pin_user ? pin_user : "free", pin_error); | |||
| 182 | } | |||
| 183 | } | |||
| 184 | if (pin_user) { | |||
| 185 | totalPinsUsed++; | |||
| 186 | } | |||
| 187 | } | |||
| 188 | #endif | |||
| 189 | ||||
| 190 | efiPrintf("Total pins used: %d", totalPinsUsed); | |||
| 191 | ||||
| 192 | gpiochips_debug(); | |||
| 193 | } | |||
| 194 | ||||
| 195 | __attribute__((weak)) const char * getBoardSpecificPinName(brain_pin_e /*brainPin*/) { | |||
| 196 | return nullptr; | |||
| 197 | } | |||
| 198 | ||||
| 199 | const char *hwOnChipPhysicalPinName(ioportid_t hwPort, int hwPin) { | |||
| 200 | portNameStream.eos = 0; // reset | |||
| 201 | if (!hwPort) { | |||
| 202 | return "NONE"; | |||
| 203 | } | |||
| 204 | chprintf((BaseSequentialStream *) &portNameStream, "%s%d", portname(hwPort), hwPin); | |||
| 205 | portNameStream.buffer[portNameStream.eos] = 0; // need to terminate explicitly | |||
| 206 | return portNameBuffer; | |||
| 207 | } | |||
| 208 | ||||
| 209 | const char *hwPhysicalPinName(Gpio brainPin) { | |||
| 210 | if (brainPin == Gpio::Invalid) { | |||
| 211 | return "INVALID"; | |||
| 212 | } | |||
| 213 | if (brainPin == Gpio::Unassigned) { | |||
| 214 | return "NONE"; | |||
| 215 | } | |||
| 216 | ||||
| 217 | if (brain_pin_is_onchip(brainPin)) { | |||
| 218 | ioportid_t hwPort = getHwPort("hostname", brainPin); | |||
| 219 | int hwPin = getHwPin("hostname", brainPin); | |||
| 220 | return hwOnChipPhysicalPinName(hwPort, hwPin); | |||
| 221 | } | |||
| 222 | #if (BOARD_EXT_GPIOCHIPS > 0) | |||
| 223 | else { | |||
| 224 | portNameStream.eos = 0; // reset | |||
| 225 | const char *pin_name = gpiochips_getPinName(brainPin); | |||
| 226 | ||||
| 227 | if (pin_name) { | |||
| 228 | chprintf((BaseSequentialStream *) &portNameStream, "ext:%s", | |||
| 229 | pin_name); | |||
| 230 | } else { | |||
| 231 | chprintf((BaseSequentialStream *) &portNameStream, "ext:%s.%d", | |||
| 232 | gpiochips_getChipName(brainPin), gpiochips_getPinOffset(brainPin)); | |||
| 233 | } | |||
| 234 | portNameStream.buffer[portNameStream.eos] = 0; // need to terminate explicitly | |||
| 235 | return portNameBuffer; | |||
| 236 | } | |||
| 237 | #endif | |||
| 238 | return "unexpected"; | |||
| 239 | } | |||
| 240 | ||||
| 241 | const char *hwPortname(brain_pin_e brainPin) { | |||
| 242 | const char * boardSpecificPinName = getBoardSpecificPinName(brainPin); | |||
| 243 | if (boardSpecificPinName != nullptr) { | |||
| 244 | return boardSpecificPinName; | |||
| 245 | } | |||
| 246 | return hwPhysicalPinName(brainPin); | |||
| 247 | } | |||
| 248 | ||||
| 249 | void initPinRepository(void) { | |||
| 250 | /** | |||
| 251 | * this method cannot use console because this method is invoked before console is initialized | |||
| 252 | */ | |||
| 253 | ||||
| 254 | addConsoleAction(CMD_PINS, reportPins); | |||
| 255 | ||||
| 256 | #if (BOARD_TLE8888_COUNT > 0) | |||
| 257 | addConsoleAction("tle8888", tle8888_dump_regs); | |||
| 258 | addConsoleAction("tle8888init", tle8888_req_init); | |||
| 259 | #endif | |||
| 260 | } | |||
| 261 | ||||
| 262 | bool brain_pin_is_onchip(brain_pin_e brainPin) | |||
| 263 | { | |||
| 264 | if ((brainPin < Gpio::A0) || (brainPin > BRAIN_PIN_ONCHIP_LAST)) | |||
| 265 | return false; | |||
| 266 | ||||
| 267 | return true; | |||
| 268 | } | |||
| 269 | ||||
| 270 | bool brain_pin_is_ext(brain_pin_e brainPin) | |||
| 271 | { | |||
| 272 | if (brainPin > BRAIN_PIN_ONCHIP_LAST) | |||
| 273 | return true; | |||
| 274 | ||||
| 275 | return false; | |||
| 276 | } | |||
| 277 | ||||
| 278 | /** | |||
| 279 | * Marks on-chip gpio port-pin as used. Works only for on-chip gpios | |||
| 280 | * To be replaced with brain_pin_markUsed later | |||
| 281 | */ | |||
| 282 | ||||
| 283 | bool gpio_pin_markUsed(ioportid_t port, ioportmask_t pin, const char *msg) { | |||
| 284 | int index = getPortPinIndex(port, pin); | |||
| 285 | #ifndef EFI_BOOTLOADER | |||
| 286 | // efiPrintf("pin_markUsed: %s on %s", msg, hwOnChipPhysicalPinName(port, pin)); | |||
| 287 | #endif | |||
| 288 | ||||
| 289 | if (getBrainUsedPin(index) != NULL) { | |||
| 290 | /** | |||
| 291 | * todo: the problem is that this warning happens before the console is even | |||
| 292 | * connected, so the warning is never displayed on the console and that's quite a problem! | |||
| 293 | */ | |||
| 294 | // warning(ObdCode::OBD_PCM_Processor_Fault, "%s%d req by %s used by %s", portname(port), pin, msg, getBrainUsedPin(index)); | |||
| 295 | firmwareError(ObdCode::CUSTOM_ERR_PIN_ALREADY_USED_1, "%s%d req by %s used by %s", portname(port), (int)pin, msg, getBrainUsedPin(index)); | |||
| 296 | return true; | |||
| 297 | } | |||
| 298 | getBrainUsedPin(index) = msg; | |||
| 299 | return false; | |||
| 300 | } | |||
| 301 | ||||
| 302 | /** | |||
| 303 | * Marks on-chip gpio port-pin as UNused. Works only for on-chip gpios | |||
| 304 | * To be replaced with brain_pin_markUnused later | |||
| 305 | */ | |||
| 306 | ||||
| 307 | void gpio_pin_markUnused(ioportid_t port, ioportmask_t pin) { | |||
| 308 | int index = getPortPinIndex(port, pin); | |||
| 309 | ||||
| 310 | getBrainUsedPin(index) = nullptr; | |||
| 311 | } | |||
| 312 | ||||
| 313 | const char *getPinFunction(brain_input_pin_e brainPin) { | |||
| 314 | int index; | |||
| 315 | ||||
| 316 | index = brainPin_to_index(brainPin); | |||
| 317 | if (index < 0) | |||
| 318 | return NULL; | |||
| 319 | ||||
| 320 | return getBrainUsedPin(index); | |||
| 321 | } | |||
| 322 | #else | |||
| 323 | ✗ | const char *hwPhysicalPinName(Gpio brainPin) { | ||
| 324 | ✗ | return "N/A"; | ||
| 325 | } | |||
| 326 | 8 | const char *hwPortname(Gpio brainPin) { | ||
| 327 | (void)brainPin; | |||
| 328 | 8 | return "N/A"; | ||
| 329 | } | |||
| 330 | #endif /* EFI_PROD_CODE */ | |||
| 331 |