The hal_pi_gpio component seems to be beta stage
meaning I don't see anything wrong.
When I programmed full-time this was when I had to hand it over to others to peer.

Where do I look for howto write a man page?

About the comp -----------------------------------------
Written in old-school c
For rpi's only
( so far anyway, the scheme is flexible for similar, opi bpi maybe bbb bbw bbg )

It's real-time ( well it starts with  loadrt under userspace linuxcnc tested on rpi33 )
It only needs 1 thread and has re-settable pins
( and has a setp'able param reset_time ,
  set in integer of nS units (up to 1/4 thread period))
It has inputs and in-nots
It has outputs and out-inverts and out-resets
It uses a man readable config format ( a teeny language to describe each pin , see end of msg)
It reads switches and blinks leds
( an old boss assured me more blinky lights was a good thing )

I can send a copy to anyone wanting to try, hell its text, see attachment.

How to build in a rip installation:-------------------------
I compile it by backing up the hal_pi_gpio.c in my rip directory
  ~/linuxcnc-dev/src/hal/drivers/hal_pi_gpio.c  (move to another dir, don't just rename it) then copying my file into there using the same name as the pi gpio.c file that was there before.

Then touch it (to insure it is changed so make will notice and rebuild it )
 type   touch hal_pi_gpio.c   in the src/hal.driver dir

Then     make modules   from the ~/linuxcnc/src   dir
(this trickery is due to the .c file
 needing other .c files in that dir,
 and a simple gcc command is beyond my ken )
I get no errors no notes no warnings, its a clean make here

Once it makes, you use it with halrun like this

halrun
loadrt hal_pi_gpio pi_pins={03,<,+,x}{05,>,1,Y}{07,>,0,}{40,>,1,Y}
loadrt threads name1=slow period1=150000
addf hal_pi_gpio.read slow
add hal_pi_gpio.write slow
addf hal_pi_gpio.reset slow
setp hal_pi_gpio.reset_time 30000 (easier to see than my 2000)
start


Then use show pin and show param to watch things work
(and halmeter and halscope too)

You'll need a scope to catch the reset pins.

They change inside one thread period so halscope cant see 'em.

Hook up a bread board and switches and leds ( 3.3V i/o here!!)

( as simple as hooking up an output to an input will show you something )

About the pi_pins config line--------------------------------------------------
It's simple but strict rules
Each pin is described by a 'quad' ( a 4 param thingy )
Each quad has the format _like_
{03,>,0,N}
This describes the rpi header pin#3
     and is an output ( the '>' is used for out )
     and has an initial power on value of 0 ( zero )
     and does NOT have a reset feature
{05,>,1,Y}
    describes header pin 5
    which is an output '>'
    and has intial value of 1
    and has the reset feature ( requires set hal_pi_gpio.reset_time 2000 for 2us example)
{07,<,+,x}
    describes header pin 7
    which is an input
    and has an in-not pin due to '+'
    and the last param is meaningless, tested lightly with a few single chars in that place
{12,<,-,x}
   describes header pin 12
   which is an input
   and does not get an in-not pin , due to '-'
   and last parm is meaningless


hokay
I am sure i didn't write near 1000 lines w/o mistakes
lemmeno
tomp

/********************************************************************
* 15feb2021 
* compiles clean  no errs not notes no warnings 
* 
* resets seems to work need scope trap to be sure
*  scope can see 2uS pulse on step pins but halscope cannot
* 
* i now have a param hal_pi_gpio.reset_time  and i can setp it 
* ( halrun show param shows in hex but ok its alive)
* TODO i can setp it but i suppose a default is good ideA
* 
*/
#include "rtapi.h"		/* RTAPI realtime OS API */
#include "rtapi_bitops.h"
#include "rtapi_app.h"		/* RTAPI realtime module decls */
                                /* this also includes config.h */
#include "hal.h"		/* HAL public API decls */
#include "bcm2835.h"
#include "cpuinfo.h"

#define BCM2708_PERI_BASE   0x20000000
#define BCM2708_GPIO_BASE   (BCM2708_PERI_BASE + 0x200000)
#define BCM2709_PERI_BASE   0x3F000000
#define BCM2709_GPIO_BASE   (BCM2709_PERI_BASE + 0x200000)

//#define RTAPI
#define RTAPI_BIT(nr)           (1UL << (nr))

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>   /* for printf key chars*/
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>


/*-------------------------------------------------------------------------------------------------------*/
/*------------------------------beg-----new arrays --------------------------*/
// i use an array witj entries for npiuns+1  0 thru and includiong npins   like 0thru26 for old pi's and 0thru41 for 'new'er pis
//// these vvvv arrays can be used to lookup gpionum for rpi2 using hdrpinnum as index, a value of 0 means the pin is NOT available for gpio
// Rev 1 Raspberry:  only 26 hdr pins for rpi rev2
static unsigned char nurev1_gpios[] = 
  {0, 0, 0, 0, 0, 1, 0, 4,14, 0,15,17,18,21, 0,22,23, 0,24,10, 0, 9,25,11, 8, 0, 7};
/*{0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26};   ruler line ruler line not code*/
// NB hdrpin 3 is gpio 0 on rpi rev1  gpio0 is not used on any other pi

// Rev2 Raspberry:   only 26 hdr pins for rpi rev2  ( not same as rpi2 )
static unsigned char nurev2_gpios[] = 
  {0, 0, 0, 2, 0, 3, 0, 4,14, 0,15,17,18,27, 0,22,23, 0,24,10, 0, 9,25,11, 8, 0, 7};
/*{0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26};  ruler line not code */

// Raspberry2/3/4:   40 hdr pins
static unsigned char nurpi2_gpios[] = 
  {0, 0, 0, 2, 0, 3, 0, 4, 0, 0, 0,17,18,27, 0,22,23, 0,24,10, 0, 9,11,25, 8, 0, 7, 0, 0, 5, 0, 6,12,13, 0,19,16,26,20, 0,21};
/*{0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38.39,40};   ruler line ruler line not code*/
/*--------------------------------end---new  arrays --------------------------*/

#define pinsMAX 40

/* i was using an array of struct, 
//  mistakenly thinking that the 1st element .pval,
//  could be used as the place where the system stored the input value
//  or the desired value of an output
// no, the struct/pval is just NEVER USED as is .pvalinv
// the .pval is not used because the read func stores the input pin's read value at the ptr  *pinfoarray[n]
// and vs:vs the real pin in the rpi bcm2835 space is set or clr'd according to *(pinfoarray[n])
*/

/* TODO what use is this struct now?? 13feb2021  well its still good for holding the pin infprmation
// which is usefull untill the pin is created  ( pnum gpionum pdir pinv pres 
// 12feb2021 i chgd do pnum followed by gpionum were 1st  WHY??? 
*/
struct pinfo
{
    char pnum;  // 13feb2021 chg order to pnum gpionum pval pvalinv pdir pinv pres may not be needed after hal_pin_bit_newf returns success
    char gpionum;
    
    hal_bit_t pdir;  // direction 0 means OUTPUT  1 means INPUT
    hal_bit_t pinv;  // wether the in-not pin is wanted ( only for INPUTS)
    hal_bit_t pres;  // wether the erset features is wanted (only for OUTPUTS)

    hal_bit_t pval; 
    hal_bit_t pvalinv; // TODO looks unused, remove it??
};

hal_bit_t **pinfoarray;  
hal_bit_t *out_inverts;
hal_bit_t *out_resets;

static struct pinfo pinarray[41]; // 0th memeber not used, index with pinhdrnum 1-40 // pin1stuff;
// I/O access   this is a ptr into the mmap'd mirror of the real gpio
static volatile unsigned *gpio;

static int npins;
static int  mem_fd;

// vvv 13feb2021 needed for reset()
static unsigned long ns2tsc_factor;

#define ns2tsc(x) (((x) * (unsigned long long)ns2tsc_factor) >> 12)

static long long write_time;

hal_u32_t * reset_time_addr;       /* min ns between write and reset */
    
int num_pins=40; // the rpi2/3/4 hdr has 40 pins, a subset are valid for gpio use
static char * cp;        // a pointer into string instructing pin features
static int pin_num = 0;  // an index into arrays

MODULE_AUTHOR("Michael Haberler");
MODULE_DESCRIPTION("Driver for Raspberry Pi GPIO pins");
MODULE_LICENSE("GPL");

static char * pi_pins="{0,<,+,x}";

// pi_pins is the name of the cfg strg  it is not dclrd here it is magically handed to this code by Obiwan K.
RTAPI_MP_STRING(pi_pins,"quads describing each desired pin");

static int comp_id;		/* component ID */

static unsigned char *gpios;

static void read_port(void *arg, long period);
static void write_port(void *arg, long period);
static void reset_port(void *arg, long period);

/* **************** all vvvvv BCM2835funcs are same code in orig ***************** */
// TODO what is the worry message about?
static __inline__ uint32_t bcm2835_peri_read(volatile uint32_t* paddr){
  /* Make sure we dont return the _last_ read which might get lost
  // if subsequent code changes to a different peripheral*/
  volatile uint32_t ret = *paddr;
  return ret;
}

// Read input pin    >>gpio<< is the addr rtnd from the mmap
static __inline__ uint8_t bcm2835_gpio_lev(uint8_t pin){
  volatile uint32_t* paddr = gpio + BCM2835_GPLEV0/4 + pin/32;
  uint8_t shift = pin % 32;
  uint32_t value = bcm2835_peri_read(paddr);
  return (value & (1 << shift)) ? HIGH : LOW;
}

// TODO is this redundancy VVVVV about  the caveat to always  readgpio   before  writegpio??
static __inline__ void bcm2835_peri_write(volatile uint32_t* paddr, uint32_t value){
  // Make sure we don't rely on the first write, which may get
  // lost if the previous access was to a different peripheral.
  *paddr = value;
  *paddr = value;
}

// Set output pin     >>gpio<< is the addr rtnd from mmap
static __inline__ void bcm2835_gpio_set(uint8_t pin){
  volatile uint32_t* paddr = gpio + BCM2835_GPSET0/4 + pin/32;
  uint8_t shift = pin % 32;
  bcm2835_peri_write(paddr, 1 << shift);
}

// Clear output pin   >>gpio<< is the addr rtnd from mmap
static __inline__ void bcm2835_gpio_clr(uint8_t pin){
  volatile uint32_t* paddr = gpio + BCM2835_GPCLR0/4 + pin/32;
  uint8_t shift = pin % 32;
  bcm2835_peri_write(paddr, 1 << shift);
}

// Set/clear only the bits in value covered by the mask
static __inline__ void bcm2835_peri_set_bits(volatile uint32_t* paddr, uint32_t value, uint32_t mask){
  uint32_t v = bcm2835_peri_read(paddr);
  v = (v & ~mask) | (value & mask);
  bcm2835_peri_write(paddr, v);
}

/* Function select   as in 'select the function of the pin'
// pin is a BCM2835 GPIO pin number NOT RPi pin number
//      There are 6 control registers, each control the functions of a block
//      of 10 pins.
//      Each control register has 10 sets of 3 bits per GPIO pin:
//
//      000 = GPIO Pin X is an input
//      001 = GPIO Pin X is an output
//      100 = GPIO Pin X takes alternate function 0
//      101 = GPIO Pin X takes alternate function 1
//      110 = GPIO Pin X takes alternate function 2
//      111 = GPIO Pin X takes alternate function 3
//      011 = GPIO Pin X takes alternate function 4
//      010 = GPIO Pin X takes alternate function 5
//
// So the 3 bits for port X are:
//      X / 10 + ((X % 10) * 3)
*/
void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode){
  // >>gpio<< is the addr rtnd from mmap

  // Function selects are 10 pins per 32 bit word, 3 bits per pin
  volatile uint32_t* paddr = gpio + BCM2835_GPFSEL0/4 + (pin/10);
  uint8_t   shift = (pin % 10) * 3;
  uint32_t  mask = BCM2835_GPIO_FSEL_MASK << shift;
  uint32_t  value = mode << shift;
  bcm2835_peri_set_bits(paddr, value, mask);
}

