Can someone look over some source code for me (C)?

Mixolydian

Lifer
Nov 7, 2011
14,570
91
86
gilramirez.net
Hello there! I could really use some help from you programming gurus. There's a lot about programming that I do not understand and as a result I am stumped.

Basically, I have a program on a UNIX box which controls an external device (GPIO) using ioctl. The problem is, I have no idea what arguments to pass in order to control the output. There is no built-in help or documentation.

I DO however have all of the source code (C or C++, likely the former) for the program. I am wondering if someone could determine what format the argument(s) need to be in by looking at the source code.

All of the values are some form of boolean but I'm not sure if binary, hex, etc...

PM me if you are willing to help.
Thanks! :)
 

Mixolydian

Lifer
Nov 7, 2011
14,570
91
86
gilramirez.net
Well there are source files for a couple different programs but it appears that more or less they all do the same thing...I've included two of them, "gpiout" and "run". And of course the header file.

"gpiout.c":
Code:
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include "gpi.h"

#define DEVICE "/dev/gpi"
#define SPIN(v) {int _x = v;while(_x>0) { _x--;sginap(1); } }

/* Which signal lines are the input */
#define BUSY_LINE	0x80
#define ACK_LINE	0x40
#define PERROR_LINE	0x20
#define SLCT_LINE	0x10
#define FAULT_LINE	0x08

#define ACK_SHFT		5 /* actual shift minus 1 */
#define FAULT_SHFT		3
#define INPUT_BIT_MASK	(ACK_LINE|FAULT_LINE)
#define Crunch(x)       ((((x)&ACK_LINE)>>ACK_SHFT)|(((x)&FAULT_LINE)>>FAULT_SHFT))


main(int argc, char **argv)
{

	int fd;
	int retval;
	int count;
	char byte;
	char last_out_byte;


	fd=open(DEVICE,O_RDWR,0666);
	if( fd < 0 ) {
		perror("Cannot open");
		exit(-1);
	}

	if(argc > 1 ) {
		byte=atoi(argv[1]);
		byte = ~byte &0x3f;
		retval = ioctl(fd,GPI_OUTPUT,&byte);
		if( retval < 0 ) {
			perror("WRITE ");
			exit(-1);
		} 
		retval = ioctl(fd,GPI_RETURN_OUTPUT,&last_out_byte);
		if( retval < 0 ) {
			perror("GPI_RETURN_OUTPUT ");
			exit(-1);
		} 
		printf("last_out_byte = 0x%x\n", last_out_byte);

		sginap(300); /* 3 second nap */
	}
	else /* INPUT TEST */
	{
		retval = ioctl(fd,GPI_INPUT,&byte);
		printf("in Byte %x\n",byte);

		retval = ioctl(fd,GPI_INPUT_INTR,INTERRUPT_ENABLE);
		retval = ioctl(fd,GPI_WAIT_FOR_INPUT,&byte);
		if( retval < 0 ) {
			perror("Cannot wait");
		}
		printf("Input %x\n",byte);
	}


	/* FINAL cleanup */
	byte = 0x3f;
	retval = ioctl(fd,GPI_OUTPUT,&byte);

}

"lrun.c":
Code:
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include "gpi.h"

#define DEVICE "/dev/gpi"
#define SPIN(v) {int _x = v;while(_x>0) { _x--;sginap(1); } }

/* Which signal lines are the input */
#define BUSY_LINE	0x80
#define ACK_LINE	0x40
#define PERROR_LINE	0x20
#define SLCT_LINE	0x10
#define FAULT_LINE	0x08

#define ACK_SHFT		5 /* actual shift minus 1 */
#define FAULT_SHFT		3
#define INPUT_BIT_MASK	(ACK_LINE|FAULT_LINE)
#define Crunch(x)       ((((x)&ACK_LINE)>>ACK_SHFT)|(((x)&FAULT_LINE)>>FAULT_SHFT))


