libxr  1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
linux_gpio.cpp
1#include "linux_gpio.hpp"
2
3#include <fcntl.h>
4#include <linux/gpio.h>
5#include <poll.h>
6#include <sys/ioctl.h>
7#include <unistd.h>
8
9#include <array>
10#include <cerrno>
11#include <cstring>
12
13#include "logger.hpp"
14
15#if !defined(XR_LINUX_GPIO_DISABLE_V2) && defined(GPIO_V2_LINE_FLAG_INPUT) && \
16 defined(GPIO_V2_GET_LINEINFO_IOCTL) && defined(GPIO_V2_GET_LINE_IOCTL) && \
17 defined(GPIO_V2_LINE_SET_CONFIG_IOCTL) && defined(GPIO_V2_LINE_GET_VALUES_IOCTL) && \
18 defined(GPIO_V2_LINE_SET_VALUES_IOCTL) && defined(GPIO_V2_LINE_EVENT_RISING_EDGE) && \
19 defined(GPIO_V2_LINE_EVENT_FALLING_EDGE)
20#define XR_LINUX_GPIO_HAS_V2 1
21#else
22#define XR_LINUX_GPIO_HAS_V2 0
23#endif
24
25#if defined(GPIOHANDLE_REQUEST_BIAS_DISABLE) && \
26 defined(GPIOHANDLE_REQUEST_BIAS_PULL_UP) && \
27 defined(GPIOHANDLE_REQUEST_BIAS_PULL_DOWN)
28#define XR_LINUX_GPIO_V1_HAS_BIAS_FLAGS 1
29#else
30#define XR_LINUX_GPIO_V1_HAS_BIAS_FLAGS 0
31#endif
32
33#if defined(GPIOHANDLE_SET_CONFIG_IOCTL)
34#define XR_LINUX_GPIO_V1_HAS_SET_CONFIG 1
35#else
36#define XR_LINUX_GPIO_V1_HAS_SET_CONFIG 0
37#endif
38
39namespace
40{
41constexpr const char* kLinuxGPIOConsumer = "LinuxGPIO";
42
44 bool interrupt_enabled)
45{
46 if (!interrupt_enabled &&
50 {
52 }
53
54 return desired;
55}
56
57template <size_t N>
58void CopyConsumer(char (&dst)[N], const char* src)
59{
60 std::strncpy(dst, src, N - 1U);
61 dst[N - 1U] = '\0';
62}
63
64#if XR_LINUX_GPIO_HAS_V2
65uint64_t BuildLineFlagsV2(LibXR::GPIO::Configuration config)
66{
67 uint64_t flags = 0U;
68
69 switch (config.direction)
70 {
72 flags |= GPIO_V2_LINE_FLAG_INPUT;
73 break;
75 flags |= GPIO_V2_LINE_FLAG_OUTPUT;
76 break;
78 flags |= GPIO_V2_LINE_FLAG_OUTPUT | GPIO_V2_LINE_FLAG_OPEN_DRAIN;
79 break;
81 flags |= GPIO_V2_LINE_FLAG_INPUT | GPIO_V2_LINE_FLAG_EDGE_RISING;
82 break;
84 flags |= GPIO_V2_LINE_FLAG_INPUT | GPIO_V2_LINE_FLAG_EDGE_FALLING;
85 break;
87 flags |= GPIO_V2_LINE_FLAG_INPUT | GPIO_V2_LINE_FLAG_EDGE_RISING |
88 GPIO_V2_LINE_FLAG_EDGE_FALLING;
89 break;
90 }
91
92 switch (config.pull)
93 {
95 flags |= GPIO_V2_LINE_FLAG_BIAS_DISABLED;
96 break;
98 flags |= GPIO_V2_LINE_FLAG_BIAS_PULL_UP;
99 break;
101 flags |= GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN;
102 break;
103 }
104
105 return flags;
106}
107#endif
108
109uint32_t BuildHandleFlagsV1(LibXR::GPIO::Configuration config)
110{
111 uint32_t flags = 0U;
112
113 switch (config.direction)
114 {
119 flags |= GPIOHANDLE_REQUEST_INPUT;
120 break;
122 flags |= GPIOHANDLE_REQUEST_OUTPUT;
123 break;
125 flags |= GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_OPEN_DRAIN;
126 break;
127 }
128
129 switch (config.pull)
130 {
132#if XR_LINUX_GPIO_V1_HAS_BIAS_FLAGS
133 flags |= GPIOHANDLE_REQUEST_BIAS_DISABLE;
134#endif
135 break;
137#if XR_LINUX_GPIO_V1_HAS_BIAS_FLAGS
138 flags |= GPIOHANDLE_REQUEST_BIAS_PULL_UP;
139#endif
140 break;
142#if XR_LINUX_GPIO_V1_HAS_BIAS_FLAGS
143 flags |= GPIOHANDLE_REQUEST_BIAS_PULL_DOWN;
144#endif
145 break;
146 }
147
148 return flags;
149}
150
151uint32_t BuildEventFlagsV1(LibXR::GPIO::Direction direction)
152{
153 switch (direction)
154 {
156 return GPIOEVENT_REQUEST_RISING_EDGE;
158 return GPIOEVENT_REQUEST_FALLING_EDGE;
160 return GPIOEVENT_REQUEST_BOTH_EDGES;
161 default:
162 return 0U;
163 }
164}
165
166int SetNonBlocking(int fd)
167{
168 const int flags = fcntl(fd, F_GETFL, 0);
169 if (flags < 0)
170 {
171 return -1;
172 }
173
174 return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
175}
176
177void DrainInterruptWakeFd(int fd)
178{
179 std::array<uint8_t, 32> buffer = {};
180 while (true)
181 {
182 const ssize_t bytes = read(fd, buffer.data(), buffer.size());
183 if (bytes > 0)
184 {
185 continue;
186 }
187
188 if ((bytes < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR)))
189 {
190 return;
191 }
192
193 return;
194 }
195}
196
197enum class PollReadableResult : int8_t
198{
199 ERROR = -1,
200 TIMEOUT = 0,
201 DATA = 1,
202 WAKE = 2,
203};
204
205PollReadableResult PollReadable(int fd, int wake_fd, int timeout_ms)
206{
207 std::array<pollfd, 2> poll_fds = {};
208 nfds_t nfds = 0;
209
210 poll_fds[nfds] = {};
211 poll_fds[nfds].fd = fd;
212 poll_fds[nfds].events = POLLIN;
213 ++nfds;
214
215 if (wake_fd >= 0)
216 {
217 poll_fds[nfds] = {};
218 poll_fds[nfds].fd = wake_fd;
219 poll_fds[nfds].events = POLLIN;
220 ++nfds;
221 }
222
223 while (true)
224 {
225 const int ret = poll(poll_fds.data(), nfds, timeout_ms);
226 if ((ret < 0) && (errno == EINTR))
227 {
228 continue;
229 }
230 if (ret < 0)
231 {
232 return PollReadableResult::ERROR;
233 }
234
235 if (ret == 0)
236 {
237 return PollReadableResult::TIMEOUT;
238 }
239
240 if ((wake_fd >= 0) && ((poll_fds[1].revents & POLLIN) != 0))
241 {
242 DrainInterruptWakeFd(wake_fd);
243 return PollReadableResult::WAKE;
244 }
245
246 if ((poll_fds[0].revents & (POLLNVAL | POLLERR | POLLHUP)) != 0)
247 {
248 errno = EBADF;
249 return PollReadableResult::ERROR;
250 }
251
252 if ((poll_fds[0].revents & POLLIN) != 0)
253 {
254 return PollReadableResult::DATA;
255 }
256
257 errno = EIO;
258 return PollReadableResult::ERROR;
259 }
260}
261
262bool IsKnownGPIOEvent(uint32_t event_id)
263{
264 if (event_id == GPIOEVENT_EVENT_RISING_EDGE)
265 {
266 return true;
267 }
268
269 if (event_id == GPIOEVENT_EVENT_FALLING_EDGE)
270 {
271 return true;
272 }
273
274#if XR_LINUX_GPIO_HAS_V2
275 if (event_id == GPIO_V2_LINE_EVENT_RISING_EDGE)
276 {
277 return true;
278 }
279
280 if (event_id == GPIO_V2_LINE_EVENT_FALLING_EDGE)
281 {
282 return true;
283 }
284#endif
285
286 XR_LOG_WARN("Unknown GPIO event id: %u", event_id);
287 return false;
288}
289
290} // namespace
291
292namespace LibXR
293{
294
295LinuxGPIO::LinuxGPIO(const std::string& chip_path, unsigned int line_offset)
296 : chip_path_(chip_path), line_offset_(line_offset)
297{
298 UNUSED(InitInterruptWakePipe());
299 UNUSED(OpenChip());
300}
301
302LinuxGPIO::~LinuxGPIO()
303{
304 interrupt_thread_exit_.store(true);
305 request_kind_.store(RequestKind::NONE);
306 interrupt_enabled_.store(false);
307 NotifyInterruptThread();
308 WaitForInterruptThreadStopped();
309 CloseRequest();
310 CloseChip();
311 CloseInterruptWakePipe();
312}
313
314bool LinuxGPIO::Read()
315{
316 if (EnsureConfigured() != ErrorCode::OK)
317 {
318 return false;
319 }
320
321 const int request_fd = request_fd_.load();
322 if (abi_version_.load() == AbiVersion::V2)
323 {
324#if XR_LINUX_GPIO_HAS_V2
325 gpio_v2_line_values values = {};
326 values.mask = 1ULL;
327 if (ioctl(request_fd, GPIO_V2_LINE_GET_VALUES_IOCTL, &values) < 0)
328 {
329 XR_LOG_ERROR("Failed to read GPIO value: %s", std::strerror(errno));
330 return false;
331 }
332 return (values.bits & 1ULL) != 0U;
333#else
334 ASSERT(false);
335 return false;
336#endif
337 }
338
339 gpiohandle_data values = {};
340 if (ioctl(request_fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &values) < 0)
341 {
342 XR_LOG_ERROR("Failed to read GPIO value: %s", std::strerror(errno));
343 return false;
344 }
345
346 return values.values[0] != 0U;
347}
348
349void LinuxGPIO::Write(bool value)
350{
351 if (EnsureConfigured() != ErrorCode::OK)
352 {
353 return;
354 }
355
356 const int request_fd = request_fd_.load();
357 if (abi_version_.load() == AbiVersion::V2)
358 {
359#if XR_LINUX_GPIO_HAS_V2
360 gpio_v2_line_values values = {};
361 values.mask = 1ULL;
362 values.bits = value ? 1ULL : 0ULL;
363 if (ioctl(request_fd, GPIO_V2_LINE_SET_VALUES_IOCTL, &values) < 0)
364 {
365 XR_LOG_WARN("Failed to write GPIO value: %s", std::strerror(errno));
366 }
367 return;
368#else
369 ASSERT(false);
370 return;
371#endif
372 }
373
374 gpiohandle_data values = {};
375 values.values[0] = value ? 1U : 0U;
376 if (ioctl(request_fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &values) < 0)
377 {
378 XR_LOG_WARN("Failed to write GPIO value: %s", std::strerror(errno));
379 }
380}
381
382ErrorCode LinuxGPIO::EnableInterrupt()
383{
384 if (EnsureConfigured() != ErrorCode::OK)
385 {
386 return ErrorCode::STATE_ERR;
387 }
388
389 if (!IsInterruptDirection(current_config_.direction))
390 {
391 return ErrorCode::ARG_ERR;
392 }
393
394 if (interrupt_enabled_.load())
395 {
396 return ErrorCode::OK;
397 }
398
399 ErrorCode ec = ErrorCode::OK;
400 if (NeedsRequestReopen(current_config_))
401 {
402 ec = ReopenRequest(current_config_);
403 }
404 else if (abi_version_.load() == AbiVersion::V2)
405 {
406 ec = ReconfigureRequestV2(current_config_);
407 }
408 else
409 {
410 ec = ReconfigureRequestV1(current_config_);
411 }
412 if (ec != ErrorCode::OK)
413 {
414 return ec;
415 }
416
417 has_config_ = true;
418 interrupt_enabled_.store(true);
419 StartInterruptThread();
420 return ErrorCode::OK;
421}
422
423ErrorCode LinuxGPIO::DisableInterrupt()
424{
425 if (EnsureConfigured() != ErrorCode::OK)
426 {
427 return ErrorCode::STATE_ERR;
428 }
429
430 if (!IsInterruptDirection(current_config_.direction))
431 {
432 return ErrorCode::ARG_ERR;
433 }
434
435 if (!interrupt_enabled_.load())
436 {
437 return ErrorCode::OK;
438 }
439
440 const Configuration inactive_config = ResolveRequestConfig(current_config_, false);
441 ErrorCode ec = ErrorCode::OK;
442 if (NeedsRequestReopen(inactive_config))
443 {
444 ec = ReopenRequest(inactive_config);
445 }
446 else if (abi_version_.load() == AbiVersion::V2)
447 {
448 ec = ReconfigureRequestV2(inactive_config);
449 }
450 else
451 {
452 ec = ReconfigureRequestV1(inactive_config);
453 }
454 if (ec != ErrorCode::OK)
455 {
456 return ec;
457 }
458
459 has_config_ = true;
460 interrupt_enabled_.store(false);
461 return ErrorCode::OK;
462}
463
464ErrorCode LinuxGPIO::SetConfig(Configuration config)
465{
466 const ErrorCode chip_ready = OpenChip();
467 if (chip_ready != ErrorCode::OK)
468 {
469 return chip_ready;
470 }
471
472 const bool keep_interrupt_enabled =
473 interrupt_enabled_.load() && IsInterruptDirection(config.direction);
474 const Configuration request_config =
475 ResolveRequestConfig(config, keep_interrupt_enabled);
476
477 ErrorCode ec = ErrorCode::OK;
478 if (request_fd_.load() < 0)
479 {
480 ec = ReopenRequest(request_config);
481 }
482 else if (NeedsRequestReopen(request_config))
483 {
484 ec = ReopenRequest(request_config);
485 }
486 else if (abi_version_.load() == AbiVersion::V2)
487 {
488 ec = ReconfigureRequestV2(request_config);
489 }
490 else
491 {
492 ec = ReconfigureRequestV1(request_config);
493 }
494
495 if (ec != ErrorCode::OK)
496 {
497 return ec;
498 }
499
500 current_config_ = config;
501 has_config_ = true;
502 interrupt_enabled_.store(keep_interrupt_enabled);
503 if (keep_interrupt_enabled)
504 {
505 StartInterruptThread();
506 }
507 return ErrorCode::OK;
508}
509
510void LinuxGPIO::StartInterruptThread()
511{
512 if (interrupt_thread_started_.exchange(true))
513 {
514 return;
515 }
516
517 interrupt_thread_.Create<LinuxGPIO*>(
518 this, [](LinuxGPIO* self) { self->InterruptLoop(); }, "irq_gpio",
519 INTERRUPT_THREAD_STACK_SIZE, Thread::Priority::MEDIUM);
520}
521
522void LinuxGPIO::InterruptLoop()
523{
524 while (!interrupt_thread_exit_.load())
525 {
526 if (!interrupt_enabled_.load())
527 {
528 Thread::Sleep(1);
529 continue;
530 }
531
532 if (request_kind_.load() != RequestKind::EVENT)
533 {
534 Thread::Sleep(1);
535 continue;
536 }
537
538 const int fd = request_fd_.load();
539 if (fd < 0)
540 {
541 Thread::Sleep(1);
542 continue;
543 }
544
545 size_t event_count = 0;
546 interrupt_poll_active_.store(true);
547 const ErrorCode pump = PumpEventQueue(fd, abi_version_.load(), event_count, 100);
548 interrupt_poll_active_.store(false);
549 if (pump == ErrorCode::OK)
550 {
551 for (size_t i = 0; i < event_count; ++i)
552 {
553 callback_.Run(false);
554 }
555 continue;
556 }
557
558 if (pump != ErrorCode::EMPTY)
559 {
560 Thread::Sleep(1);
561 }
562 }
563
564 interrupt_poll_active_.store(false);
565 interrupt_thread_started_.store(false);
566}
567
568ErrorCode LinuxGPIO::OpenChip()
569{
570 if (chip_fd_ >= 0)
571 {
572 return ErrorCode::OK;
573 }
574
575 chip_fd_ = open(chip_path_.c_str(), O_RDONLY | O_CLOEXEC);
576 if (chip_fd_ < 0)
577 {
578 XR_LOG_ERROR("Failed to open GPIO chip: %s (%s)", chip_path_.c_str(),
579 std::strerror(errno));
580 ASSERT(false);
581 return ErrorCode::INIT_ERR;
582 }
583
584 return DetectAbiVersion();
585}
586
587void LinuxGPIO::CloseChip()
588{
589 if (chip_fd_ >= 0)
590 {
591 close(chip_fd_);
592 chip_fd_ = -1;
593 }
594 abi_version_.store(AbiVersion::UNKNOWN);
595}
596
597void LinuxGPIO::CloseRequest()
598{
599 request_kind_.store(RequestKind::NONE);
600 interrupt_enabled_.store(false);
601 NotifyInterruptThread();
602 WaitForInterruptLoopIdle();
603
604 const int request_fd = request_fd_.exchange(-1);
605 if (request_fd >= 0)
606 {
607 close(request_fd);
608 }
609
610 request_kind_.store(RequestKind::NONE);
611 interrupt_enabled_.store(false);
612 has_config_ = false;
613}
614
615ErrorCode LinuxGPIO::InitInterruptWakePipe()
616{
617 if (interrupt_wake_pipe_[0] >= 0)
618 {
619 return ErrorCode::OK;
620 }
621
622 if (pipe(interrupt_wake_pipe_) < 0)
623 {
624 XR_LOG_ERROR("Failed to create GPIO interrupt wake pipe: %s", std::strerror(errno));
625 return ErrorCode::INIT_ERR;
626 }
627
628 if ((SetNonBlocking(interrupt_wake_pipe_[0]) < 0) ||
629 (SetNonBlocking(interrupt_wake_pipe_[1]) < 0))
630 {
631 XR_LOG_ERROR("Failed to configure GPIO interrupt wake pipe: %s",
632 std::strerror(errno));
633 CloseInterruptWakePipe();
634 return ErrorCode::INIT_ERR;
635 }
636
637 return ErrorCode::OK;
638}
639
640void LinuxGPIO::CloseInterruptWakePipe()
641{
642 for (int& fd : interrupt_wake_pipe_)
643 {
644 if (fd >= 0)
645 {
646 close(fd);
647 fd = -1;
648 }
649 }
650}
651
652void LinuxGPIO::NotifyInterruptThread()
653{
654 if (interrupt_wake_pipe_[1] < 0)
655 {
656 return;
657 }
658
659 const uint8_t signal = 1U;
660 while (true)
661 {
662 const ssize_t ret = write(interrupt_wake_pipe_[1], &signal, sizeof(signal));
663 if (ret >= 0)
664 {
665 return;
666 }
667
668 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
669 {
670 DrainInterruptWakeFd(interrupt_wake_pipe_[0]);
671 continue;
672 }
673
674 if (errno == EINTR)
675 {
676 continue;
677 }
678
679 return;
680 }
681}
682
683void LinuxGPIO::WaitForInterruptLoopIdle()
684{
685 if (!interrupt_thread_started_.load())
686 {
687 return;
688 }
689
690 for (uint32_t i = 0; i < 200; ++i)
691 {
692 if (!interrupt_poll_active_.load())
693 {
694 return;
695 }
696
697 Thread::Sleep(1);
698 }
699
700 XR_LOG_WARN("Timed out waiting for GPIO interrupt loop to go idle");
701}
702
703void LinuxGPIO::WaitForInterruptThreadStopped()
704{
705 if (!interrupt_thread_started_.load())
706 {
707 return;
708 }
709
710 for (uint32_t i = 0; i < 500; ++i)
711 {
712 if (!interrupt_thread_started_.load())
713 {
714 return;
715 }
716
717 NotifyInterruptThread();
718 Thread::Sleep(1);
719 }
720
721 XR_LOG_WARN("Timed out waiting for GPIO interrupt thread to stop");
722}
723
724ErrorCode LinuxGPIO::DetectAbiVersion()
725{
726 if (abi_version_.load() != AbiVersion::UNKNOWN)
727 {
728 return ErrorCode::OK;
729 }
730
731 gpiochip_info chip_info = {};
732 if (ioctl(chip_fd_, GPIO_GET_CHIPINFO_IOCTL, &chip_info) < 0)
733 {
734 XR_LOG_ERROR("Failed to read GPIO chip info: %s", std::strerror(errno));
735 return ErrorCode::FAILED;
736 }
737
738 if (chip_info.lines == 0U)
739 {
740 XR_LOG_ERROR("GPIO chip exposes zero lines");
741 return ErrorCode::FAILED;
742 }
743
744#if XR_LINUX_GPIO_HAS_V2
745 gpio_v2_line_info info = {};
746 info.offset = (line_offset_ < chip_info.lines) ? line_offset_ : 0U;
747 if (ioctl(chip_fd_, GPIO_V2_GET_LINEINFO_IOCTL, &info) == 0)
748 {
749 abi_version_.store(AbiVersion::V2);
750 return ErrorCode::OK;
751 }
752
753 if ((errno == ENOTTY) || (errno == EINVAL)
754#ifdef EOPNOTSUPP
755 || (errno == EOPNOTSUPP)
756#endif
757 )
758 {
759 abi_version_.store(AbiVersion::V1);
760 return ErrorCode::OK;
761 }
762
763 XR_LOG_ERROR("Failed to probe GPIO chardev ABI: %s", std::strerror(errno));
764 return ErrorCode::FAILED;
765#else
766 abi_version_.store(AbiVersion::V1);
767 return ErrorCode::OK;
768#endif
769}
770
771ErrorCode LinuxGPIO::ReopenRequest(Configuration config)
772{
773 CloseRequest();
774
775 if (abi_version_.load() == AbiVersion::V2)
776 {
777 return OpenRequestV2(config);
778 }
779
780 return OpenRequestV1(config);
781}
782
783ErrorCode LinuxGPIO::OpenRequestV2(Configuration config)
784{
785#if XR_LINUX_GPIO_HAS_V2
786 gpio_v2_line_request request = {};
787 request.offsets[0] = line_offset_;
788 request.num_lines = 1U;
789 request.event_buffer_size =
790 IsInterruptDirection(config.direction) ? EVENT_BUFFER_CAPACITY : 0U;
791 CopyConsumer(request.consumer, kLinuxGPIOConsumer);
792 request.config.flags = BuildLineFlagsV2(config);
793
794 if (ioctl(chip_fd_, GPIO_V2_GET_LINE_IOCTL, &request) < 0)
795 {
796 XR_LOG_ERROR("Failed to request GPIO line (v2): %s", std::strerror(errno));
797 return ErrorCode::FAILED;
798 }
799
800 request_fd_.store(request.fd);
801 request_kind_.store(IsInterruptDirection(config.direction) ? RequestKind::EVENT
802 : RequestKind::HANDLE);
803 if (SetNonBlocking(request.fd) < 0)
804 {
805 XR_LOG_ERROR("Failed to enable non-blocking GPIO request fd: %s",
806 std::strerror(errno));
807 CloseRequest();
808 return ErrorCode::FAILED;
809 }
810
811 return ErrorCode::OK;
812#else
813 (void)config;
814 ASSERT(false);
815 return ErrorCode::FAILED;
816#endif
817}
818
819ErrorCode LinuxGPIO::ReconfigureRequestV2(Configuration config)
820{
821#if XR_LINUX_GPIO_HAS_V2
822 const bool was_interrupt_request =
823 (request_kind_.load() == RequestKind::EVENT) && interrupt_enabled_.load();
824 if (was_interrupt_request)
825 {
826 request_kind_.store(RequestKind::NONE);
827 interrupt_enabled_.store(false);
828 NotifyInterruptThread();
829 WaitForInterruptLoopIdle();
830 }
831
832 gpio_v2_line_config line_config = {};
833 line_config.flags = BuildLineFlagsV2(config);
834
835 if (ioctl(request_fd_.load(), GPIO_V2_LINE_SET_CONFIG_IOCTL, &line_config) < 0)
836 {
837 XR_LOG_ERROR("Failed to reconfigure GPIO line (v2): %s", std::strerror(errno));
838 return ErrorCode::FAILED;
839 }
840
841 request_kind_.store(IsInterruptDirection(config.direction) ? RequestKind::EVENT
842 : RequestKind::HANDLE);
843 if (was_interrupt_request && IsInterruptDirection(config.direction))
844 {
845 interrupt_enabled_.store(true);
846 }
847 return ErrorCode::OK;
848#else
849 (void)config;
850 ASSERT(false);
851 return ErrorCode::FAILED;
852#endif
853}
854
855ErrorCode LinuxGPIO::OpenRequestV1(Configuration config)
856{
857 if (IsInterruptDirection(config.direction))
858 {
859 gpioevent_request request = {};
860 request.lineoffset = line_offset_;
861 request.handleflags = BuildHandleFlagsV1(config);
862 request.eventflags = BuildEventFlagsV1(config.direction);
863 CopyConsumer(request.consumer_label, kLinuxGPIOConsumer);
864
865 if (ioctl(chip_fd_, GPIO_GET_LINEEVENT_IOCTL, &request) < 0)
866 {
867 XR_LOG_ERROR("Failed to request GPIO event line (v1): %s", std::strerror(errno));
868 return ErrorCode::FAILED;
869 }
870
871 request_fd_.store(request.fd);
872 request_kind_.store(RequestKind::EVENT);
873 }
874 else
875 {
876 gpiohandle_request request = {};
877 request.lineoffsets[0] = line_offset_;
878 request.lines = 1U;
879 request.flags = BuildHandleFlagsV1(config);
880 CopyConsumer(request.consumer_label, kLinuxGPIOConsumer);
881
882 if (ioctl(chip_fd_, GPIO_GET_LINEHANDLE_IOCTL, &request) < 0)
883 {
884 XR_LOG_ERROR("Failed to request GPIO handle line (v1): %s", std::strerror(errno));
885 return ErrorCode::FAILED;
886 }
887
888 request_fd_.store(request.fd);
889 request_kind_.store(RequestKind::HANDLE);
890 }
891
892 if (SetNonBlocking(request_fd_.load()) < 0)
893 {
894 XR_LOG_ERROR("Failed to enable non-blocking GPIO request fd: %s",
895 std::strerror(errno));
896 CloseRequest();
897 return ErrorCode::FAILED;
898 }
899
900 return ErrorCode::OK;
901}
902
903ErrorCode LinuxGPIO::ReconfigureRequestV1(Configuration config)
904{
905 if (request_kind_.load() != RequestKind::HANDLE)
906 {
907 return ReopenRequest(config);
908 }
909
910#if XR_LINUX_GPIO_V1_HAS_SET_CONFIG
911 gpiohandle_config handle_config = {};
912 handle_config.flags = BuildHandleFlagsV1(config);
913 if (ioctl(request_fd_.load(), GPIOHANDLE_SET_CONFIG_IOCTL, &handle_config) < 0)
914 {
915 XR_LOG_ERROR("Failed to reconfigure GPIO line (v1): %s", std::strerror(errno));
916 return ErrorCode::FAILED;
917 }
918
919 return ErrorCode::OK;
920#else
921 return ReopenRequest(config);
922#endif
923}
924
925ErrorCode LinuxGPIO::PumpEventQueue(int fd, AbiVersion abi_version, size_t& event_count,
926 int timeout_ms) const
927{
928 if (fd < 0)
929 {
930 return ErrorCode::STATE_ERR;
931 }
932
933 const PollReadableResult ready = PollReadable(fd, interrupt_wake_pipe_[0], timeout_ms);
934 if (ready == PollReadableResult::ERROR)
935 {
936 if (errno == EBADF)
937 {
938 return ErrorCode::EMPTY;
939 }
940 XR_LOG_ERROR("Failed to poll GPIO fd: %s", std::strerror(errno));
941 return ErrorCode::FAILED;
942 }
943
944 if ((ready == PollReadableResult::TIMEOUT) || (ready == PollReadableResult::WAKE))
945 {
946 return ErrorCode::EMPTY;
947 }
948
949 if (abi_version == AbiVersion::V2)
950 {
951 return ReadEventsV2(fd, event_count);
952 }
953
954 return ReadEventsV1(fd, event_count);
955}
956
957ErrorCode LinuxGPIO::ReadEventsV2(int fd, size_t& event_count) const
958{
959#if XR_LINUX_GPIO_HAS_V2
960 bool received = false;
961 while (true)
962 {
963 std::array<gpio_v2_line_event, EVENT_BUFFER_CAPACITY> events = {};
964 const ssize_t bytes = read(fd, events.data(), sizeof(events));
965 if (bytes < 0)
966 {
967 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
968 {
969 break;
970 }
971 if (errno == EBADF)
972 {
973 return received ? ErrorCode::OK : ErrorCode::EMPTY;
974 }
975
976 XR_LOG_ERROR("Failed to read GPIO v2 events: %s", std::strerror(errno));
977 return ErrorCode::FAILED;
978 }
979
980 if (bytes == 0)
981 {
982 break;
983 }
984
985 if ((bytes % static_cast<ssize_t>(sizeof(gpio_v2_line_event))) != 0)
986 {
987 XR_LOG_ERROR("Corrupted GPIO v2 event payload length");
988 return ErrorCode::FAILED;
989 }
990
991 const size_t count = static_cast<size_t>(bytes / sizeof(gpio_v2_line_event));
992 for (size_t i = 0; i < count; ++i)
993 {
994 if (!IsKnownGPIOEvent(events[i].id))
995 {
996 continue;
997 }
998
999 ++event_count;
1000 received = true;
1001 }
1002 }
1003
1004 return received ? ErrorCode::OK : ErrorCode::EMPTY;
1005#else
1006 (void)fd;
1007 event_count = 0U;
1008 ASSERT(false);
1009 return ErrorCode::FAILED;
1010#endif
1011}
1012
1013ErrorCode LinuxGPIO::ReadEventsV1(int fd, size_t& event_count) const
1014{
1015 bool received = false;
1016 while (true)
1017 {
1018 gpioevent_data event = {};
1019 const ssize_t bytes = read(fd, &event, sizeof(event));
1020 if (bytes < 0)
1021 {
1022 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
1023 {
1024 break;
1025 }
1026 if (errno == EBADF)
1027 {
1028 return received ? ErrorCode::OK : ErrorCode::EMPTY;
1029 }
1030
1031 XR_LOG_ERROR("Failed to read GPIO v1 events: %s", std::strerror(errno));
1032 return ErrorCode::FAILED;
1033 }
1034
1035 if (bytes == 0)
1036 {
1037 break;
1038 }
1039
1040 if (bytes != static_cast<ssize_t>(sizeof(event)))
1041 {
1042 XR_LOG_ERROR("Corrupted GPIO v1 event payload length");
1043 return ErrorCode::FAILED;
1044 }
1045
1046 if (!IsKnownGPIOEvent(event.id))
1047 {
1048 continue;
1049 }
1050
1051 ++event_count;
1052 received = true;
1053 }
1054
1055 return received ? ErrorCode::OK : ErrorCode::EMPTY;
1056}
1057
1058ErrorCode LinuxGPIO::EnsureConfigured() const
1059{
1060 if (!has_config_ || (request_fd_.load() < 0))
1061 {
1062 XR_LOG_ERROR("GPIO is not configured");
1063 ASSERT(false);
1064 return ErrorCode::STATE_ERR;
1065 }
1066
1067 return ErrorCode::OK;
1068}
1069bool LinuxGPIO::IsInterruptDirection(Direction direction)
1070{
1071 return direction == Direction::RISING_INTERRUPT ||
1072 direction == Direction::FALL_INTERRUPT ||
1073 direction == Direction::FALL_RISING_INTERRUPT;
1074}
1075
1076bool LinuxGPIO::NeedsRequestReopen(Configuration config) const
1077{
1078 if (request_fd_.load() < 0)
1079 {
1080 return true;
1081 }
1082
1083 const bool old_interrupt =
1084 interrupt_enabled_.load() && IsInterruptDirection(current_config_.direction);
1085 const bool new_interrupt = IsInterruptDirection(config.direction);
1086
1087 if (abi_version_.load() == AbiVersion::V2)
1088 {
1089 return old_interrupt != new_interrupt;
1090 }
1091
1092 if (new_interrupt)
1093 {
1094 return true;
1095 }
1096
1097 if (old_interrupt)
1098 {
1099 return true;
1100 }
1101
1102 return request_kind_.load() != RequestKind::HANDLE;
1103}
1104
1105} // namespace LibXR
Direction
定义 GPIO 引脚的方向类型。Defines the direction types for GPIO pins.
Definition gpio.hpp:20
@ OUTPUT_PUSH_PULL
推挽输出模式。Push-pull output mode.
@ RISING_INTERRUPT
上升沿中断模式。Rising edge interrupt mode.
@ FALL_RISING_INTERRUPT
双沿触发中断模式。Both edge interrupt mode.
@ OUTPUT_OPEN_DRAIN
开漏输出模式。Open-drain output mode.
@ INPUT
输入模式。Input mode.
@ FALL_INTERRUPT
下降沿中断模式。Falling edge interrupt mode.
@ NONE
无上拉或下拉。No pull-up or pull-down.
@ DOWN
下拉模式。Pull-down mode.
@ UP
上拉模式。Pull-up mode.
LibXR 命名空间
Definition ch32_can.hpp:14
ErrorCode
定义错误码枚举
存储 GPIO 配置参数的结构体。Structure storing GPIO configuration parameters.
Definition gpio.hpp:46
Pull pull
GPIO 上拉/下拉配置。GPIO pull-up/pull-down configuration.
Definition gpio.hpp:48
Direction direction
GPIO 引脚方向。GPIO pin direction.
Definition gpio.hpp:47