// 2 funcs to setup access to the gpio, one is alwys run, 2nd conditionally run   not well understood
// TODO the fd should not be left open till exiym close asap
static int setup_gpiomem_access(void){
  if ((mem_fd = open("/dev/gpiomem", O_RDWR|O_SYNC)) < 0) {
    rtapi_print_msg(RTAPI_MSG_ERR,"HAL_PI_GPIO: can't open /dev/gpiomem:  %d - %s\n"
        "If the error is 'permission denied' then try adding the user who runs\n"
        "LinuxCNC to the gpio group: sudo gpasswd -a username gpio\n", errno, strerror(errno));
    return -1;
  }

  gpio = mmap(NULL, BCM2835_BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, 0);

  if (gpio == MAP_FAILED) {
    close(mem_fd);
    mem_fd = -1;
    rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: mmap failed: %d - %s\n", errno, strerror(errno));
    return -1;
  }

  return 0;
}

static int  setup_gpio_access(int rev, int ncores){
  // open /dev/mem
  if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
    rtapi_print_msg(RTAPI_MSG_ERR,"HAL_PI_GPIO: can't open /dev/mem:  %d - %s\n", errno, strerror(errno));
    return -1;
  }

  if (rev <= 2  || ncores <= 2){
    gpio = mmap(NULL, BCM2835_BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, BCM2708_GPIO_BASE);
  }else{
    gpio = mmap(NULL, BCM2835_BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, BCM2709_GPIO_BASE);
  }
  
  if (gpio == MAP_FAILED) {
    rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: mmap failed: %d - %s\n", errno, strerror(errno));
    return -1;;
  }
  return 0;
}