main(int argc, char **argv)
{

	int fd;
	int retval;
	char out_byte;
	char last_out_byte=0;
	char in_byte;


	printf("Opening %s\n", DEVICE);
	fd=open(DEVICE,O_RDWR,0666);
	if( fd < 0 ) {
		perror("Cannot open");
		exit(-1);
	}

	/* print the initial state */
	printf("Getting initial state\n");
	retval = ioctl(fd,GPI_RETURN_OUTPUT,&last_out_byte);
	if( retval < 0 ) {
		printf("ERROR: GPI_RETURN_OUTPUT ");
		exit(-1);
	} 
	printf("last_out_byte = 0x%x\n", last_out_byte);


	retval = ioctl(fd,GPI_INPUT,&in_byte);
	printf("Initial in Byte %x\n",in_byte);

	retval = ioctl(fd,GPI_INPUT_INTR,INTERRUPT_ENABLE);
	if( retval < 0 ) {
		perror("GPI_INPUT_INTR ");
		exit(-1);
	} 

	retval = ioctl(fd,GPI_WAIT_FOR_INPUT,&in_byte);
	if( retval < 0 ) {
		perror("GPI_WAIT_FOR_INPUT ");
	}
	printf("Input %x\n",in_byte);




#define TAPE_GPI 2

	out_byte = (~(1<<TAPE_GPI))&0x3F;
	retval = ioctl(fd,GPI_OUTPUT,&out_byte);
	if( retval < 0 ) {
		perror("GPI_OUTPUT ");
		exit(-1);
	} 

	retval = ioctl(fd,GPI_RETURN_OUTPUT,&last_out_byte);
	if( retval < 0 ) {
		perror("GPI_RETURN_OUTPUT ");
		exit(-1);
	} 
	printf("last_out_byte = 0x%x\n", last_out_byte);


	printf("Sleeping for 68 seconds.\n");
	sginap(6800); /* 68 second nap */

	/* Final cleanup. Open all contacts */
	out_byte = 0x3f;
	retval = ioctl(fd,GPI_OUTPUT,&out_byte);

}

"gpi.h":
Code:
/*
| Ioctl interface.
*/
#define CMDPRE				0x54574300

#define GPI_INPUT_INTR			(CMDPRE|0x1)
#define GPI_OUTPUT			(CMDPRE|0x2)
#define GPI_INPUT			(CMDPRE|0x3)
#define GPI_WAIT_FOR_INPUT		(CMDPRE|0x4)
#define GPI_RETURN_OUTPUT		(CMDPRE|0x5)

#define INPUT_I1_MASK			0x1
#define INPUT_I2_MASK			0x2

#define OUTPUT_MASK			0x3F

#define INTERRUPT_ENABLE		0x1
#define INTERRUPT_DISABLE		0x2



#ifdef _KERNEL

#define		BOOT_UP_VAL	0x3F     /* Value turns off output couples */
#define		O_SHIFT			2
#define		OutShift(x)		(((x)&OUTPUT_MASK)<<O_SHIFT)
#define	InShift(x)		(((x)>> O_SHIFT)& OUTPUT_MASK)
/*
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Sync locks
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#define SLP_PRI          ((PZERO+1) |PCATCH)
#define MTX_PRI          (PZERO-1) 
#define EventSema(x)    &(x)->eventSema
#define SleepEvent(x)   psema(EventSema(x),SLP_PRI)
#define EventWakeup(x)  vsema(EventSema(x))
#define CEventWakeup(x)  cvsema(EventSema(x))

#define MutexSema(x)	&(x)->mutexSema
#define MutexLock(x)	psema(MutexSema(x),MTX_PRI);
#define MutexUnlock(x)	vsema(MutexSema(x))

/* Which signal lines are the input */
#define BUSY_LINE	0x80
#define ACK_LINE	0x40
#define PERROR_LINE	0x20
#define SLCT_LINE	0x10
#define FAULT_LINE	0x08

