/* * Copyright (C) 2016 Philippe Gerum * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include smokey_test_plugin(interrupt, SMOKEY_ARGLIST( SMOKEY_STRING(device), SMOKEY_STRING(trigger), SMOKEY_BOOL(select), SMOKEY_BOOL(timestamp), ), "Wait for interrupts from a GPIO pin.\n" "\tdevice=\n" "\trigger={edge[-rising/falling/both], level[-low/high]}\n" "\tselect, wait on select(2)." "\ttimestamp, enable timestamping." ); smokey_test_plugin(read_value, SMOKEY_ARGLIST( SMOKEY_STRING(device), ), "Read GPIO value.\n" "\tdevice=." ); smokey_test_plugin(write_value, SMOKEY_ARGLIST( SMOKEY_STRING(device), ), "Write GPIO value.\n" "\tdevice=." ); static int run_interrupt(struct smokey_test *t, int argc, char *const argv[]) { static struct { const char *name; int flag; } trigger_types[] = { { .name = "edge", .flag = GPIO_TRIGGER_EDGE_RISING }, { .name = "edge-rising", .flag = GPIO_TRIGGER_EDGE_RISING }, { .name = "edge-falling", .flag = GPIO_TRIGGER_EDGE_FALLING }, { .name = "edge-both", .flag = GPIO_TRIGGER_EDGE_FALLING|GPIO_TRIGGER_EDGE_RISING }, { .name = "level", .flag = GPIO_TRIGGER_LEVEL_LOW }, { .name = "level-low", .flag = GPIO_TRIGGER_LEVEL_LOW }, { .name = "level-high", .flag = GPIO_TRIGGER_LEVEL_HIGH }, { NULL, 0 }, }; int do_select = 0, fd, ret, trigger, n, value, do_timestamp = 0; const char *device = NULL, *trigname; struct rtdm_gpio_readout rdo; struct timespec now; fd_set set; smokey_parse_args(t, argc, argv); if (!SMOKEY_ARG_ISSET(interrupt, device)) { warning("missing device= specification"); return -EINVAL; } device = SMOKEY_ARG_STRING(interrupt, device); fd = open(device, O_RDWR); if (fd < 0) { ret = -errno; warning("cannot open device %s [%s]", device, symerror(ret)); return ret; } if (SMOKEY_ARG_ISSET(interrupt, select)) do_select = SMOKEY_ARG_BOOL(interrupt, select); if (SMOKEY_ARG_ISSET(interrupt, timestamp)) do_timestamp = SMOKEY_ARG_BOOL(interrupt, timestamp); trigger = GPIO_TRIGGER_NONE; if (SMOKEY_ARG_ISSET(interrupt, trigger)) { trigname = SMOKEY_ARG_STRING(interrupt, trigger); for (n = 0; trigger_types[n].name; n++) { if (strcmp(trigger_types[n].name, trigname) == 0) { trigger = trigger_types[n].flag; break; } } if (trigger == GPIO_TRIGGER_NONE) { warning("invalid trigger type %s", trigname); return -EINVAL; } } ret = ioctl(fd, GPIO_RTIOC_IRQEN, &trigger); if (ret) { ret = -errno; warning("GPIO_RTIOC_IRQEN failed on %s [%s]", device, symerror(ret)); return ret; } FD_ZERO(&set); FD_SET(fd, &set); for (;;) { if (do_select) { ret = select(fd + 1, &set, NULL, NULL, NULL); if (ret < 0) { ret = -errno; warning("failed listening to %s [%s]", device, symerror(ret)); return ret; } } if (do_timestamp) { ret = read(fd, &rdo, sizeof(rdo)); if (ret < 0) { ret = -errno; warning("failed reading from %s [%s]", device, symerror(ret)); return ret; } clock_gettime(CLOCK_MONOTONIC, &now); printf("received irq %llu us from now, GPIO state=%d\n", (timespec_scalar(&now) - rdo.timestamp) / 1000ULL, rdo.value); } else { ret = read(fd, &value, sizeof(value)); if (ret < 0) { ret = -errno; warning("failed reading from %s [%s]", device, symerror(ret)); return ret; } printf("received irq, GPIO state=%d\n", value); } } close(fd); return 0; } static int run_read_value(struct smokey_test *t, int argc, char *const argv[]) { const char *device = NULL; int fd, ret, value = -1; smokey_parse_args(t, argc, argv); if (!SMOKEY_ARG_ISSET(read_value, device)) { warning("missing device= specification"); return -EINVAL; } device = SMOKEY_ARG_STRING(read_value, device); fd = open(device, O_RDONLY|O_NONBLOCK); if (fd < 0) { ret = -errno; warning("cannot open device %s [%s]", device, symerror(ret)); return ret; } if (!__Terrno(ret, ioctl(fd, GPIO_RTIOC_DIR_IN))) return ret; ret = read(fd, &value, sizeof(value)); close(fd); if (!__Tassert(ret == sizeof(value))) return -EINVAL; smokey_trace("value=%d", value); return 0; } static int run_write_value(struct smokey_test *t, int argc, char *const argv[]) { const char *device = NULL; int fd, ret, value; smokey_parse_args(t, argc, argv); if (!SMOKEY_ARG_ISSET(write_value, device)) { warning("missing device= specification"); return -EINVAL; } device = SMOKEY_ARG_STRING(write_value, device); fd = open(device, O_WRONLY); if (fd < 0) { ret = -errno; warning("cannot open device %s [%s]", device, symerror(ret)); return ret; } value = 1; if (!__Terrno(ret, ioctl(fd, GPIO_RTIOC_DIR_OUT, &value))) return ret; ret = write(fd, &value, sizeof(value)); close(fd); if (!__Tassert(ret == sizeof(value))) return -EINVAL; return 0; } int main(int argc, char *const argv[]) { struct smokey_test *t; int ret, fails = 0; if (pvlist_empty(&smokey_test_list)) return 0; for_each_smokey_test(t) { ret = t->run(t, argc, argv); if (ret) { if (ret == -ENOSYS) { smokey_note("%s skipped (no kernel support)", t->name); continue; } fails++; if (smokey_keep_going) continue; if (smokey_verbose_mode) error(1, -ret, "test %s failed", t->name); return 1; } smokey_note("%s OK", t->name); } return fails != 0; }