GPIO_Sysfs.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. #include "GPIO_Sysfs.h"
  2. #include <AP_HAL/AP_HAL.h>
  3. #include <fcntl.h>
  4. #include <stdio.h>
  5. #include <sys/stat.h>
  6. #include <unistd.h>
  7. #include "Util.h"
  8. #define LOW 0
  9. #define HIGH 1
  10. #define assert_vpin(v_, max_, ...) do { \
  11. if (v_ >= max_) { \
  12. hal.console->printf("warning (%s): vpin %u out of range [0, %u)\n",\
  13. __PRETTY_FUNCTION__, v_, max_); \
  14. return __VA_ARGS__; \
  15. } \
  16. } while (0)
  17. using namespace Linux;
  18. static const AP_HAL::HAL& hal = AP_HAL::get_HAL();
  19. #define UINT32_MAX_STR "4294967295"
  20. /* Trick to use minimum stack space for each of the params */
  21. union gpio_params {
  22. char export_gpio[sizeof("export")];
  23. char direction[sizeof("gpio" UINT32_MAX_STR "/direction")];
  24. char value[sizeof("gpio" UINT32_MAX_STR "/value")];
  25. };
  26. #define GPIO_BASE_PATH "/sys/class/gpio/"
  27. #define GPIO_PATH_MAX (sizeof(GPIO_BASE_PATH) + sizeof(gpio_params) - 1)
  28. DigitalSource_Sysfs::DigitalSource_Sysfs(unsigned pin, int value_fd)
  29. : _pin(pin)
  30. , _value_fd(value_fd)
  31. {
  32. }
  33. DigitalSource_Sysfs::~DigitalSource_Sysfs()
  34. {
  35. if (_value_fd >= 0) {
  36. ::close(_value_fd);
  37. }
  38. }
  39. uint8_t DigitalSource_Sysfs::read()
  40. {
  41. char char_value;
  42. if (::pread(_value_fd, &char_value, 1, 0) < 0) {
  43. hal.console->printf("DigitalSource_Sysfs: Unable to read pin %u value.\n",
  44. _pin);
  45. return LOW;
  46. }
  47. return char_value - '0';
  48. }
  49. void DigitalSource_Sysfs::write(uint8_t value)
  50. {
  51. if (::pwrite(_value_fd, value == HIGH ? "1" : "0", 1, 0) < 0) {
  52. hal.console->printf("DigitalSource_Sysfs: Unable to write pin %u value.\n",
  53. _pin);
  54. }
  55. }
  56. void DigitalSource_Sysfs::mode(uint8_t output)
  57. {
  58. auto gpio_sysfs = GPIO_Sysfs::from(hal.gpio);
  59. gpio_sysfs->_pinMode(_pin, output);
  60. }
  61. void DigitalSource_Sysfs::toggle()
  62. {
  63. write(!read());
  64. }
  65. void GPIO_Sysfs::init()
  66. {
  67. #ifdef HAL_GPIO_SCRIPT
  68. if (!_script.thread_created) {
  69. _script.thread_created = true;
  70. if (!hal.scheduler->thread_create(FUNCTOR_BIND_MEMBER(&GPIO_Sysfs::_gpio_script_thread, void),
  71. "GPIO_Script", 4096, AP_HAL::Scheduler::PRIORITY_IO, -1)) {
  72. AP_HAL::panic("Unable to create GPIO_Script thread");
  73. }
  74. }
  75. #endif
  76. }
  77. void GPIO_Sysfs::pinMode(uint8_t vpin, uint8_t output)
  78. {
  79. #ifdef HAL_GPIO_SCRIPT
  80. if (vpin >= n_pins && output) {
  81. return;
  82. }
  83. #endif
  84. assert_vpin(vpin, n_pins);
  85. _export_pin(vpin);
  86. _pinMode(pin_table[vpin], output);
  87. }
  88. void GPIO_Sysfs::_pinMode(unsigned int pin, uint8_t output)
  89. {
  90. const char *dir = output ? "out" : "in";
  91. char path[GPIO_PATH_MAX];
  92. int r = snprintf(path, GPIO_PATH_MAX, GPIO_BASE_PATH "gpio%u/direction",
  93. pin);
  94. if (r < 0 || r >= (int)GPIO_PATH_MAX
  95. || Util::from(hal.util)->write_file(path, "%s", dir) < 0) {
  96. hal.console->printf("GPIO_Sysfs: Unable to set pin %u mode.\n", pin);
  97. }
  98. }
  99. int GPIO_Sysfs::_open_pin_value(unsigned int pin, int flags)
  100. {
  101. char path[GPIO_PATH_MAX];
  102. int fd;
  103. int r = snprintf(path, GPIO_PATH_MAX, GPIO_BASE_PATH "gpio%u/value", pin);
  104. if (r < 0 || r >= (int)GPIO_PATH_MAX
  105. || (fd = open(path, flags | O_CLOEXEC)) < 0) {
  106. hal.console->printf("GPIO_Sysfs: Unable to get value file descriptor for pin %u.\n",
  107. pin);
  108. return -1;
  109. }
  110. return fd;
  111. }
  112. uint8_t GPIO_Sysfs::read(uint8_t vpin)
  113. {
  114. assert_vpin(vpin, n_pins, LOW);
  115. const unsigned pin = pin_table[vpin];
  116. int fd = _open_pin_value(pin, O_RDONLY);
  117. if (fd < 0) {
  118. goto error;
  119. }
  120. char char_value;
  121. if (::pread(fd, &char_value, 1, 0) < 0) {
  122. goto error;
  123. }
  124. close(fd);
  125. return char_value - '0';
  126. error:
  127. hal.console->printf("GPIO_Sysfs: Unable to read pin %u value.\n", vpin);
  128. return LOW;
  129. }
  130. void GPIO_Sysfs::write(uint8_t vpin, uint8_t value)
  131. {
  132. #ifdef HAL_GPIO_SCRIPT
  133. if (vpin >= n_pins) {
  134. _gpio_script_write(vpin, value);
  135. return;
  136. }
  137. #endif
  138. assert_vpin(vpin, n_pins);
  139. const unsigned pin = pin_table[vpin];
  140. int fd = _open_pin_value(pin, O_WRONLY);
  141. if (fd < 0) {
  142. goto error;
  143. }
  144. if (::pwrite(fd, value == HIGH ? "1" : "0", 1, 0) < 0) {
  145. goto error;
  146. }
  147. close(fd);
  148. return;
  149. error:
  150. hal.console->printf("GPIO_Sysfs: Unable to write pin %u value.\n", vpin);
  151. }
  152. void GPIO_Sysfs::toggle(uint8_t vpin)
  153. {
  154. write(vpin, !read(vpin));
  155. }
  156. AP_HAL::DigitalSource* GPIO_Sysfs::channel(uint16_t vpin)
  157. {
  158. assert_vpin(vpin, n_pins, nullptr);
  159. const unsigned pin = pin_table[vpin];
  160. int value_fd = -1;
  161. if (_export_pin(vpin)) {
  162. value_fd = _open_pin_value(pin, O_RDWR);
  163. }
  164. /* Even if we couldn't open the fd, return a new DigitalSource and let
  165. * reads and writes fail later due to invalid. Otherwise we
  166. * could crash in undesired places */
  167. return new DigitalSource_Sysfs(pin, value_fd);
  168. }
  169. bool GPIO_Sysfs::usb_connected(void)
  170. {
  171. return false;
  172. }
  173. bool GPIO_Sysfs::_export_pin(uint8_t vpin)
  174. {
  175. #ifdef HAL_GPIO_SCRIPT
  176. if (vpin >= n_pins) {
  177. return false;
  178. }
  179. #endif
  180. assert_vpin(vpin, n_pins, false);
  181. const unsigned int pin = pin_table[vpin];
  182. char gpio_path[GPIO_PATH_MAX];
  183. int fd;
  184. int r = snprintf(gpio_path, GPIO_PATH_MAX, GPIO_BASE_PATH "gpio%u", pin);
  185. if (r < 0 || r >= (int) GPIO_PATH_MAX) {
  186. goto fail_snprintf;
  187. }
  188. if (access(gpio_path, F_OK) == 0) {
  189. // Already exported
  190. return true;
  191. }
  192. fd = open(GPIO_BASE_PATH "export", O_WRONLY | O_CLOEXEC);
  193. if (fd < 0) {
  194. goto fail_open;
  195. }
  196. // Try to export
  197. if (dprintf(fd, "%u", pin) < 0) {
  198. goto fail_export;
  199. }
  200. close(fd);
  201. return true;
  202. fail_export:
  203. close(fd);
  204. fail_open:
  205. fail_snprintf:
  206. hal.console->printf("GPIO_Sysfs: Unable to export pin %u.\n", pin);
  207. return false;
  208. }
  209. #ifdef HAL_GPIO_SCRIPT
  210. /*
  211. support using an external script triggered by a write to a GPIO
  212. value. This is called whenever a GPIO request is made that is for an
  213. unknown pin value. The script is called by a separate thread, and
  214. only one script can be run at a time. This prevents the scripts
  215. using too many resources
  216. */
  217. void GPIO_Sysfs::_gpio_script_write(uint8_t vpin, uint8_t value)
  218. {
  219. pin_value_t pv;
  220. pv.pin = vpin;
  221. pv.value = value;
  222. _script.pending.push(pv);
  223. }
  224. /*
  225. thread for running GPIO scripts
  226. */
  227. void GPIO_Sysfs::_gpio_script_thread(void)
  228. {
  229. while (true) {
  230. // don't run more than 20/sec
  231. hal.scheduler->delay(50);
  232. pin_value_t pv;
  233. if (_script.pending.pop(pv)) {
  234. char cmd[100];
  235. snprintf(cmd, sizeof(cmd)-1, "/bin/sh %s %u %u", HAL_GPIO_SCRIPT, pv.pin, pv.value);
  236. hal.console->printf("Running: %s\n", cmd);
  237. const int system_ret = system(cmd);
  238. if (system_ret != 0) {
  239. hal.console->printf("Unexpected return value (%d)\n", system_ret);
  240. }
  241. }
  242. }
  243. }
  244. #endif // HAL_GPIO_SCRIPT