#define ACK_SHFT		6 /* actual shift minus 1 */
#define FAULT_SHFT		2
#define INPUT_BIT_MASK	(ACK_LINE|FAULT_LINE)
#define Crunch(x)	((((x)&ACK_LINE)>>ACK_SHFT)|(((x)&FAULT_LINE)>>FAULT_SHFT))
#define Invert(x)	((~(x))&0x3)

struct gpi_s {
 sema_t eventSema;
 sema_t mutexSema;
 char outValue;
 int intrStatus;
};

typedef struct gpi_s *gpi_t;
#endif /* _KERNEL */
 

uclabachelor

Senior member
Nov 9, 2009
448
0
71
From the source code posted, it looks like ioctl takes in three parameters:

1. The device pointer, which is created from the open() command.

2. The command, which is defined in gpi.h lines 3-7, GPI_XXXXXX

3. The pointer to the variable to set/read the value of the port.

On a basic level, you read port value and store the results in a variable, or similarly, you write to the port using the values from a variable.

So to read the values on the port, you have to issue:

ioctl(fd, GPI_INPUT, &in_byte); //where in_byte is a variable that will store the value of the i/o port.

To output value(s) to the port, you have to issue:

ioctl(fd, GPI_OUTPUT, &out_byte); //where out_byte is a variable that contains the values for the i/o port.

If the out_byte value is 0xAA, the output ports will result in 10101010 (which is the binary representation of 0xAA). That means anything that is wired to bit 7, 5, 3, and 1 of that port will be asserted a logic high (3.3V or 5V, or whatever the I/O voltage is) and anything that is wired to bit 6, 4, 2, and 0 will be low (0 volts).
 

Mixolydian

Lifer
Nov 7, 2011
14,570
91
86
gilramirez.net
From the source code posted, it looks like ioctl takes in three parameters:

1. The device pointer, which is created from the open() command.

2. The command, which is defined in gpi.h lines 3-7, GPI_XXXXXX

3. The pointer to the variable to set/read the value of the port.

On a basic level, you read port value and store the results in a variable, or similarly, you write to the port using the values from a variable.

So to read the values on the port, you have to issue:

ioctl(fd, GPI_INPUT, &in_byte); //where in_byte is a variable that will store the value of the i/o port.

To output value(s) to the port, you have to issue:

ioctl(fd, GPI_OUTPUT, &out_byte); //where out_byte is a variable that contains the values for the i/o port.

If the out_byte value is 0xAA, the output ports will result in 10101010 (which is the binary representation of 0xAA). That means anything that is wired to bit 7, 5, 3, and 1 of that port will be asserted a logic high (3.3V or 5V, or whatever the I/O voltage is) and anything that is wired to bit 6, 4, 2, and 0 will be low (0 volts).

That makes sense, however I get an error whenever I try to run either program with either 0xAA or 10101010 as arguments. For gpiout and lrun it returns the errors "WRITE : Invalid argument" and "ERROR: GPI_RETURN_OUTPUT" respectively. :confused:
 

Mark R

Diamond Member
Oct 9, 1999
8,513
14
81
Looking at lrun, which is the simpler program, the first thing that lrun does is run GPI_RETURN_OUTPUT.

It looks like GPI_RETURN_OUTPUT takes no parameters, and retrieves the last transmitted command from the driver.

If this is erroring, then there must be a problem with the driver or the hardware, as it takes no parameters.
 

uclabachelor

Senior member
Nov 9, 2009
448
0
71
That makes sense, however I get an error whenever I try to run either program with either 0xAA or 10101010 as arguments. For gpiout and lrun it returns the errors "WRITE : Invalid argument" and "ERROR: GPI_RETURN_OUTPUT" respectively. :confused:

What is the exact command you are using to run the executable?
 

Mixolydian