// TODO CHECKME with rpi4  there was some error detecting revision  and an italian gent was working on it, i never heard a final solution, i did report the prob
static int number_of_cores(void){
  char str[256];
  int procCount = 0;
  FILE *fp;

  if( (fp = fopen("/proc/cpuinfo", "r")) ) {
    while(fgets(str, sizeof str, fp)){
      if( !memcmp(str, "processor", 9) ){
        procCount++;
      }
    }
  }
  if( !procCount ) {
	  rtapi_print_msg(RTAPI_MSG_ERR,"HAL_PI_GPIO: Unable to get proc count. Defaulting to 2");
	  procCount = 2;
  }
  return procCount;
}
/* **************** all ^^^^^ BCM2835funcs same code in orig ***************** */

/*-beg------------------------------------------the parsing func and helpers----------------------------
// these parsing helper func return the data serached for or error -1
 the number is ascii formatted to 2 chars always (0n for nums <10)
   copy 2 chars to a tempstrg, cnvrt to int (or fail) and rtn int (or failure notice) 
 get pinnum, i expect 2 ascii chars and expect number described to be 01 to 99
*/

int pinValidForGPIO( int i ){
   return(nurpi2_gpios[i]); // a return of 0 is bad
}
//
int get_pin_num(char * cp) { // gets ptr to string, returns in 01 to 00 or err -1
    char buf1[3];
    int numread;
    int ret1;
    
    strncpy(buf1, cp, 2);
    if(*(cp+2) != ','){
      printf("ERR get_pin_num() , pin number is not 2 digits followed by comma\n");
      return -1;
    }
    buf1[2]='\0';
    
    // checking each char is a digit is not necc, as atoi will rtn err code if conversion is bad
    numread= atoi(buf1);
    
    ret1 = pinValidForGPIO(numread);
    if( ! ret1){
      printf("ERR get_pin_num() , pin #%d is not valid for Rpi2 or 3 or 4, returning -1\n", numread);
      return -1;
    }
    
    return numread;
}

