GCC Code Coverage Report


Directory: ./
File: firmware/hw_layer/drivers/gpio/core.cpp
Date: 2025-10-03 00:57:22
Coverage Exec Excl Total
Lines: 48.7% 55 0 113
Functions: 42.9% 6 0 14
Branches: 55.3% 42 0 76
Decisions: 54.8% 34 - 62

Line Branch Decision Exec Source
1 /**
2 * @file gpio/core.c
3 * @brief EFI-related GPIO code for external gpio chips
4 *
5 * @date Mar 8, 2019
6 * @author Andrey Gusakov, (c) 2019
7 */
8
9 #include "pch.h"
10 #include "gpio/gpio_ext.h"
11 #include "smart_gpio.h"
12
13 #define STRING2(x) #x
14 #define STRING(x) STRING2(x)
15 #pragma message(STRING(BOARD_EXT_GPIOCHIPS))
16
17 #if (BOARD_EXT_GPIOCHIPS > 0)
18
19 /*==========================================================================*/
20 /* Local definitions. */
21 /*==========================================================================*/
22
23 /*==========================================================================*/
24 /* Exported variables. */
25 /*==========================================================================*/
26
27 /*==========================================================================*/
28 /* Local variables and types. */
29 /*==========================================================================*/
30
31 /* TODO: change array to list? */
32 struct gpiochip {
33 brain_pin_e base;
34 size_t size;
35 GpioChip *chip;
36 const char *name;
37 /* optional names of each gpio */
38 const char **gpio_names;
39 };
40
41 static gpiochip chips[BOARD_EXT_GPIOCHIPS];
42
43 #if EFI_PROD_CODE
44
45 /* TODO: move inside gpio chip driver? */
46 class external_hardware_pwm : public hardware_pwm {
47 public:
48 bool hasInit() const {
49 return m_chip != nullptr;
50 }
51
52 int start(const char* msg, gpiochip* chip, size_t pin, float frequency, float duty) {
53 int ret;
54
55 ret = chip->chip->setPadPWM(pin, frequency, duty);
56 if (ret >= 0) {
57 m_chip = chip;
58 m_pin = pin;
59 m_frequency = frequency;
60 } else {
61 /* This is not an error, will fallback to SW PWM */
62 //firmwareError(ObdCode::CUSTOM_GPIO_CHIP_FAILED_PWM, "Faield to enable PWM mode for chip %s on pin \"%s\"", msg, chip->name, pin);
63 efiPrintf("Faield to enable PWM mode for chip %s on pin %d \"%s\"", chip->name, pin, msg);
64 return -1;
65 }
66 return 0;
67 }
68
69 void setDuty(float duty) override {
70 if (!m_chip) {
71 criticalError("Attempted to set duty on null external PWM device");
72 return;
73 }
74
75 m_chip->chip->setPadPWM(m_pin, m_frequency, duty);
76 }
77
78 private:
79 gpiochip* m_chip = nullptr;
80 size_t m_pin = 0;
81 float m_frequency = 0;
82 };
83
84 /* TODO: is 5 enought? */
85 static external_hardware_pwm extPwms[5];
86
87 #endif
88
89 /*==========================================================================*/
90 /* Local functions. */
91 /*==========================================================================*/
92
93 /**
94 * @return pointer to GPIO device for specified pin
95 */
96 10 static gpiochip *gpiochip_find(brain_pin_e pin)
97 {
98
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 6 times.
2/2
✓ Decision 'true' taken 24 times.
✓ Decision 'false' taken 6 times.
30 for (int i = 0; i < BOARD_EXT_GPIOCHIPS; i++) {
99 24 gpiochip *chip = &chips[i];
100
101
6/6
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 4 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 16 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 20 times.
2/2
✓ Decision 'true' taken 4 times.
✓ Decision 'false' taken 20 times.
24 if ((pin >= chip->base) && (pin < (chip->base + chip->size)))
102 4 return chip;
103 }
104
105 6 return nullptr;
106 }
107
108 #if EFI_PROD_CODE
109
110 static external_hardware_pwm* gpiochip_getNextPwmDevice() {
111 for (size_t i = 0; i < efi::size(extPwms); i++) {
112 if (!extPwms[i].hasInit()) {
113 return &extPwms[i];
114 }
115 }
116
117 criticalError("Run out of gpiochip PWM devices!");
118 return nullptr;
119 }
120
121 #endif
122
123 /*==========================================================================*/
124 /* Exported functions. */
125 /*==========================================================================*/
126
127 /**
128 * return numeric part of EXTERNAL pin name.
129 * @details
130 */
131
132 int gpiochips_getPinOffset(brain_pin_e pin)
133 {
134 gpiochip *chip = gpiochip_find(pin);
135
136 if (chip)
137 return pin - chip->base;
138
139 return EFI_ERROR_CODE;
140 }
141
142
143 /**
144 * @brief Get external chip name
145 * @details return gpiochip name
146 */
147
148 const char *gpiochips_getChipName(brain_pin_e pin) {
149 gpiochip *chip = gpiochip_find(pin);
150
151 if (chip)
152 return chip->name;
153
154 return nullptr;
155 }
156
157 /**
158 * @brief Get external chip port name
159 * @details return pin name or gpiochip name (if no pins names provided)
160 */
161
162 const char *gpiochips_getPinName(brain_pin_e pin)
163 {
164 int offset;
165 gpiochip *chip = gpiochip_find(pin);
166
167 if (chip) {
168 offset = pin - chip->base;
169 if ((chip->gpio_names) && (chip->gpio_names[offset]))
170 return chip->gpio_names[offset];
171 }
172
173 return nullptr;
174 }
175
176 /**
177 * @brief Register gpiochip
178 * @details should be called from board file. Can be called before os ready.
179 * All chips should be registered before gpiochips_init() called.
180 * returns -101 in case of no free chips left
181 * returns -100 in case of no ops provided, incorrect chip size
182 * returns -102 or -103 in case chip overlaps already registered chip(s)
183 * else returns chip base
184 */
185
186
1/1
✓ Decision 'true' taken 6 times.
6 int gpiochip_register(brain_pin_e base, const char *name, GpioChip& gpioChip, size_t size) {
187 /* zero size? */
188
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
2/2
✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 4 times.
6 if (!size)
189 2 return -100;
190
191 /* outside? */
192
3/6
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 4 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 4 times.
4 if ((base + size - 1 > BRAIN_PIN_LAST) || (base <= BRAIN_PIN_ONCHIP_LAST))
193 return -101;
194
195 /* check for overlap with other chips */
196
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 3 times.
2/2
✓ Decision 'true' taken 10 times.
✓ Decision 'false' taken 3 times.
13 for (int i = 0; i < BOARD_EXT_GPIOCHIPS; i++) {
197
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 6 times.
2/2
✓ Decision 'true' taken 4 times.
✓ Decision 'false' taken 6 times.
10 if (chips[i].base != Gpio::Unassigned) {
198 #define in_range(a, b, c) (((a) > (b)) && ((a) < (c)))
199
5/6
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 3 times.
✓ Branch 8 taken 1 time.
✓ Branch 9 taken 3 times.
2/2
✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 3 times.
4 if (in_range(base, chips[i].base, chips[i].base + chips[i].size))
200 1 return -102;
201
3/6
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 3 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 3 times.
3 if (in_range(base + size, chips[i].base, chips[i].base + chips[i].size))
202 return -103;
203 }
204 }
205
206 3 gpiochip *chip = nullptr;
207
208 /* find free gpiochip struct */
209
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
1/2
✓ Decision 'true' taken 6 times.
✗ Decision 'false' not taken.
6 for (int i = 0; i < BOARD_EXT_GPIOCHIPS; i++) {
210
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
2/2
✓ Decision 'true' taken 3 times.
✓ Decision 'false' taken 3 times.
6 if (chips[i].base == Gpio::Unassigned) {
211 3 chip = &chips[i];
212 3 break;
213 }
214 }
215
216 /* no free chips left */
217
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 3 times.
3 if (!chip) {
218 return -104;
219 }
220
221 /* register chip */
222 3 chip->name = name;
223 3 chip->chip = &gpioChip;
224 3 chip->base = base;
225 3 chip->size = size;
226 3 chip->gpio_names = nullptr;
227
228 // TODO: this cast seems wrong?
229 3 return (int)base;
230 }
231
232
233 /**
234 * @brief Unregister gpiochip
235 * @details removes chip from list
236 * TODO: call deinit?
237 */
238
239 int gpiochip_unregister(brain_pin_e base)
240 {
241 gpiochip *chip = gpiochip_find(base);
242
243 if (!chip)
244 return -105;
245
246 /* gpiochip_find - returns chip if base within its range, but we need it to be base */
247 if (chip->base != base)
248 return -106;
249
250 /* unregister chip */
251 chip->name = nullptr;
252 chip->chip = nullptr;
253 chip->base = Gpio::Unassigned;
254 chip->size = 0;
255 chip->gpio_names = nullptr;
256
257 return 0;
258 }
259
260 /**
261 * @brief Set pins names for registered gpiochip
262 * @details should be called after chip registration. May be called several times
263 * Names array size should be aqual to chip gpio count
264 */
265
266 int gpiochips_setPinNames(brain_pin_e base, const char **names)
267 {
268 gpiochip *chip = gpiochip_find(base);
269
270 if (!chip)
271 return -113;
272
273 chip->gpio_names = names;
274
275 return 0;
276 }
277
278 /**
279 * @brief Init all registered gpiochips
280 * @details will call gpiochip init ops for all registered chips
281 * calles when OS is ready, so gpiochip can start threads, use drivers and so on.
282 */
283
284 1 int gpiochips_init(void) {
285 1 int pins_added = 0;
286
287
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 time.
2/2
✓ Decision 'true' taken 3 times.
✓ Decision 'false' taken 1 time.
4 for (int i = 0; i < BOARD_EXT_GPIOCHIPS; i++) {
288 3 gpiochip *chip = &chips[i];
289
290
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 3 times.
3 if (chip->base == Gpio::Unassigned)
291 continue;
292
293 3 int ret = chip->chip->init();
294
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
2/2
✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 2 times.
3 if (ret < 0) {
295 #if EFI_PROD_CODE
296 // todo: adjust unit tests to validate this fatal
297 criticalError("Failed to init chip %d: %d", i, ret);
298 #else
299 1 chip->base = Gpio::Unassigned;
300 #endif
301 } else {
302 2 pins_added += chip->size;
303 }
304 }
305
306 1 return pins_added;
307 }
308
309 /**
310 * @brief Set pin mode of gpiochip
311 * @details set pad mode for given pin.
312 * return -107 if driver does not implemet setPadMode ops
313 * else return value from gpiochip driver.
314 */
315 /* this fuction uses iomode_t that is related to STM32 (or other MCU)
316 * output modes. Use some common enums? */
317 int gpiochips_setPadMode(brain_pin_e pin, iomode_t mode)
318 {
319 gpiochip *chip = gpiochip_find(pin);
320
321 if (!chip)
322 return -107;
323
324 return chip->chip->setPadMode(pin - chip->base, mode);
325 }
326
327 /**
328 * @brief Set value to gpio of gpiochip
329 * @details actual output value depent on current gpiochip implementation
330 * for smart switch inactive supposed to be closed switch (no current flows)
331 * returns -1 in case of pin not belong to any gpio chip
332 * returns -1 in case of chip does not support seting output value (input only)
333 * else return value from gpiochip driver;
334 */
335
336
1/1
✓ Decision 'true' taken 6 times.
6 int gpiochips_writePad(brain_pin_e pin, int value) {
337 #if EFI_PROD_CODE
338 extern bool isInHardFaultHandler;
339 // todo: technical debt, how do we turn off smart GPIO?!
340 if (isInHardFaultHandler) {
341 return -130;
342 }
343 #endif // EFI_PROD_CODE
344 6 gpiochip *chip = gpiochip_find(pin);
345
346
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
2/2
✓ Decision 'true' taken 4 times.
✓ Decision 'false' taken 2 times.
6 if (!chip) {
347 // todo: make readPad fail in a similar way?
348 4 criticalError("Failed migration? Time to reset settings? gpiochip not found for pin %d", pin);
349 return -108;
350 }
351
352 2 return chip->chip->writePad(pin - chip->base, value);
353 }
354
355 /**
356 * @brief Get value to gpio of gpiochip
357 * @details actual input value depent on current gpiochip implementation
358 * returns -1 in case of pin not belong to any gpio chip
359 * returns -1 in case of chip does not support getting output value (output only)
360 * else return value from gpiochip driver;
361 */
362
363
1/1
✓ Decision 'true' taken 4 times.
4 int gpiochips_readPad(brain_pin_e pin)
364 {
365 4 gpiochip *chip = gpiochip_find(pin);
366
367
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
2/2
✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 2 times.
4 if (!chip)
368 2 return -109;
369
370 2 return chip->chip->readPad(pin - chip->base);
371 }
372
373 /**
374 * @brief Get diagnostic for given gpio
375 * @details actual output value depend on gpiochip capabilities
376 * returns PIN_UNKNOWN in case of pin not belong to any gpio chip
377 * returns PIN_OK in case of chip does not support getting diagnostic
378 * else return brain_pin_diag_e from gpiochip driver;
379 */
380
381 brain_pin_diag_e gpiochips_getDiag(brain_pin_e pin) {
382 gpiochip *chip = gpiochip_find(pin);
383
384 if (!chip)
385 return PIN_UNKNOWN;
386
387 return chip->chip->getDiag(pin - chip->base);
388 }
389
390 /**
391 * @brief Get total pin count allocated for external gpio chips.
392 * @details Will also include unused pins for chips that was registered
393 * but later fails to init.
394 */
395
396 2 int gpiochips_get_total_pins(void)
397 {
398 int i;
399 2 int cnt = 0;
400
401
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
2/2
✓ Decision 'true' taken 6 times.
✓ Decision 'false' taken 2 times.
8 for (i = 0; i < BOARD_EXT_GPIOCHIPS; i++) {
402 6 gpiochip *chip = &chips[i];
403
404
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
2/2
✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 4 times.
6 if (chip->base == Gpio::Unassigned)
405 2 continue;
406
407 4 cnt += chip->size;
408 }
409
410 2 return cnt;
411 }
412
413 void gpiochips_debug(void)
414 {
415 int i;
416
417 for (i = 0; i < BOARD_EXT_GPIOCHIPS; i++) {
418 gpiochip *chip = &chips[i];
419
420 if (chip->base == Gpio::Unassigned)
421 continue;
422
423 efiPrintf("%s (base %d, size %d):\n", chip->name, (int)chip->base, chip->size);
424 chip->chip->debug();
425 }
426 }
427
428 #if EFI_PROD_CODE
429
430 /**
431 * @brief Try to init PWM on given pin
432 * @details success of call depends on chip capabilities
433 * returns nullptr in case there is no chip for given pin
434 * returns nullptr in case of pin is not PWM capable
435 * returns nullptr in case all extPwms are already used
436 * returns hardware_pwm if succes, later user can call ->setDuty to change duty
437 */
438
439 hardware_pwm* gpiochip_tryInitPwm(const char* msg, brain_pin_e pin, float frequency, float duty)
440 {
441 gpiochip *chip = gpiochip_find(pin);
442
443 if (!chip) {
444 return nullptr;
445 }
446
447 /* TODO: implement reintialization of same pin with different settings reusing same external_hardware_pwm */
448 if (external_hardware_pwm *device = gpiochip_getNextPwmDevice()) {
449 if (device->start(msg, chip, pin - chip->base, frequency, duty) >= 0) {
450 return device;
451 }
452 }
453
454 return nullptr;
455 }
456
457 #endif
458
459 #else /* BOARD_EXT_GPIOCHIPS > 0 */
460
461 int gpiochips_getPinOffset(brain_pin_e pin) {
462 (void)pin;
463
464 return -111;
465 }
466
467 const char *gpiochips_getChipName(brain_pin_e pin) {
468 (void)pin;
469
470 return nullptr;
471 }
472
473 const char *gpiochips_getPinName(brain_pin_e pin) {
474 (void)pin;
475
476 return nullptr;
477 }
478
479 int gpiochip_register(brain_pin_e base, const char *name, GpioChip&, size_t size) {
480 (void)base; (void)name; (void)size;
481
482 return 0;
483 }
484
485 int gpiochips_setPinNames(brain_pin_e pin, const char **names)
486 {
487 (void)pin; (void)names;
488
489 return 0;
490 }
491
492 int gpiochips_init(void) {
493 return 0;
494 }
495
496 int gpiochips_get_total_pins(void)
497 {
498 return 0;
499 }
500
501 void gpiochips_debug(void)
502 {
503 }
504
505 #endif /* BOARD_EXT_GPIOCHIPS > 0 */
506