On 03/06/2018 02:53 PM, Bartosz Gołaszewski wrote:
2018-03-06 21:49 GMT+01:00 Timur Tabi <timur@xxxxxxxxxxxxxx>:
On 03/06/2018 01:30 PM, Timur Tabi wrote:
Never mind, I figured it out. I was passing the wrong parameter to
pthread_create(). Everything works now.
Thanks for all your help. libgpiod makes a lot more sense to me now.
So I found one small oddity.
When I start the interrupt test, the output pin is set to 1, and therefore
the input pin is also set to 1 since they're wired together.
It appears that gpiod_line_event_wait() is receiving a rising-edge event
when I call gpiod_line_set_value(..., 1). That is, the input is reading 1,
and it is still set to 1, but I get a rising event anyway.
This is normal, you're generating an interrupt anyway.
How is that possible? I'm not writing to the input line directly. It
should see a continuous stream of "1", so there should be nothing that
triggers an interrupt. If the output is "1", and I call
gpiod_line_set_value(output, 1), there should be no electrical changes
on that output pin.
If I do this:
gpiod_line_set_value(output, 1);
gpiod_line_set_value(output, 1);
gpiod_line_set_value(output, 1);
gpiod_line_set_value(output, 1);
gpiod_line_set_value(output, 1);
gpiod_line_set_value(output, 1);
I should see no changes on a scope attached to the GPIO. So how could
there be an interrupt?
At one point, I told it to write a 0 to output, and I still got a rising
event:
This isn't normal - do you have an strace output for that? We need to
figure out if it's something in libgpiod or in the kernel.
write(1, "Running interrupt test", 22) = 22
write(1, "\n", 1) = 1
mmap(NULL, 8388608, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0xffff99fa0000
mprotect(0xffff99fa0000, 65536, PROT_NONE) = 0
clone(child_stack=0xffff9a79eb20,
flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID,
parent_tidptr=0xffff9a79f2e0, tls=0xffff9a79f900,
child_tidptr=0xffff9a79f2e0) = 24048
close(5) = 0
ioctl(3, _IOC(_IOC_READ|_IOC_WRITE, 0xb4, 0x04, 0x30), 0xffffec347160) = 0
ioctl(3, GPIO_GET_LINEINFO_IOCTL, 0xffffec3470b8) = 0
ioctl(5, _IOC(_IOC_READ|_IOC_WRITE, 0xb4, 0x08, 0x40), 0xffffec347170) = 0
write(1, "Initial input value: 1\n", 23) = 23
futex(0xffffec347470, FUTEX_WAKE_PRIVATE, 2147483647) = 1
ppoll([{fd=5, events=POLLIN|POLLPRI}], 1, {1, 0}, NULL, 8) = 1 ([{fd=5,
revents=POLLIN}], left {0, 999996600})
read(5, "\20i\272\226Tp\31\25\1\0\0\0\0\0\0\0", 16) = 16
ioctl(5, _IOC(_IOC_READ|_IOC_WRITE, 0xb4, 0x08, 0x40), 0xffffec347170) = 0
write(1, "Received rising edge event, valu"..., 56) = 56
ppoll([{fd=5, events=POLLIN|POLLPRI}], 1, {1, 0}, NULL, 8) = 1 ([{fd=5,
revents=POLLIN}], left {0, 999998200})
read(5, "\17J\300\226Tp\31\25\2\0\0\0\0\0\0\0", 16) = 16
ioctl(5, _IOC(_IOC_READ|_IOC_WRITE, 0xb4, 0x08, 0x40), 0xffffec347170) = 0
write(1, "Received falling edge event, val"..., 56) = 56
futex(0xffff9a79f2e0, FUTEX_WAIT, 24048, NULL) = 0
write(1, "All tests passed", 16) = 16
I've attached the latest version of my code.
Oscillate thread started
Initial input value: 1
Setting output to 0
Received rising edge event, value=0, elapsed=-0.000204s
And it apparently went backwards in time.
What happens in this scenario:
1. gpiod_line_request_both_edges_events() is called
2. GPIO line transitions from 0 to 1
3. gpiod_line_event_wait() is called
Is it possible that this event was queued in the kernel buffer and
that you actually read one of the previous events?
Yes, it's very possible. How do I flush that queue?
--
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm
Technologies, Inc. Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.
#include <stdio.h>
#include <linux/gpio.h>
#include <gpiod.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
int loopback(struct gpiod_chip *chip, struct gpiod_line *output, struct gpiod_line *input, int value)
{
int ret;
ret = gpiod_line_set_value(output, value);
if (ret < 0)
return ret;
if (value != gpiod_line_get_value(input))
return -1;
return 0;
}
struct toggle_data {
struct gpiod_line *output;
unsigned int frequency;
volatile bool finished;
pthread_barrier_t barrier;
};
void *oscillate(void *p)
{
struct toggle_data *data = p;
unsigned int toggle = 1 - gpiod_line_get_value(data->output);
int ret;
printf("Oscillate thread started\n");
pthread_barrier_wait(&data->barrier);
while (!data->finished) {
printf("Setting output to %i\n", toggle);
ret = gpiod_line_set_value(data->output, toggle);
if (ret < 0) {
printf("Error: could not set value (errno=%i)\n", errno);
break;
}
sleep(data->frequency);
toggle = 1 - toggle;
}
exit:
printf("Oscillate thread exits\n");
return NULL;
}
int interrupt(struct gpiod_chip *chip, struct gpiod_line *output, struct gpiod_line *input)
{
struct toggle_data data;
pthread_t thread;
struct timespec timeout = { 0 };
struct timespec previous = { 0 };
double elapsed;
struct gpiod_line_event event;
unsigned int count = 3;
int ret;
data.output = output;
data.frequency = 1;
data.finished = false;
timeout.tv_sec = data.frequency;
pthread_barrier_init(&data.barrier, NULL, 2);
ret = pthread_create(&thread, NULL, oscillate, &data);
if (ret < 0) {
printf("Error: Could not create thread (ret=%i)\n", ret);
return ret;
}
/* We need to release the line so that it can be requested */
gpiod_line_release(input);
ret = gpiod_line_request_both_edges_events(input, NULL);
if (ret < 0) {
printf("Error: Could not create event (errno=%i)\n", errno);
return ret;
}
printf("Initial input value: %i\n", gpiod_line_get_value(input));
pthread_barrier_wait(&data.barrier);
clock_gettime(CLOCK_REALTIME, &previous);
while (--count) {
ret = gpiod_line_event_wait(input, &timeout);
if (ret < 0) {
printf("Error: Could not wait for event (errno=%i)\n", errno);
break;
}
if (ret == 0) {
printf("Error: Timeout waiting for event (errno=%i)\n", errno);
ret = -1;
break;
}
ret = gpiod_line_event_read(input, &event);
elapsed = (event.ts.tv_sec + event.ts.tv_nsec / 1000000000.0) -
(previous.tv_sec + previous.tv_nsec / 1000000000.0);
printf("Received %s edge event, value=%i, elapsed=%fs\n",
event.event_type == GPIOD_LINE_EVENT_RISING_EDGE ? "rising" : "falling",
gpiod_line_get_value(input), elapsed);
previous = event.ts;
}
data.finished = true;
pthread_join(thread, NULL);
return ret;
}
int test(struct gpiod_chip *chip, int _output, int _input)
{
struct gpiod_line *output;
struct gpiod_line *input;
int ret, i;
printf("Chip Name: %s\n", gpiod_chip_name(chip));
printf("Output pin: %i\n", _output);
printf("Input pin: %i\n", _input);
output = gpiod_chip_get_line(chip, _output);
if (!output) {
printf("Error: Could not obtain gpioline for pin %i\n", _output);
return 2;
}
input = gpiod_chip_get_line(chip, _input);
if (!input) {
printf("Error: Could not obtain gpioline for pin %i\n", _input);
return 2;
}
printf("Configuring pins\n");
ret = gpiod_line_request_output(output, NULL, 0);
if (ret) {
printf("Error: Could not configure pin %i to output\n", _output);
return 2;
}
if (gpiod_line_direction(output) != GPIOD_LINE_DIRECTION_OUTPUT) {
printf("Error: Pin %i refuses to be configured for output\n", _output);
return 2;
}
ret = gpiod_line_request_input(input, NULL);
if (ret) {
printf("Error: Could not configure pin $i to input\n", _output);
return 2;
}
if (gpiod_line_direction(input) != GPIOD_LINE_DIRECTION_INPUT) {
printf("Error: Pin %i refuses to be configured for input\n", _input);
return 2;
}
printf("Running loopback test\n");
for (i = 0; i < 10; i++) {
ret = loopback(chip, output, input, 0);
if (ret) {
printf("Error: Loopback test #%iA failed\n", i);
return 2;
}
ret = loopback(chip, output, input, 1);
if (ret) {
printf("Error: Loopback test #%iB failed\n", i);
return 2;
}
}
printf("Running interrupt test\n");
ret = interrupt(chip, output, input);
if (ret < 0)
return 2;
printf("All tests passed\n");
return 0;
}
int main(int argc, char *argv[])
{
struct gpiod_chip *chip;
int ret, fd;
if (argc != 4) {
printf("Usage: %s chip-device output-pin input-pin\n", argv[0]);
return 1;
}
/* Test to see if the gpiochip device exists and is readable */
fd = open(argv[1], O_RDWR);
if (fd < 0) {
printf("Error: Device %s is not accessible (error=%i)\n", fd);
return 1;
} else
close(fd);
chip = gpiod_chip_open(argv[1]);
if (!chip) {
printf("Error: Device %s is not a gpiochip device\n");
return 1;
}
setbuf(stdout, NULL);
ret = test(chip, atoi(argv[2]), atoi(argv[3]));
gpiod_chip_close(chip);
return ret;
}