int get_dir(char * cp) { // dir = direction, this indentifies the pin as input or output
  int dir;
  
  if(*cp == '>'){
    dir = 0;
  }else{
    if(*cp == '<') {
      dir = 1;
    }else{
      dir = -1;
    }
  }  

  return dir;
}

int get_initial(char * cp) {
  int initial_value;
  if(*cp == '0'){
    initial_value = 0;
  }else{
    if(*cp =='1'){
      initial_value = 1;
    }else{
      initial_value = -1;
    }
  }
  
  return initial_value;
}

int get_inv_mark(char * cp){  // check if input pin wants and invert pin
  int inv_wanted;
  
  if( *cp == '+'){
    inv_wanted=1;
  }else{
    if( *cp == '-'){
      inv_wanted=0;
    }else{
      inv_wanted=-1; // err bad char
    }
  }
  
  return inv_wanted;
}

int get_reset_mark(char * cp){  // check if output pin  wants the reset feature
  char ctemp=toupper(*cp);
  int reset_wanted;
  
  if(ctemp=='Y'){
    reset_wanted = 1;
  }else{
    if(ctemp=='N'){
      reset_wanted = 0;
    }else{
      reset_wanted = -1;
    }
  }
  return reset_wanted;
}

/* decodeone  decodes a single 'quad'  eg {03,>,0,y}
   it uses several helper funcs, one for each typre of data
   *the helper funcs return the serached for data or -1
   * the returned data should be assembled into the nth array element of pinarray[n] ( an array of structs type pinfo )
   * the data can be stored using   pinarray[5].pnum=5;
   * once filled in, the struct's address can be added to pinfoarray ( an array of ptrs to structs) using pinfoarry[n] = &pinarray[n];
   * after the ptrs are saved, the indiv elements can be 
   *   written to   pinfoarray[3]->pval = 1
   *   read from    lastreadstate = pinfoarray[11].pval
   */
   
