libxr
Embedded C++ framework for bare-metal, RTOS and Linux
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* LINUX_GPIO_CONSUMER = "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 return false;
335#endif
336 }
337
338 gpiohandle_data values = {};
339 if (ioctl(request_fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &values) < 0)
340 {
341 XR_LOG_ERROR("Failed to read GPIO value: %s", std::strerror(errno));
342 return false;
343 }
344
345 return values.values[0] != 0U;
346}
347
348void LinuxGPIO::Write(bool value)
349{
350 if (EnsureConfigured() != ErrorCode::OK)
351 {
352 return;
353 }
354
355 const int request_fd = request_fd_.load();
356 if (abi_version_.load() == AbiVersion::V2)
357 {
358#if XR_LINUX_GPIO_HAS_V2
359 gpio_v2_line_values values = {};
360 values.mask = 1ULL;
361 values.bits = value ? 1ULL : 0ULL;
362 if (ioctl(request_fd, GPIO_V2_LINE_SET_VALUES_IOCTL, &values) < 0)
363 {
364 XR_LOG_WARN("Failed to write GPIO value: %s", std::strerror(errno));
365 }
366 return;
367#else
368 return;
369#endif
370 }
371
372 gpiohandle_data values = {};
373 values.values[0] = value ? 1U : 0U;
374 if (ioctl(request_fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &values) < 0)
375 {
376 XR_LOG_WARN("Failed to write GPIO value: %s", std::strerror(errno));
377 }
378}
379
380ErrorCode LinuxGPIO::EnableInterrupt()
381{
382 if (EnsureConfigured() != ErrorCode::OK)
383 {
384 return ErrorCode::STATE_ERR;
385 }
386
387 if (!IsInterruptDirection(current_config_.direction))
388 {
389 return ErrorCode::ARG_ERR;
390 }
391
392 if (interrupt_enabled_.load())
393 {
394 return ErrorCode::OK;
395 }
396
397 ErrorCode ec = ErrorCode::OK;
398 if (NeedsRequestReopen(current_config_))
399 {
400 ec = ReopenRequest(current_config_);
401 }
402 else if (abi_version_.load() == AbiVersion::V2)
403 {
404 ec = ReconfigureRequestV2(current_config_);
405 }
406 else
407 {
408 ec = ReconfigureRequestV1(current_config_);
409 }
410 if (ec != ErrorCode::OK)
411 {
412 return ec;
413 }
414
415 has_config_ = true;
416 interrupt_enabled_.store(true);
417 StartInterruptThread();
418 return ErrorCode::OK;
419}
420
421ErrorCode LinuxGPIO::DisableInterrupt()
422{
423 if (EnsureConfigured() != ErrorCode::OK)
424 {
425 return ErrorCode::STATE_ERR;
426 }
427
428 if (!IsInterruptDirection(current_config_.direction))
429 {
430 return ErrorCode::ARG_ERR;
431 }
432
433 if (!interrupt_enabled_.load())
434 {
435 return ErrorCode::OK;
436 }
437
438 const Configuration inactive_config = ResolveRequestConfig(current_config_, false);
439 ErrorCode ec = ErrorCode::OK;
440 if (NeedsRequestReopen(inactive_config))
441 {
442 ec = ReopenRequest(inactive_config);
443 }
444 else if (abi_version_.load() == AbiVersion::V2)
445 {
446 ec = ReconfigureRequestV2(inactive_config);
447 }
448 else
449 {
450 ec = ReconfigureRequestV1(inactive_config);
451 }
452 if (ec != ErrorCode::OK)
453 {
454 return ec;
455 }
456
457 has_config_ = true;
458 interrupt_enabled_.store(false);
459 return ErrorCode::OK;
460}
461
462ErrorCode LinuxGPIO::SetConfig(Configuration config)
463{
464 const ErrorCode chip_ready = OpenChip();
465 if (chip_ready != ErrorCode::OK)
466 {
467 return chip_ready;
468 }
469
470 const bool keep_interrupt_enabled =
471 interrupt_enabled_.load() && IsInterruptDirection(config.direction);
472 const Configuration request_config =
473 ResolveRequestConfig(config, keep_interrupt_enabled);
474
475 ErrorCode ec = ErrorCode::OK;
476 if (request_fd_.load() < 0)
477 {
478 ec = ReopenRequest(request_config);
479 }
480 else if (NeedsRequestReopen(request_config))
481 {
482 ec = ReopenRequest(request_config);
483 }
484 else if (abi_version_.load() == AbiVersion::V2)
485 {
486 ec = ReconfigureRequestV2(request_config);
487 }
488 else
489 {
490 ec = ReconfigureRequestV1(request_config);
491 }
492
493 if (ec != ErrorCode::OK)
494 {
495 return ec;
496 }
497
498 current_config_ = config;
499 has_config_ = true;
500 interrupt_enabled_.store(keep_interrupt_enabled);
501 if (keep_interrupt_enabled)
502 {
503 StartInterruptThread();
504 }
505 return ErrorCode::OK;
506}
507
508void LinuxGPIO::StartInterruptThread()
509{
510 if (interrupt_thread_started_.exchange(true))
511 {
512 return;
513 }
514
515 interrupt_thread_.Create<LinuxGPIO*>(
516 this, [](LinuxGPIO* self) { self->InterruptLoop(); }, "irq_gpio",
517 INTERRUPT_THREAD_STACK_SIZE, Thread::Priority::MEDIUM);
518}
519
520void LinuxGPIO::InterruptLoop()
521{
522 while (!interrupt_thread_exit_.load())
523 {
524 if (!interrupt_enabled_.load())
525 {
526 Thread::Sleep(1);
527 continue;
528 }
529
530 if (request_kind_.load() != RequestKind::EVENT)
531 {
532 Thread::Sleep(1);
533 continue;
534 }
535
536 const int fd = request_fd_.load();
537 if (fd < 0)
538 {
539 Thread::Sleep(1);
540 continue;
541 }
542
543 size_t event_count = 0;
544 interrupt_poll_active_.store(true);
545 const ErrorCode pump = PumpEventQueue(fd, abi_version_.load(), event_count, 100);
546 interrupt_poll_active_.store(false);
547 if (pump == ErrorCode::OK)
548 {
549 for (size_t i = 0; i < event_count; ++i)
550 {
551 callback_.Run(false);
552 }
553 continue;
554 }
555
556 if (pump != ErrorCode::EMPTY)
557 {
558 Thread::Sleep(1);
559 }
560 }
561
562 interrupt_poll_active_.store(false);
563 interrupt_thread_started_.store(false);
564}
565
566ErrorCode LinuxGPIO::OpenChip()
567{
568 if (chip_fd_ >= 0)
569 {
570 return ErrorCode::OK;
571 }
572
573 chip_fd_ = open(chip_path_.c_str(), O_RDONLY | O_CLOEXEC);
574 if (chip_fd_ < 0)
575 {
576 XR_LOG_ERROR("Failed to open GPIO chip: %s (%s)", chip_path_.c_str(),
577 std::strerror(errno));
578 return ErrorCode::INIT_ERR;
579 }
580
581 return DetectAbiVersion();
582}
583
584void LinuxGPIO::CloseChip()
585{
586 if (chip_fd_ >= 0)
587 {
588 close(chip_fd_);
589 chip_fd_ = -1;
590 }
591 abi_version_.store(AbiVersion::UNKNOWN);
592}
593
594void LinuxGPIO::CloseRequest()
595{
596 request_kind_.store(RequestKind::NONE);
597 interrupt_enabled_.store(false);
598 NotifyInterruptThread();
599 WaitForInterruptLoopIdle();
600
601 const int request_fd = request_fd_.exchange(-1);
602 if (request_fd >= 0)
603 {
604 close(request_fd);
605 }
606
607 request_kind_.store(RequestKind::NONE);
608 interrupt_enabled_.store(false);
609 has_config_ = false;
610}
611
612ErrorCode LinuxGPIO::InitInterruptWakePipe()
613{
614 if (interrupt_wake_pipe_[0] >= 0)
615 {
616 return ErrorCode::OK;
617 }
618
619 if (pipe(interrupt_wake_pipe_) < 0)
620 {
621 XR_LOG_ERROR("Failed to create GPIO interrupt wake pipe: %s", std::strerror(errno));
622 return ErrorCode::INIT_ERR;
623 }
624
625 if ((SetNonBlocking(interrupt_wake_pipe_[0]) < 0) ||
626 (SetNonBlocking(interrupt_wake_pipe_[1]) < 0))
627 {
628 XR_LOG_ERROR("Failed to configure GPIO interrupt wake pipe: %s",
629 std::strerror(errno));
630 CloseInterruptWakePipe();
631 return ErrorCode::INIT_ERR;
632 }
633
634 return ErrorCode::OK;
635}
636
637void LinuxGPIO::CloseInterruptWakePipe()
638{
639 for (int& fd : interrupt_wake_pipe_)
640 {
641 if (fd >= 0)
642 {
643 close(fd);
644 fd = -1;
645 }
646 }
647}
648
649void LinuxGPIO::NotifyInterruptThread()
650{
651 if (interrupt_wake_pipe_[1] < 0)
652 {
653 return;
654 }
655
656 const uint8_t signal = 1U;
657 while (true)
658 {
659 const ssize_t ret = write(interrupt_wake_pipe_[1], &signal, sizeof(signal));
660 if (ret >= 0)
661 {
662 return;
663 }
664
665 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
666 {
667 DrainInterruptWakeFd(interrupt_wake_pipe_[0]);
668 continue;
669 }
670
671 if (errno == EINTR)
672 {
673 continue;
674 }
675
676 return;
677 }
678}
679
680void LinuxGPIO::WaitForInterruptLoopIdle()
681{
682 if (!interrupt_thread_started_.load())
683 {
684 return;
685 }
686
687 for (uint32_t i = 0; i < 200; ++i)
688 {
689 if (!interrupt_poll_active_.load())
690 {
691 return;
692 }
693
694 Thread::Sleep(1);
695 }
696
697 XR_LOG_WARN("Timed out waiting for GPIO interrupt loop to go idle");
698}
699
700void LinuxGPIO::WaitForInterruptThreadStopped()
701{
702 if (!interrupt_thread_started_.load())
703 {
704 return;
705 }
706
707 for (uint32_t i = 0; i < 500; ++i)
708 {
709 if (!interrupt_thread_started_.load())
710 {
711 return;
712 }
713
714 NotifyInterruptThread();
715 Thread::Sleep(1);
716 }
717
718 XR_LOG_WARN("Timed out waiting for GPIO interrupt thread to stop");
719}
720
721ErrorCode LinuxGPIO::DetectAbiVersion()
722{
723 if (abi_version_.load() != AbiVersion::UNKNOWN)
724 {
725 return ErrorCode::OK;
726 }
727
728 gpiochip_info chip_info = {};
729 if (ioctl(chip_fd_, GPIO_GET_CHIPINFO_IOCTL, &chip_info) < 0)
730 {
731 XR_LOG_ERROR("Failed to read GPIO chip info: %s", std::strerror(errno));
732 return ErrorCode::FAILED;
733 }
734
735 if (chip_info.lines == 0U)
736 {
737 XR_LOG_ERROR("GPIO chip exposes zero lines");
738 return ErrorCode::FAILED;
739 }
740
741#if XR_LINUX_GPIO_HAS_V2
742 gpio_v2_line_info info = {};
743 info.offset = (line_offset_ < chip_info.lines) ? line_offset_ : 0U;
744 if (ioctl(chip_fd_, GPIO_V2_GET_LINEINFO_IOCTL, &info) == 0)
745 {
746 abi_version_.store(AbiVersion::V2);
747 return ErrorCode::OK;
748 }
749
750 if ((errno == ENOTTY) || (errno == EINVAL)
751#ifdef EOPNOTSUPP
752 || (errno == EOPNOTSUPP)
753#endif
754 )
755 {
756 abi_version_.store(AbiVersion::V1);
757 return ErrorCode::OK;
758 }
759
760 XR_LOG_ERROR("Failed to probe GPIO chardev ABI: %s", std::strerror(errno));
761 return ErrorCode::FAILED;
762#else
763 abi_version_.store(AbiVersion::V1);
764 return ErrorCode::OK;
765#endif
766}
767
768ErrorCode LinuxGPIO::ReopenRequest(Configuration config)
769{
770 CloseRequest();
771
772 if (abi_version_.load() == AbiVersion::V2)
773 {
774 return OpenRequestV2(config);
775 }
776
777 return OpenRequestV1(config);
778}
779
780ErrorCode LinuxGPIO::OpenRequestV2(Configuration config)
781{
782#if XR_LINUX_GPIO_HAS_V2
783 gpio_v2_line_request request = {};
784 request.offsets[0] = line_offset_;
785 request.num_lines = 1U;
786 request.event_buffer_size =
787 IsInterruptDirection(config.direction) ? EVENT_BUFFER_CAPACITY : 0U;
788 CopyConsumer(request.consumer, LINUX_GPIO_CONSUMER);
789 request.config.flags = BuildLineFlagsV2(config);
790
791 if (ioctl(chip_fd_, GPIO_V2_GET_LINE_IOCTL, &request) < 0)
792 {
793 XR_LOG_ERROR("Failed to request GPIO line (v2): %s", std::strerror(errno));
794 return ErrorCode::FAILED;
795 }
796
797 request_fd_.store(request.fd);
798 request_kind_.store(IsInterruptDirection(config.direction) ? RequestKind::EVENT
799 : RequestKind::HANDLE);
800 if (SetNonBlocking(request.fd) < 0)
801 {
802 XR_LOG_ERROR("Failed to enable non-blocking GPIO request fd: %s",
803 std::strerror(errno));
804 CloseRequest();
805 return ErrorCode::FAILED;
806 }
807
808 return ErrorCode::OK;
809#else
810 (void)config;
811 return ErrorCode::NOT_SUPPORT;
812#endif
813}
814
815ErrorCode LinuxGPIO::ReconfigureRequestV2(Configuration config)
816{
817#if XR_LINUX_GPIO_HAS_V2
818 const bool was_interrupt_request =
819 (request_kind_.load() == RequestKind::EVENT) && interrupt_enabled_.load();
820 if (was_interrupt_request)
821 {
822 request_kind_.store(RequestKind::NONE);
823 interrupt_enabled_.store(false);
824 NotifyInterruptThread();
825 WaitForInterruptLoopIdle();
826 }
827
828 gpio_v2_line_config line_config = {};
829 line_config.flags = BuildLineFlagsV2(config);
830
831 if (ioctl(request_fd_.load(), GPIO_V2_LINE_SET_CONFIG_IOCTL, &line_config) < 0)
832 {
833 XR_LOG_ERROR("Failed to reconfigure GPIO line (v2): %s", std::strerror(errno));
834 return ErrorCode::FAILED;
835 }
836
837 request_kind_.store(IsInterruptDirection(config.direction) ? RequestKind::EVENT
838 : RequestKind::HANDLE);
839 if (was_interrupt_request && IsInterruptDirection(config.direction))
840 {
841 interrupt_enabled_.store(true);
842 }
843 return ErrorCode::OK;
844#else
845 (void)config;
846 return ErrorCode::NOT_SUPPORT;
847#endif
848}
849
850ErrorCode LinuxGPIO::OpenRequestV1(Configuration config)
851{
852 if (IsInterruptDirection(config.direction))
853 {
854 gpioevent_request request = {};
855 request.lineoffset = line_offset_;
856 request.handleflags = BuildHandleFlagsV1(config);
857 request.eventflags = BuildEventFlagsV1(config.direction);
858 CopyConsumer(request.consumer_label, LINUX_GPIO_CONSUMER);
859
860 if (ioctl(chip_fd_, GPIO_GET_LINEEVENT_IOCTL, &request) < 0)
861 {
862 XR_LOG_ERROR("Failed to request GPIO event line (v1): %s", std::strerror(errno));
863 return ErrorCode::FAILED;
864 }
865
866 request_fd_.store(request.fd);
867 request_kind_.store(RequestKind::EVENT);
868 }
869 else
870 {
871 gpiohandle_request request = {};
872 request.lineoffsets[0] = line_offset_;
873 request.lines = 1U;
874 request.flags = BuildHandleFlagsV1(config);
875 CopyConsumer(request.consumer_label, LINUX_GPIO_CONSUMER);
876
877 if (ioctl(chip_fd_, GPIO_GET_LINEHANDLE_IOCTL, &request) < 0)
878 {
879 XR_LOG_ERROR("Failed to request GPIO handle line (v1): %s", std::strerror(errno));
880 return ErrorCode::FAILED;
881 }
882
883 request_fd_.store(request.fd);
884 request_kind_.store(RequestKind::HANDLE);
885 }
886
887 if (SetNonBlocking(request_fd_.load()) < 0)
888 {
889 XR_LOG_ERROR("Failed to enable non-blocking GPIO request fd: %s",
890 std::strerror(errno));
891 CloseRequest();
892 return ErrorCode::FAILED;
893 }
894
895 return ErrorCode::OK;
896}
897
898ErrorCode LinuxGPIO::ReconfigureRequestV1(Configuration config)
899{
900 if (request_kind_.load() != RequestKind::HANDLE)
901 {
902 return ReopenRequest(config);
903 }
904
905#if XR_LINUX_GPIO_V1_HAS_SET_CONFIG
906 gpiohandle_config handle_config = {};
907 handle_config.flags = BuildHandleFlagsV1(config);
908 if (ioctl(request_fd_.load(), GPIOHANDLE_SET_CONFIG_IOCTL, &handle_config) < 0)
909 {
910 XR_LOG_ERROR("Failed to reconfigure GPIO line (v1): %s", std::strerror(errno));
911 return ErrorCode::FAILED;
912 }
913
914 return ErrorCode::OK;
915#else
916 return ReopenRequest(config);
917#endif
918}
919
920ErrorCode LinuxGPIO::PumpEventQueue(int fd, AbiVersion abi_version, size_t& event_count,
921 int timeout_ms) const
922{
923 if (fd < 0)
924 {
925 return ErrorCode::STATE_ERR;
926 }
927
928 const PollReadableResult ready = PollReadable(fd, interrupt_wake_pipe_[0], timeout_ms);
929 if (ready == PollReadableResult::ERROR)
930 {
931 if (errno == EBADF)
932 {
933 return ErrorCode::EMPTY;
934 }
935 XR_LOG_ERROR("Failed to poll GPIO fd: %s", std::strerror(errno));
936 return ErrorCode::FAILED;
937 }
938
939 if ((ready == PollReadableResult::TIMEOUT) || (ready == PollReadableResult::WAKE))
940 {
941 return ErrorCode::EMPTY;
942 }
943
944 if (abi_version == AbiVersion::V2)
945 {
946 return ReadEventsV2(fd, event_count);
947 }
948
949 return ReadEventsV1(fd, event_count);
950}
951
952ErrorCode LinuxGPIO::ReadEventsV2(int fd, size_t& event_count) const
953{
954#if XR_LINUX_GPIO_HAS_V2
955 bool received = false;
956 while (true)
957 {
958 std::array<gpio_v2_line_event, EVENT_BUFFER_CAPACITY> events = {};
959 const ssize_t bytes = read(fd, events.data(), sizeof(events));
960 if (bytes < 0)
961 {
962 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
963 {
964 break;
965 }
966 if (errno == EBADF)
967 {
968 return received ? ErrorCode::OK : ErrorCode::EMPTY;
969 }
970
971 XR_LOG_ERROR("Failed to read GPIO v2 events: %s", std::strerror(errno));
972 return ErrorCode::FAILED;
973 }
974
975 if (bytes == 0)
976 {
977 break;
978 }
979
980 if ((bytes % static_cast<ssize_t>(sizeof(gpio_v2_line_event))) != 0)
981 {
982 XR_LOG_ERROR("Corrupted GPIO v2 event payload length");
983 return ErrorCode::FAILED;
984 }
985
986 const size_t count = static_cast<size_t>(bytes / sizeof(gpio_v2_line_event));
987 for (size_t i = 0; i < count; ++i)
988 {
989 if (!IsKnownGPIOEvent(events[i].id))
990 {
991 continue;
992 }
993
994 ++event_count;
995 received = true;
996 }
997 }
998
999 return received ? ErrorCode::OK : ErrorCode::EMPTY;
1000#else
1001 (void)fd;
1002 event_count = 0U;
1003 return ErrorCode::NOT_SUPPORT;
1004#endif
1005}
1006
1007ErrorCode LinuxGPIO::ReadEventsV1(int fd, size_t& event_count) const
1008{
1009 bool received = false;
1010 while (true)
1011 {
1012 gpioevent_data event = {};
1013 const ssize_t bytes = read(fd, &event, sizeof(event));
1014 if (bytes < 0)
1015 {
1016 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
1017 {
1018 break;
1019 }
1020 if (errno == EBADF)
1021 {
1022 return received ? ErrorCode::OK : ErrorCode::EMPTY;
1023 }
1024
1025 XR_LOG_ERROR("Failed to read GPIO v1 events: %s", std::strerror(errno));
1026 return ErrorCode::FAILED;
1027 }
1028
1029 if (bytes == 0)
1030 {
1031 break;
1032 }
1033
1034 if (bytes != static_cast<ssize_t>(sizeof(event)))
1035 {
1036 XR_LOG_ERROR("Corrupted GPIO v1 event payload length");
1037 return ErrorCode::FAILED;
1038 }
1039
1040 if (!IsKnownGPIOEvent(event.id))
1041 {
1042 continue;
1043 }
1044
1045 ++event_count;
1046 received = true;
1047 }
1048
1049 return received ? ErrorCode::OK : ErrorCode::EMPTY;
1050}
1051
1052ErrorCode LinuxGPIO::EnsureConfigured() const
1053{
1054 if (!has_config_ || (request_fd_.load() < 0))
1055 {
1056 XR_LOG_ERROR("GPIO is not configured");
1057 return ErrorCode::STATE_ERR;
1058 }
1059
1060 return ErrorCode::OK;
1061}
1062bool LinuxGPIO::IsInterruptDirection(Direction direction)
1063{
1064 return direction == Direction::RISING_INTERRUPT ||
1065 direction == Direction::FALL_INTERRUPT ||
1066 direction == Direction::FALL_RISING_INTERRUPT;
1067}
1068
1069bool LinuxGPIO::NeedsRequestReopen(Configuration config) const
1070{
1071 if (request_fd_.load() < 0)
1072 {
1073 return true;
1074 }
1075
1076 const bool old_interrupt =
1077 interrupt_enabled_.load() && IsInterruptDirection(current_config_.direction);
1078 const bool new_interrupt = IsInterruptDirection(config.direction);
1079
1080 if (abi_version_.load() == AbiVersion::V2)
1081 {
1082 return old_interrupt != new_interrupt;
1083 }
1084
1085 if (new_interrupt)
1086 {
1087 return true;
1088 }
1089
1090 if (old_interrupt)
1091 {
1092 return true;
1093 }
1094
1095 return request_kind_.load() != RequestKind::HANDLE;
1096}
1097
1098} // namespace LibXR
Direction
定义 GPIO 引脚的方向类型 / Defines the direction types for GPIO pins.
Definition gpio.hpp:21
@ 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 命名空间 / LibXR namespace.
Definition ch32_can.hpp:14
ErrorCode
定义错误码枚举 / Defines an enumeration for error codes
存储 GPIO 配置参数的结构体 / Structure storing GPIO configuration parameters.
Definition gpio.hpp:47
Pull pull
GPIO 上拉/下拉配置 / GPIO pull-up/pull-down configuration.
Definition gpio.hpp:49
Direction direction
GPIO 引脚方向 / GPIO pin direction.
Definition gpio.hpp:48