Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
731 lines (587 sloc) 17.7 KB
//////////////////////////////////////////////////////////////////////////////
// //
// _ //
// __ _ _ __ (_) ___ ___ //
// / _` || '_ \ | | / _ \ / __| //
// | (_| || |_) || || (_) |_| (__ //
// \__, || .__/ |_| \___/(_)\___| //
// |___/ |_| //
// //
//////////////////////////////////////////////////////////////////////////////
// //
// Copyright (c) 2016 by Bob Frazier and S.F.T. Inc. - All rights reserved //
// Use, copying, and distribution of this software are licensed according //
// to the GPLv2 or BSD-like license, as appropriate (see LICENSE) //
// //
// //
// GUTLESS DISCLAIMER //
// ================== //
// //
// The 'gpio' utility for FreeBSD and Raspberry Pi has been //
// supplied 'as-is', with no warranties, either implied or explicit. //
// Any claims to alleged functionality or features should be //
// considered 'preliminary', and might not function as advertised. //
// //
//////////////////////////////////////////////////////////////////////////////
// THIS SOFTWARE IS BEING PROVIDED AS-IS, WITH NO WARRANTY WHATSOEVER. USE IT
// AT YOUR OWN RISK. THE AUTHOR AND/OR S.F.T. INC. CLAIM NO RESPONSIBILITY
// FOR ANY ACTIONS OR CONSEQUENCES THAT MAY ARISE FROM THE USE, ABUSE, OR
// DISTRIBUTION OF THIS SOFTWARE, INCLUDING A MODIFIED OR DERIVED VERSION.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <memory.h>
#include <sys/gpio.h>
void usage(void);
void do_status(int hF, int bVerbose, int iMaxPin);
int get_max_pin(int hF);
const char * get_pin_name(int hF, int iPin);
int get_pin_caps(int hF, int iPin);
int get_pin_flags(int hF, int iPin);
int set_pin_flags(int hF, int iPin, int iFlags);
int get_pin_value(int hF, int iPin);
int set_pin_value(int hF, int iPin, int iVal);
struct DoIt
{
int iOp;
int iPin;
int iVal;
};
#define OP_CONFIGURE_PIN 0
#define OP_READ_PIN 1
#define OP_WRITE_PIN 2
#define OP_DELAY 3
int main(int argc, char *argv[])
{
int i1, hF, iRval, iPin, iVal, iMaxPin;
const char *p1;
int bVerbose = 0;
struct DoIt *pQ = NULL;
int nQ = 0;
int bOutFlag = 0;
if(argc < 2)
{
usage();
return 0;
}
hF = open("/dev/gpioc0", O_RDWR | O_SYNC | O_DIRECT);
if(hF < 0)
{
fprintf(stderr, "unable to open device, errno=%d\n", errno);
fputs("You should consider altering the permissions on '/dev/gpioc0', or run as 'root'\n\n"
"As an example, you can add the following lines to /etc/devfs.conf:\n"
" # change the permissions on 'gpioc0' for gpio group to have access\n"
" own gpioc0 root:gpio\n"
" perm gpioc0 0660\n\n"
"This will enable the 'gpio' group to access /dev/gpioc0 and manipulate the\n"
"IO pins via the gpio application. Then, you can add all of the users that\n"
"need IO pin access to the 'gpio' group, as needed.\n\n", stderr);
return 1;
}
// allocate queue for operations
pQ = (struct DoIt *)malloc(sizeof(*pQ) * argc);
if(!pQ)
{
fprintf(stderr, "ERROR: not enough memory, errno=%d\n\n", errno);
iRval = -1;
goto return_point;
}
// query max pin. this tests the overall GPIO functionality
iMaxPin = get_max_pin(hF);
if(iMaxPin <= 0)
{
iRval = -1;
goto return_point;
}
// OPTION PARSING
iRval = 1; // default is error return at this point
while((i1 = getopt(argc, argv,
"hvsc:w:r:d:")) != -1)
{
if(i1 == 'h')
{
usage();
iRval = 0;
goto return_point;
}
else if(i1 == 's')
{
do_status(hF, bVerbose, iMaxPin);
iRval = 0;
goto return_point;
}
else if(i1 == 'v')
{
if(bVerbose)
{
fputs("ERROR - '-v' can only be specified once\n", stderr);
usage();
goto return_point;
}
bVerbose = 1;
}
else if(i1 == 'd') // delay
{
iVal = atoi(optarg); // pin number (do it the easy way)
if(iVal < 0) // no
{
fprintf(stderr, "ERROR - value out of range for -%c - %d\n", i1, iPin);
usage();
goto return_point;
}
// add to queue
pQ[nQ].iOp = OP_DELAY;
pQ[nQ].iPin = -1; // by convention, invalid pin #
pQ[nQ].iVal = iVal; // value is delay in microseconds
nQ++; // increase # items in queue (and that's all, folks!)
}
else if(i1 == 'r') // read
{
iPin = atoi(optarg); // pin number (do it the easy way)
if(iPin < 0 || iPin > iMaxPin)
{
invalid_pin_for:
fprintf(stderr, "ERROR - pin value out of range for -%c - %d\n", i1, iPin);
usage();
goto return_point;
}
// add to queue
pQ[nQ].iOp = OP_READ_PIN;
pQ[nQ].iPin = iPin;
pQ[nQ].iVal = 0; // by convention, not needed
nQ++; // increase # items in queue (and that's all, folks!)
}
else if(i1 == 'c') // configure
{
// format: PIN=[R|W]
if(!optarg || !*optarg)
{
fprintf(stderr, "ERROR - empty option for -%c\n\n", i1);
usage();
goto return_point;
}
iPin = 0;
p1 = optarg;
while(*p1 && *p1 != '=')
{
if(*p1 < '0' || *p1 > '9')
{
invalid_option_for:
fprintf(stderr, "ERROR - invalid option for -%c - \"%s\"\n\n", i1, optarg);
usage();
goto return_point;
}
iPin *= 10;
iPin += *(p1++) - '0';
}
if(iPin < 0 || iPin > iMaxPin)
{
goto invalid_pin_for;
}
if(*p1 != '=')
{
goto invalid_option_for;
}
else
{
p1++;
iVal = 0;
while(*p1)
{
if(!*p1 || *p1 == 'R' || *p1 == 'r')
{
if((iVal & GPIO_PIN_OUTPUT) || (iVal & GPIO_PIN_INPUT))
{
goto invalid_option_for;
}
iVal |= GPIO_PIN_INPUT;
}
else if(*p1 == 'W' || *p1 == 'w')
{
if((iVal & GPIO_PIN_OUTPUT) || (iVal & GPIO_PIN_INPUT))
{
goto invalid_option_for;
}
iVal |= GPIO_PIN_OUTPUT;
}
else if(*p1 == 'T' || *p1 == 't')
{
if(!(iVal & GPIO_PIN_OUTPUT) || (iVal & GPIO_PIN_INPUT))
{
goto invalid_option_for;
}
if(iVal & GPIO_PIN_OPENDRAIN)
{
goto invalid_option_for; // can't set this if I'm tri-stating the output
}
iVal |= GPIO_PIN_TRISTATE; // tristate output (effectively disabled)
}
else if(*p1 == 'O' || *p1 == 'o')
{
if(!(iVal & GPIO_PIN_OUTPUT) || (iVal & GPIO_PIN_INPUT))
{
goto invalid_option_for;
}
if(iVal & GPIO_PIN_TRISTATE)
{
goto invalid_option_for;
}
iVal |= GPIO_PIN_OPENDRAIN; // if not specified, it's push-pull
}
else if(*p1 == '+')
{
if(((iVal & GPIO_PIN_OUTPUT) && !(iVal & GPIO_PIN_OPENDRAIN)) // output AND NOT open drain
|| (!(iVal & GPIO_PIN_OUTPUT) && !(iVal & GPIO_PIN_INPUT))
|| (iVal & GPIO_PIN_PULLDOWN)) // or puldown already specified
{
fprintf(stderr, "TEMPORARY: here I am 1 iVal=%d (%04xH)\n", iVal, iVal);
goto invalid_option_for;
}
iVal |= GPIO_PIN_PULLUP; // internal pullup resistor
}
else if(*p1 == '-')
{
if(((iVal & GPIO_PIN_OUTPUT) && !(iVal & GPIO_PIN_OPENDRAIN)) // output AND NOT open drain
|| (!(iVal & GPIO_PIN_OUTPUT) && !(iVal & GPIO_PIN_INPUT))
|| (iVal & GPIO_PIN_PULLUP)) // or pullup already specified
{
fprintf(stderr, "TEMPORARY: here I am 1 iVal=%d (%04xH)\n", iVal, iVal);
goto invalid_option_for;
}
iVal |= GPIO_PIN_PULLDOWN; // internal pulldown resistor
}
else if(*p1 == '!')
{
if((iVal & GPIO_PIN_INPUT))
{
iVal |= GPIO_PIN_INVIN;
}
else if((iVal & GPIO_PIN_OUTPUT))
{
iVal |= GPIO_PIN_INVOUT;
}
else
{
goto invalid_option_for;
}
}
else
{
goto invalid_option_for;
}
p1++;
}
}
// preliminary validation
if(!(iVal & GPIO_PIN_OUTPUT) && !(iVal & GPIO_PIN_INPUT))
{
iVal |= GPIO_PIN_INPUT; // default, input
}
// add to queue
pQ[nQ].iOp = OP_CONFIGURE_PIN;
pQ[nQ].iPin = iPin;
pQ[nQ].iVal = iVal;
nQ++; // increase # items in queue (and that's all, folks!)
}
else if(i1 == 'w') // write
{
// format: PIN=[R|W]
if(!optarg || !*optarg)
{
fprintf(stderr, "ERROR - empty option for -%c\n\n", i1);
usage();
goto return_point;
}
iPin = 0;
p1 = optarg;
while(*p1 && *p1 != '=')
{
if(*p1 < '0' || *p1 > '9')
{
goto invalid_option_for;
}
iPin *= 10;
iPin += *(p1++) - '0';
}
if(iPin < 0 || iPin > iMaxPin)
{
goto invalid_pin_for;
}
if(*p1 != '=')
{
goto invalid_option_for;
}
else
{
p1++;
if(!*p1 || *p1 == '0')
{
iVal = 0;
}
else if(*p1 == '1')
{
iVal = 1;
}
else
{
goto invalid_option_for;
}
}
// add to queue
pQ[nQ].iOp = OP_WRITE_PIN;
pQ[nQ].iPin = iPin;
pQ[nQ].iVal = iVal;
nQ++; // increase # items in queue (and that's all, folks!)
}
// TODO: others
else
{
fprintf(stderr, "ERROR - unrecognized parameter - %c\n\n", i1);
usage();
goto return_point;
}
}
argc -= optind;
argv += optind;
if(argc > 1)
{
fprintf(stderr, "ERROR - unrecognized parameter - %s\n\n", argv[0]);
usage();
goto return_point;
}
iRval = 0;
for(i1=0; i1 < nQ; i1++)
{
if(pQ[i1].iOp == OP_READ_PIN)
{
if(bOutFlag)
{
fputs(" ", stdout);
}
else
{
bOutFlag = 1;
}
iVal = get_pin_value(hF, pQ[i1].iPin);
if(iVal < 0)
{
iRval = 1; // indicates 'one or more errors'
fputs("?", stdout);
}
else
{
fprintf(stdout, "%d", iVal);
}
}
else if(pQ[i1].iOp == OP_WRITE_PIN)
{
if(set_pin_value(hF, pQ[i1].iPin, pQ[i1].iVal) < 0)
{
iRval = 1; // indicates 'one or more errors'
fprintf(stderr, "ERROR: %02d: WRITE pin=%d val=%d\n",
i1, pQ[i1].iPin, pQ[i1].iVal);
}
}
else if(pQ[i1].iOp == OP_CONFIGURE_PIN)
{
if(set_pin_flags(hF, pQ[i1].iPin, pQ[i1].iVal) < 0)
{
iRval = 1; // indicates 'one or more errors'
fprintf(stderr, "ERROR: ITEM %d CONFIGURE pin=%d val=%04xH\n",
i1, pQ[i1].iPin, pQ[i1].iVal);
}
}
else if(pQ[i1].iOp == OP_DELAY)
{
usleep(pQ[i1].iVal);
}
// else
// {
// fprintf(stderr, "TEMPORARY: %02d: op=%d pin=%d val=%d\n",
// i1, pQ[i1].iOp, pQ[i1].iPin, pQ[i1].iVal);
// }
}
if(bOutFlag)
{
fputs("\n", stdout); // end of line (also a vague 'Tron' reference)
}
return_point:
free(pQ);
close(hF);
return iRval;
}
void usage(void)
{
fputs("\n" // extra newline up front, to aid readability
"gpio - for FreeBSD sysutils/gpio-rpi - version 1.0\n"
"Reads, writes, and configures GPIO pins on the Raspberry Pi\n\n"
"Usage: gpio [-h][-v][-s][-rPIN][-wPIN=[0|1]][-cPIN=type][-dDELAY]\n"
" where PIN is a valid PIN number\n"
" and type is a pin type (R, W, other options)\n"
" and DELAY is the delay value (in microseconds)\n"
" and '-v' specifies 'verbose mode' (outputs info to stderr)\n"
" and '-s' writes pin status info to stdout (other options are ignored)\n"
" and '-h' displays this help message (other options are ignored)\n\n"
" and '-c' configures a pin as read (R) or write (W), with other options\n"
" and '-r' reads a pin's value and writes '0' or '1' to stdout\n"
" and '-w' assigns a pin's output value as '0' or '1'\n"
" and '-d' specifies a delay period (useful for toggling a pin)\n"
"With the exception of '-s', '-v', and '-h', you can specify an option\n"
"more than once. For example if you want to read I/O pins 3 and 4, you\n"
"can use a command line similar to the following:\n\n"
" gpio -r3 -r4\n\n"
"This will give you an output value of each bit value, separated with\n"
"a single space character, similar to this:\n\n"
" 0 1\n\n"
"(note that the indent is for readability; actual output starts at column 0)\n\n"
"Additionally, if you want to toggle a pin, you can do so by specifying\n"
"multiple 'W' values for the same pin, with an optional delay in between\n\n"
" gpio -w16=0 -d250000 -w16=1 -d250000 -w16=0 -d250000 -w16=1\n\n"
"This will blink the status LED twice for a period of 1/4 second.\n\n"
"Specifying '-s' or '-h' ignores all other options on the command line. If\n"
"you want verbose output on '-s', specify '-v' before the '-s'.\n\n", stderr);
}
void do_status(int hF, int bVerbose, int iMaxPin)
{
int i1;
if(bVerbose)
{
fputs("PIN\tTYPE\tValue\tFlags\tCaps\tName\n", stdout);
}
for(i1=0; i1 < iMaxPin; i1++)
{
int iFlags = get_pin_flags(hF, i1);
if(iFlags < 0)
{
continue;
}
if(!bVerbose &&
!(iFlags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)))
{
continue; // don't display disabled pins when NOT verbose mode
}
fprintf(stdout, "%d\t%c\t%d\t%04xH", i1,
(iFlags & GPIO_PIN_OUTPUT) ? 'O' : (iFlags & GPIO_PIN_INPUT) ? 'I' : 'd',
get_pin_value(hF, i1),
iFlags);
if(bVerbose)
{
fprintf(stdout, "\t%04xH\t\"%s\"\n", get_pin_caps(hF, i1), get_pin_name(hF, i1));
}
else
{
fputs("\n", stdout);
}
}
}
int get_max_pin(int hF)
{
int iRet, iRval;
iRval = 0;
iRet = ioctl(hF, GPIOMAXPIN, &iRval);
if(iRet == -1)
{
fprintf(stderr, "ERROR: ioctl fail, GPIOMAXPIN, errno=%d\n", errno);
return -1;
}
return iRval;
}
const char *get_pin_name(int hF, int iPin)
{
int iRet;
struct gpio_pin xPin;
static char szBuf[GPIOMAXNAME + 2];
bzero(szBuf, sizeof(szBuf));
bzero(&xPin, sizeof(xPin));
xPin.gp_pin = iPin;
iRet = ioctl(hF, GPIOGETCONFIG, &xPin);
if(iRet < 0)
{
fprintf(stderr, "ERROR: ioctl fail, GPIOGETCONFIG, errno=%d\n", errno);
return "ERR";
}
memcpy(szBuf, xPin.gp_name, GPIOMAXNAME);
return szBuf;
}
int get_pin_flags(int hF, int iPin)
{
int iRet;
struct gpio_pin xPin;
bzero(&xPin, sizeof(xPin));
xPin.gp_pin = iPin;
iRet = ioctl(hF, GPIOGETCONFIG, &xPin);
if(iRet < 0)
{
fprintf(stderr, "ERROR: ioctl fail, GPIOGETCONFIG, errno=%d\n", errno);
return -1;
}
return xPin.gp_flags;
}
int get_pin_caps(int hF, int iPin)
{
int iRet;
struct gpio_pin xPin;
bzero(&xPin, sizeof(xPin));
xPin.gp_pin = iPin;
iRet = ioctl(hF, GPIOGETCONFIG, &xPin);
if(iRet < 0)
{
fprintf(stderr, "ERROR: ioctl fail, GPIOGETCONFIG, errno=%d\n", errno);
return -1;
}
return xPin.gp_caps;
}
int set_pin_flags(int hF, int iPin, int iFlags)
{
int iRet, iCaps;
struct gpio_pin xPin;
bzero(&xPin, sizeof(xPin));
xPin.gp_pin = iPin;
xPin.gp_flags = iFlags;
iCaps = get_pin_caps(hF, iPin);
if((iFlags & iCaps) != iFlags) // flags specified that aren't capable?
{
fprintf(stderr, "ERROR: ioctl caps do not match pin=%d, caps=%04xH, flags=%04xH\n",
iPin, iCaps, iFlags);
return -1;
}
iRet = ioctl(hF, GPIOSETCONFIG, &xPin);
if(iRet < 0)
{
fprintf(stderr, "ERROR: ioctl fail, GPIOSETCONFIG, errno=%d pin=%d, flags=%04xH\n", errno, iPin, iFlags);
return -1;
}
return 0;
}
int get_pin_value(int hF, int iPin)
{
int iRet;
struct gpio_req xReq;
bzero(&xReq, sizeof(xReq));
xReq.gp_pin = iPin;
iRet = ioctl(hF, GPIOGET, &xReq);
if(iRet < 0)
{
fprintf(stderr, "ERROR: ioctl fail, GPIOGET, errno=%d\n", errno);
return -1;
}
return xReq.gp_value; // either 0 or 1, i.e. GPIO_PIN_LOW == 0, GPIO_PIN_HIGH == 1
}
int set_pin_value(int hF, int iPin, int iVal)
{
int iRet;
struct gpio_req xReq;
bzero(&xReq, sizeof(xReq));
xReq.gp_pin = iPin;
xReq.gp_value = iVal;
iRet = ioctl(hF, GPIOSET, &xReq);
if(iRet < 0)
{
fprintf(stderr, "ERROR: ioctl fail, GPIOSET, errno=%d\n", errno);
return -1;
}
return 0; // success!
}