int decodeone(char * instrg ){  // read a 'quad'  like {03,>,0,Y} whuich describes a single pin
    /* the 'quad' begins with 2 char pinnum ( the rpi header number)
    // the 2nd data is < or > for input or output, this becomes boolean 0 for out and 1 for in
    // the 3rd param:
    //   if pin is IN then 3rd is wether a,'invert' pin is wanted , + means wanted, - means not wanted, there become 1 and 0 resp
    //   if pin is OUT then 3rd data is initial value for 1st thread loop, AND ( if reset feature used, this is the value the pin returns to after reset_delay
    // the 3th data is not used by inputs, expected 'x' char, but any single char is ok, becomes 0 for all input pins
    //   if the pin is OUTPUT then this is expected Y or N meaning rested_wanted or not becomes 1 or 0
    */
    
    int pin_dir;
    int initial_value;
    int invert_wanted;
    int reset_wanted;
    
    cp = instrg;

    if(*cp != '{'){ // for this chunk (data from { till } , insure 1st char is '{' 
      printf("ERR  decodeone(), 1st char not '{',  it is %c\n", *cp);
      return -1;
    }
    cp++; // the 0th char is { , point to next char which should be a digit
    
    /* get  pin_num   which a 2 char strg cnvrtd to char sized int 
         and pnum is kept during this loop as idenitfier and index to storage arrays */
    pin_num = get_pin_num(cp); //  HERE  cp or *cp??  cp i think
    if(pin_num < 1){
      printf("ERR  invalid pin_num %d, returning -1\n", pin_num);
      return -1;
    }
    cp+=3; // skip past NN and comma to where '+' or '-' is expected   

    pinarray[pin_num].pnum=pin_num; // assign value to the local arrays NOT to pointers yet
    // TODO test all the other kinds of rpi's
    pinarray[pin_num].gpionum=nurpi2_gpios[pin_num];

    // get_dir, i expect *cp = '>' or '<' and return value of 0(out)  or 1(in), else err
    pin_dir=get_dir(cp);
    if(pin_dir<0){
      printf("ERR  pin #%d pin_dir invalid, must be '0'(out) or '1'(in)\n", pin_num);
      return -1;
    }else{
      pinarray[pin_num].pdir=pin_dir;
    }
    cp+=2;
    
    /* the next data to parse depends on wether input or output
        if input, next char defines wether invert pin is wanted or not
        if output, next char is initial state of pin 0or1, 
          also it is the state of the pin after any reset */
    if(pin_dir==0){ // if output, get initial value  if inputs get invert_wanted
      initial_value=get_initial(cp);
      if(initial_value < 0){
        printf("ERR  output[%d] .pval initial value must be 0 or 1\n", pin_num);
        return -1;
      }else{
        pinarray[pin_num].pval=initial_value;
      }
    }else{ // this pin is an input, find out if invert wanted
      invert_wanted=get_inv_mark(cp);
      if(invert_wanted > -1){
        pinarray[pin_num].pinv=invert_wanted;
      }else{
        printf("ERR  input[%d] invert char must be '+' or '-'\n", pin_num);
        return -1;
      }
    }
    cp+=2; // advance to last field, only used by outputs
    
    /* get reset_wanted for outputs, ignore anything for inputs  */
    if(! pinarray[pin_num].pdir){  // only check for reset on outputs, ignore tail for inputs
      reset_wanted=get_reset_mark(cp);
      if(reset_wanted == -1){
        printf("ERR  output[%d] reset mark was none of these chars:  y Y n N\n",pin_num);
        return -1;
      }else{
        pinarray[pin_num].pres=reset_wanted;
      }
    }
    cp+=2; // should be pointing to another begin marker  '{' or endofstring '\n'
    
    return 0;
}

/*-end------------------------------------------the parsing func and helpers----------------------------*/

