2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2016-06-02 12:38:33 +03:00
/*
2016-07-27 08:41:49 +03:00
* gpio - event - mon - monitor GPIO line events from userspace
2016-06-02 12:38:33 +03:00
*
* Copyright ( C ) 2016 Linus Walleij
*
* Usage :
* gpio - event - mon - n < device - name > - o < offset >
*/
# include <unistd.h>
# include <stdlib.h>
# include <stdbool.h>
2017-12-14 23:26:08 +03:00
# include <stdint.h>
2016-06-02 12:38:33 +03:00
# include <stdio.h>
# include <dirent.h>
# include <errno.h>
# include <string.h>
# include <poll.h>
# include <fcntl.h>
# include <getopt.h>
# include <inttypes.h>
# include <sys/ioctl.h>
2017-12-21 03:41:31 +03:00
# include <sys/types.h>
2016-06-02 12:38:33 +03:00
# include <linux/gpio.h>
2020-09-28 03:28:05 +03:00
# include "gpio-utils.h"
2016-06-02 12:38:33 +03:00
int monitor_device ( const char * device_name ,
2020-09-28 03:28:06 +03:00
unsigned int * lines ,
unsigned int num_lines ,
2020-09-28 03:28:05 +03:00
struct gpio_v2_line_config * config ,
2016-06-02 12:38:33 +03:00
unsigned int loops )
{
2020-09-28 03:28:05 +03:00
struct gpio_v2_line_values values ;
2016-06-02 12:38:33 +03:00
char * chrdev_name ;
2020-09-28 03:28:05 +03:00
int cfd , lfd ;
2016-06-02 12:38:33 +03:00
int ret ;
int i = 0 ;
ret = asprintf ( & chrdev_name , " /dev/%s " , device_name ) ;
if ( ret < 0 )
return - ENOMEM ;
2020-09-28 03:28:05 +03:00
cfd = open ( chrdev_name , 0 ) ;
if ( cfd = = - 1 ) {
2016-06-02 12:38:33 +03:00
ret = - errno ;
fprintf ( stderr , " Failed to open %s \n " , chrdev_name ) ;
2020-07-08 07:16:00 +03:00
goto exit_free_name ;
2016-06-02 12:38:33 +03:00
}
2020-09-28 03:28:06 +03:00
ret = gpiotools_request_line ( device_name , lines , num_lines , config ,
2020-09-28 03:28:05 +03:00
" gpio-event-mon " ) ;
if ( ret < 0 )
goto exit_device_close ;
else
lfd = ret ;
2016-06-02 12:38:33 +03:00
/* Read initial states */
2020-09-28 03:28:06 +03:00
values . mask = 0 ;
2020-09-28 03:28:05 +03:00
values . bits = 0 ;
2020-09-28 03:28:06 +03:00
for ( i = 0 ; i < num_lines ; i + + )
gpiotools_set_bit ( & values . mask , i ) ;
2020-09-28 03:28:05 +03:00
ret = gpiotools_get_values ( lfd , & values ) ;
if ( ret < 0 ) {
fprintf ( stderr ,
" Failed to issue GPIO LINE GET VALUES IOCTL (%d) \n " ,
2016-06-02 12:38:33 +03:00
ret ) ;
2020-09-28 03:28:05 +03:00
goto exit_line_close ;
2016-06-02 12:38:33 +03:00
}
2020-09-28 03:28:06 +03:00
if ( num_lines = = 1 ) {
fprintf ( stdout , " Monitoring line %d on %s \n " , lines [ 0 ] , device_name ) ;
fprintf ( stdout , " Initial line value: %d \n " ,
gpiotools_test_bit ( values . bits , 0 ) ) ;
} else {
fprintf ( stdout , " Monitoring lines %d " , lines [ 0 ] ) ;
for ( i = 1 ; i < num_lines - 1 ; i + + )
fprintf ( stdout , " , %d " , lines [ i ] ) ;
fprintf ( stdout , " and %d on %s \n " , lines [ i ] , device_name ) ;
fprintf ( stdout , " Initial line values: %d " ,
gpiotools_test_bit ( values . bits , 0 ) ) ;
for ( i = 1 ; i < num_lines - 1 ; i + + )
fprintf ( stdout , " , %d " ,
gpiotools_test_bit ( values . bits , i ) ) ;
fprintf ( stdout , " and %d \n " ,
gpiotools_test_bit ( values . bits , i ) ) ;
}
2016-06-02 12:38:33 +03:00
while ( 1 ) {
2020-09-28 03:28:05 +03:00
struct gpio_v2_line_event event ;
2016-06-02 12:38:33 +03:00
2020-09-28 03:28:05 +03:00
ret = read ( lfd , & event , sizeof ( event ) ) ;
2016-06-02 12:38:33 +03:00
if ( ret = = - 1 ) {
if ( errno = = - EAGAIN ) {
fprintf ( stderr , " nothing available \n " ) ;
continue ;
} else {
ret = - errno ;
fprintf ( stderr , " Failed to read event (%d) \n " ,
ret ) ;
break ;
}
}
if ( ret ! = sizeof ( event ) ) {
fprintf ( stderr , " Reading event failed \n " ) ;
ret = - EIO ;
break ;
}
2021-01-07 07:00:19 +03:00
fprintf ( stdout , " GPIO EVENT at % " PRIu64 " on line %d (%d|%d) " ,
( uint64_t ) event . timestamp_ns , event . offset , event . line_seqno ,
2020-09-28 03:28:05 +03:00
event . seqno ) ;
2016-06-02 12:38:33 +03:00
switch ( event . id ) {
2020-09-28 03:28:05 +03:00
case GPIO_V2_LINE_EVENT_RISING_EDGE :
2016-06-02 12:38:33 +03:00
fprintf ( stdout , " rising edge " ) ;
break ;
2020-09-28 03:28:05 +03:00
case GPIO_V2_LINE_EVENT_FALLING_EDGE :
2016-06-02 12:38:33 +03:00
fprintf ( stdout , " falling edge " ) ;
break ;
default :
fprintf ( stdout , " unknown event " ) ;
}
fprintf ( stdout , " \n " ) ;
i + + ;
if ( i = = loops )
break ;
}
2020-09-28 03:28:05 +03:00
exit_line_close :
if ( close ( lfd ) = = - 1 )
perror ( " Failed to close line file " ) ;
exit_device_close :
if ( close ( cfd ) = = - 1 )
2016-06-02 12:38:33 +03:00
perror ( " Failed to close GPIO character device file " ) ;
2020-07-08 07:16:00 +03:00
exit_free_name :
2016-06-02 12:38:33 +03:00
free ( chrdev_name ) ;
return ret ;
}
void print_usage ( void )
{
fprintf ( stderr , " Usage: gpio-event-mon [options]... \n "
" Listen to events on GPIO lines, 0->1 1->0 \n "
" -n <name> Listen on GPIOs on a named device (must be stated) \n "
2020-09-28 03:28:06 +03:00
" -o <n> Offset of line to monitor (may be repeated) \n "
2016-06-02 12:38:33 +03:00
" -d Set line as open drain \n "
" -s Set line as open source \n "
" -r Listen for rising edges \n "
" -f Listen for falling edges \n "
2020-10-15 02:11:58 +03:00
" -w Report the wall-clock time for events \n "
2020-09-28 03:28:07 +03:00
" -b <n> Debounce the line with period n microseconds \n "
2016-06-02 12:38:33 +03:00
" [-c <n>] Do <n> loops (optional, infinite loop if not stated) \n "
" -? This helptext \n "
" \n "
" Example: \n "
2020-09-28 03:28:07 +03:00
" gpio-event-mon -n gpiochip0 -o 4 -r -f -b 10000 \n "
2016-06-02 12:38:33 +03:00
) ;
}
2020-09-28 03:28:05 +03:00
# define EDGE_FLAGS \
( GPIO_V2_LINE_FLAG_EDGE_RISING | \
GPIO_V2_LINE_FLAG_EDGE_FALLING )
2016-06-02 12:38:33 +03:00
int main ( int argc , char * * argv )
{
const char * device_name = NULL ;
2020-09-28 03:28:06 +03:00
unsigned int lines [ GPIO_V2_LINES_MAX ] ;
unsigned int num_lines = 0 ;
2016-06-02 12:38:33 +03:00
unsigned int loops = 0 ;
2020-09-28 03:28:05 +03:00
struct gpio_v2_line_config config ;
2020-09-28 03:28:07 +03:00
int c , attr , i ;
unsigned long debounce_period_us = 0 ;
2016-06-02 12:38:33 +03:00
2020-09-28 03:28:05 +03:00
memset ( & config , 0 , sizeof ( config ) ) ;
config . flags = GPIO_V2_LINE_FLAG_INPUT ;
2020-10-15 02:11:58 +03:00
while ( ( c = getopt ( argc , argv , " c:n:o:b:dsrfw? " ) ) ! = - 1 ) {
2016-06-02 12:38:33 +03:00
switch ( c ) {
case ' c ' :
loops = strtoul ( optarg , NULL , 10 ) ;
break ;
case ' n ' :
device_name = optarg ;
break ;
case ' o ' :
2020-09-28 03:28:06 +03:00
if ( num_lines > = GPIO_V2_LINES_MAX ) {
print_usage ( ) ;
return - 1 ;
}
lines [ num_lines ] = strtoul ( optarg , NULL , 10 ) ;
num_lines + + ;
2016-06-02 12:38:33 +03:00
break ;
2020-09-28 03:28:07 +03:00
case ' b ' :
debounce_period_us = strtoul ( optarg , NULL , 10 ) ;
break ;
2016-06-02 12:38:33 +03:00
case ' d ' :
2020-09-28 03:28:05 +03:00
config . flags | = GPIO_V2_LINE_FLAG_OPEN_DRAIN ;
2016-06-02 12:38:33 +03:00
break ;
case ' s ' :
2020-09-28 03:28:05 +03:00
config . flags | = GPIO_V2_LINE_FLAG_OPEN_SOURCE ;
2016-06-02 12:38:33 +03:00
break ;
case ' r ' :
2020-09-28 03:28:05 +03:00
config . flags | = GPIO_V2_LINE_FLAG_EDGE_RISING ;
2016-06-02 12:38:33 +03:00
break ;
case ' f ' :
2020-09-28 03:28:05 +03:00
config . flags | = GPIO_V2_LINE_FLAG_EDGE_FALLING ;
2016-06-02 12:38:33 +03:00
break ;
2020-10-15 02:11:58 +03:00
case ' w ' :
config . flags | = GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME ;
break ;
2016-06-02 12:38:33 +03:00
case ' ? ' :
print_usage ( ) ;
return - 1 ;
}
}
2020-09-28 03:28:07 +03:00
if ( debounce_period_us ) {
attr = config . num_attrs ;
config . num_attrs + + ;
for ( i = 0 ; i < num_lines ; i + + )
gpiotools_set_bit ( & config . attrs [ attr ] . mask , i ) ;
config . attrs [ attr ] . attr . id = GPIO_V2_LINE_ATTR_ID_DEBOUNCE ;
config . attrs [ attr ] . attr . debounce_period_us = debounce_period_us ;
}
2020-09-28 03:28:06 +03:00
if ( ! device_name | | num_lines = = 0 ) {
2016-06-02 12:38:33 +03:00
print_usage ( ) ;
return - 1 ;
}
2020-09-28 03:28:05 +03:00
if ( ! ( config . flags & EDGE_FLAGS ) ) {
2016-06-02 12:38:33 +03:00
printf ( " No flags specified, listening on both rising and "
" falling edges \n " ) ;
2020-09-28 03:28:05 +03:00
config . flags | = EDGE_FLAGS ;
2016-06-02 12:38:33 +03:00
}
2020-09-28 03:28:06 +03:00
return monitor_device ( device_name , lines , num_lines , & config , loops ) ;
2016-06-02 12:38:33 +03:00
}