Lifer
Nov 7, 2011
14,570
91
86
gilramirez.net
Looking at lrun, which is the simpler program, the first thing that lrun does is run GPI_RETURN_OUTPUT.

It looks like GPI_RETURN_OUTPUT takes no parameters, and retrieves the last transmitted command from the driver.

If this is erroring, then there must be a problem with the driver or the hardware, as it takes no parameters.
Thanks for the tip. I reinstalled the driver and it seems to work now.

What is the exact command you are using to run the executable?
./lrun 111111
./lrun 0x3F
same with gpiout.

Now that I know the program works, the next issue is figuring out how to trigger the specific outputs. the GPIO board has 6 outputs and 2 inputs. Here is the information I have regarding that:

Code:
A. Output section
  The 6 outputs are opto-isolated NPN (bipolar) transistors. When a
 value of 0 is output the transistor turns on (low impedance)  and when 
 a value of 1 is output the transistor turns off (high impedance).  
  Each output has two connections which are the collector and the emitter
 of the NPN isolation transistor. Note that the collector has a 100 ohm
 current limiting resistor in series.

B. Input section
  The 2 inputs are opto-isolated. The connections are to the anode
 and cathode of the isolator's led. When foward biased (on) the input
 is read as 1, when  reversed biased (off) the input is read as a 0.

C. Input interrupt
  Only one input can interrupt the O2. Input 1 (I1) is capable of
 interrupt if enabled by software.  
 
  Note that the interrupt is level sensitive. Hence the driver turns
 off the interrupt when first received. The interrupt must be reenabled
 after the stimulus is removed by software.

2. Software interface
  All commication to the GPI driver is done via a ioctl(2) interface.
 The commands for this interface all return 0 for success and -1 for
 failure (with the errno set). The command are included in gpi.h, which
 must be included into the application source code. They are:

 A. GPI_OUTPUT

 int retval;
 int fd;
 char byte;

   fd = open("/dev/gpi",O_RDWR,666); 
 ...
  byte = ~(0x1);
  retval = ioctl(fd,GPI_OUTPUT,&byte);
  ...

  The above fragment will open the gpi device and output 111110 to
 the GPI output pins. This would cause output 1 to turn on (low impedance).
 Note that the one's complment is taken because to turn on the transistor
 you must output a 0 to that bit.
  NOTE that only lower 6 bits are ouput. The convience function in gpi.h:

#define OUTPUT_MASK                     0x3F


 B. GPI_INPUT
   char byte;

   retval = ioctl(fd,GPI_INPUT,&byte);

   The value of byte will reflect the 2 inputs. They are the 2 lsb bits.
 Input 1 is bit 0 and input 2 is bit 1. Note the convience mask in 
 gpi.h:
 
#define INPUT_I1_MASK                   0x1
#define INPUT_I2_MASK                   0x2


 C. GPI_INPUT_INTR

  retval=ioctl(fd,GPI_INPUT_INTR,INTERRUPT_ENABLE);

  This code fragment will enable a interrupt from input 1. Note that
 after an interrupt the sofware must renable interrupts. The interrupts
 should only be reenabled after the input returns to off (reversed biased).

 D. GPI_WAIT_FOR_INPUT

   retval=ioctl(fd,GPI_WAIT_FOR_INPUT,&byte);

   If interrupt is enabled, this command will wait for the interrupt
 and return the value of the inputs. If the interrupts are not enabled
 this command will return immediately with EINVAL.

 E. GPI_RETURN_OUTPUT

  retval=ioctl(fd,GPI_RETURN_OUTPUT,&byte);

  This command will return the last value written to the GPI outputs.

The problem is that the ports are not physically numbered.
 

Mixolydian

Lifer
Nov 7, 2011
14,570
91
86
gilramirez.net
OK! I was able to get it to do exactly what I wanted it to do...just had to play around with the binary arguments a bit.

Thank you guys so much for your help!!! :)