int rtapi_app_main(void){
    int n, retval = 0;
    int ret2;
    int rev, ncores;
    
    // vvv for deadline calc in reset()
#ifdef __KERNEL__
    // this calculation fits in a 32-bit unsigned 
    // as long as CPUs are under about 6GHz
    ns2tsc_factor = (cpu_khz << 6) / 15625ul;
#else
    ns2tsc_factor = 1ll<<12;
#endif
    
    if ((rev = get_rpi_revision()) < 0) {
      rtapi_print_msg(RTAPI_MSG_ERR, "unrecognized Raspberry revision, see /proc/cpuinfo\n");
      return -EINVAL;
    }
    ncores = number_of_cores();
    rtapi_print_msg(RTAPI_MSG_INFO, "%d cores rev %d", ncores, rev);

    /* 07feb2021 i chgd the arrays 'rpi2_gpiopins' (et al) so the data is indexed by the hdr pin num
    // and i dont use the old rpi_pins at all ( useless complexity given the simple new cfg strg method using quads
    //   so index is chgd  at least
    */
    switch (rev) {
    case 5:
      rtapi_print_msg(RTAPI_MSG_INFO, "Raspberry4\n");
      //pins = rpi2_pins;  // NEW 06feb2021  not used
      gpios = nurpi2_gpios;  // >>gpios<< is an array indexed by the actual rpi hdr pin
      npins = sizeof(nurpi2_gpios);
      break;
      
    case 4:
      rtapi_print_msg(RTAPI_MSG_INFO, "Raspberry3\n");
      gpios = nurpi2_gpios;
      npins = sizeof(nurpi2_gpios);
      break;

    case 3:
      rtapi_print_msg(RTAPI_MSG_INFO, "Raspberry2\n");
      gpios = nurpi2_gpios;
      npins = sizeof(nurpi2_gpios);
      break;

    case 2:
      rtapi_print_msg(RTAPI_MSG_INFO, "Raspberry1 rev 2.0\n");
      gpios = nurev2_gpios;
      npins = sizeof(nurev2_gpios);
      break;

    case 1:
      rtapi_print_msg(RTAPI_MSG_INFO, "Raspberry1 rev 1.0\n");
      gpios = nurev1_gpios;
      npins = sizeof(nurev1_gpios);
      break;

    default:
	    rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: board revision %d not supported\n", rev);
	    return -EINVAL;
    }

    // 13feb2021 pinfoarray already has 80 posns make it 82
    pinfoarray = hal_malloc( ( pinsMAX + 1) * 2 * sizeof(void *));
    if (pinfoarray == 0) {
	    rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: hal_malloc() pinfoarray failed\n");
	    hal_exit(comp_id);
	    return -1;
    }
    
    // 13feb2021 add array of 41 out_inverts
    out_inverts = hal_malloc( ( pinsMAX + 1) * sizeof(void *));
    if (out_inverts == 0) {
	    rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: hal_malloc() out_inverts failed\n");
	    hal_exit(comp_id);
	    return -1;
    }

    reset_time_addr = hal_malloc( sizeof(hal_u32_t));  // make room for VALUE of hal_u32_t
    if (reset_time_addr == 0) {
	    rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: hal_malloc() addr for  reset_time (addr hal_u32_t *) failed\n");
	    hal_exit(comp_id);
	    return -1;
    }
    
    // 13feb2021 add array of 41 out_resets
    out_resets = hal_malloc( ( pinsMAX + 1) * sizeof(void *));
    if (out_resets == 0) {
	    rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: hal_malloc() out_resets failed\n");
	    hal_exit(comp_id);
	    return -1;
    }
    
    if (pi_pins == 0) {
	    rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: no config string\n");
	    return -1;
    }
    
    /* NB decodeone assigns the data parsed into the local arrays of structs  pinarray[n].pnum...pres
    // and DOES NOT assign the address of the new struct into the ptr array pinfoarray[n]   pinfoarray[pin_num] = &pinarray[pin_num];
    // the loop increment inside decodeone by adding to 'cp'  
    */
    cp = pi_pins;
    while( (ret2 = (decodeone(cp)) == 0 ) ) {
      if(*cp!='{'){    // if the next char is NOT a left brace, then exit the while loop
        break;
      }
    }
    
    /* this code is same as orig  i am not sure BOTH calls are needed, 
    // hint: the data returned from 1st call is clobbered by 2nd call   
    // but  'mostly harmless'
    */
    // mmap the file /dev/gpiomem into memory and get the start address 'gpio'
    if (setup_gpiomem_access()) {
      if (setup_gpio_access(rev, ncores)){
        return -1;
      }
    }
    
    // register the comp so the hal world knows its there
    comp_id = hal_init("hal_pi_gpio");
    if (comp_id < 0) {
	    rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: hal_init() failed\n");
	    return -1;
    }

    // --- beg --- create & export hal_pins
    for (n = 0; n < npins; n++) {
      if( pinarray[n].gpionum ){ 
      	// NADA  fall into ensuing code
      }else{   /* this pin is not used so skip to top of loop       */
        continue;
      } 
      if( ! pinarray[n].pdir ){ // if OUTPUT pin wanted ( ! .pdir  would be true )   then setup phys pin
	      bcm2835_gpio_fsel(gpios[n], BCM2835_GPIO_FSEL_OUTP);
        // 13feb2021 i want to use even indices for inputs and outputs, and odd indices for in-not is it wanted, index 0 and 1 not used
        
        if ((retval = hal_pin_bit_newf(HAL_IN, (hal_bit_t **)(&(pinfoarray[n+n])), comp_id, "hal_pi_gpio.pin-%02d-out", n)) < 0) {
          break;
        }

        if (( retval = hal_param_bit_newf(HAL_RW, &(out_inverts[n]), comp_id, "hal_pi_gpio.pin-%02d-out-invert", n)) < 0) {
          break;
        }
        if( pinarray[n].pres ) {
          if (( retval = hal_param_bit_newf(HAL_RW, &(out_resets[n]), comp_id, "hal_pi_gpio.pin-%02d-out-reset", n)) < 0) {
            break;
          }
          *(&(out_resets[n])) = 1; // set the bool true high 1 hokay
        }          
      }else{                    // if INPUT pin wanted ( ! .pdir  would be false )
	      bcm2835_gpio_fsel(gpios[n], BCM2835_GPIO_FSEL_INPT);
        if ((retval = hal_pin_bit_newf(HAL_OUT, (hal_bit_t **)&pinfoarray[n+n], comp_id, "hal_pi_gpio.pin-%02d-in", n)) < 0) {
          break;
        }
        if(pinarray[n].pinv){
          if ((retval = hal_pin_bit_newf(HAL_OUT, (hal_bit_t **)&pinfoarray[n+n+1], comp_id, "hal_pi_gpio.pin-%02d-in-not", n)) < 0) {
            break;
          }
        }
      }
    }
    // --- end --- create and export hal_pins

    if (retval < 0) {
      rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: pin %d export failed with err=%i\n", n,retval);
      hal_exit(comp_id);
      return -1;
    }

    // --- beg create and export hal_params    
    if ( (retval = hal_param_u32_newf(HAL_RW, reset_time_addr, comp_id, "hal_pi_gpio.reset_time")) < 0) {
      rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: param reset_time failed with err=%i\n", retval);
      hal_exit(comp_id);
      return -1;
    }
    // --- end create and export hal_params

    // export funcs,  TODO need new reset func()
    retval = hal_export_funct("hal_pi_gpio.write", write_port, 0, 0, 0, comp_id);
    if (retval < 0) {
	    rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: write funct export failed\n");
	    hal_exit(comp_id);
	    return -1;
    }

    // ok the func is named read_port but the name read is what's exported  i thought this was a disconnect
    retval = hal_export_funct("hal_pi_gpio.read", read_port, 0, 0, 0, comp_id);
    if (retval < 0) {
	    rtapi_print_msg(RTAPI_MSG_ERR,"HAL_PI_GPIO: ERROR: read funct export failed\n");
	    hal_exit(comp_id);
	    return -1;
    }

	  retval = hal_export_funct("hal_pi_gpio.reset", reset_port, 0, 0, 0, comp_id);
	  if (retval != 0) {
	    rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: reset funct export failed\n");
	    hal_exit(comp_id);
	    return -1;
	  }

    rtapi_print_msg(RTAPI_MSG_INFO,"HAL_PI_GPIO: installed driver\n");
    hal_ready(comp_id);
    // dbg     printf("driver installed, hal_ready\n");

    // output pins can have an intial value, so set them and the out-invert pins as well
    for(n=0;n<41;n++){
      if( !(pinarray[n].pdir) && (pinarray[n].gpionum) ){    // if this pin is an output  and is in use ( has gpionum assigned )
        if(pinarray[n].pval){
          *pinfoarray[n+n] = HIGH;
          *(&(out_inverts[n])) = LOW;
        }else{
          *pinfoarray[n+n] = LOW;
          *(&(out_inverts[n])) = HIGH;
        }
      }
    }
      
    return 0;
}

// TODO why would file descriptor still be open? should be closed asap
void rtapi_app_exit(void){
  if (gpio){  // >>gpio<< is a mmap object
    munmap((void *) gpio, BCM2835_BLOCK_SIZE);
  }
  if (mem_fd > -1){
    close(mem_fd);
  }
  hal_exit(comp_id);
}

/* this will write all output pins, even those with reset feature
   then will record the time when the write occured so reset can use that + the reset_time to create a 'deadline'
*/
static void write_port(void *arg, long period){
  int n;

  for (n = 0; n < npins; n++) {
    if(  (!pinarray[n].gpionum) || (pinarray[n].pdir ) ){     // if this pin is not assigned a gpionum(unused) OR input, skip to next
      continue;
    }
    // if here pin is used (has gpionum )
    if(! pinarray[n].pdir) {  // i say dir=0 means out, so, !pinfoarray[n].pdir   means if NOT 0
      // if here pin is used AND is output (.pdir is false )
      if ( *(pinfoarray[n+n]) ) {  // if value of logical pin is 1 then set the physical pin
	      bcm2835_gpio_set(gpios[n]);
        *(&(out_inverts[n])) = (hal_bit_t) LOW;
      } else {
	      bcm2835_gpio_clr(gpios[n]);
        *(&(out_inverts[n])) = (hal_bit_t) HIGH;
      }
    }
  }

  /* AFTER all writes, grab the system time  */
  write_time = rtapi_get_clocks();  // the time NOW, the time of the write
}

static void read_port(void *arg, long period){
  int n;
  for (n = 0; n < npins; n++) {  // beware that pinfoarray is 80 elements! not 40
    if ( (pinarray[n].pdir) && (pinarray[n].gpionum) ) { // if hasDir1 && has_gpionum, then its an used input
      *pinfoarray[n+n] = bcm2835_gpio_lev(gpios[n]);
      if(pinarray[n].pinv){
        *pinfoarray[n+n+1] = !(bcm2835_gpio_lev(gpios[n]));
      }
    } // there is no else, only input pins are of interest to a read func
  }
  
  /*
      // this is WRITE code but i need its work done before the write or reset funcs
      // try force value on pin 5  it worked   now on all outs ( and out inverts)
      for(n=0;n<41;n++){
        if( !(pinarray[n].pdir) && (pinarray[n].gpionum) ){    // if this pin is an output  and is in use ( has gpionum assigned )
          if(pinarray[n].pval){
            *pinfoarray[n+n] = HIGH;
            *(&(out_inverts[n])) = LOW;
          }else{
            *pinfoarray[n+n] = LOW;
            *(&(out_inverts[n])) = HIGH;
          }
        }
      }
   */  
   
  // dbg ?? does .pval stay same despite setp?? YES so the struct us storing the value to be reset to
  //printf("pinarray[5].pval = %d\t pinarray[5]pres = %d\n",pinarray[5].pval, pinarray[5].pres);
}

/* i now have a global write_time   a long long (64bit) of the systen time after the write func finished
// write_time + reset_time = deadline  for when  reset should be done
*/
static void reset_port(void *arg, long period){
  int n;
  long long deadline;
  long long reset_time_tsc;
  
  // dbg printf(" reset_time is %d\n", *(reset_time_addr) );
  // 'period' seems to be the thread period that the module is addf'd to
  // ---------------beg---- wait for deadline time to expire -------------------------------
  if(*(reset_time_addr) > period/4){  // works i setpd 60000 with a 100000 thread and got 0x00000061A8 = 25000 ;-)
     *(reset_time_addr) = period/4;
  }
  
  reset_time_tsc = ns2tsc(*(reset_time_addr));   // convert the reset_time ( given in nSecs with a setp right now )  TO decimal seconds??
  deadline = reset_time_tsc + write_time; 
  
  while(rtapi_get_clocks() < deadline) {
    // NADA just wait
  }
  // ---------------end---- wait for deadline time to expire -------------------------------

  // --------------- beg --- loop thru all resettable pins and flip them-------------------NB this relies on pin JUST chgd by write()
  for (n = 0; n < npins; n++) {
    if( (pinarray[n].gpionum)&&(!(pinarray[n].pdir))&&(pinarray[n].pres) ){ // if this pin is  A)used B)output with C)reset feature, else skip it
      if(*(pinfoarray[n+n]) != pinarray[n].pval){// test skipping pins that are already = .pval
        if(pinarray[n].pval){  // if the reset state is 1 HIGH TRUE then make the phys pin 1 HIGH TRUE SET, and the out_inverts 0 LOW FALSE CLR
	        bcm2835_gpio_set(gpios[n]);  // reset to initial value  1 HIGH TRUE SET  the val in pinarray[n].pval
          *(&(out_inverts[n])) = (hal_bit_t) LOW;
        }else{
	        bcm2835_gpio_clr(gpios[n]);  // reset to initial value 0 LOW FALSE CLR   the val in pinarray[n].pval
          *(&(out_inverts[n])) = (hal_bit_t) HIGH;
        }
        // reset the pin desire/request to the intial value .pval
        *(pinfoarray[n+n]) = pinarray[n].pval;
      }
    }else{
      continue;  // skip me,  the droids you are looking for are not here
    }
    
  }
  // --------------- end --- loop thru all resettable pins and set them to initial state-------------------
}
_______________________________________________
Emc-developers mailing list
Emc-developers@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/emc-developers

Reply via email to