I am not impressed with libgpiod. Frankly it seems to be a retrograde step compared to sysfs.
Here is some partial code which calls up other not included code. It should give you enough of an idea to get your own code working.
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
#include <poll.h>
#include <errno.h>
#include <string.h>
#include "lgHdl.h"
#include "lgDbg.h"
#include "lgErr.h"
#include "lgGPIO.h"
#define LG_GPIO_MAGIC 1623920569
#define LG_MODE_UNUSED 0
#define LG_MODE_GPIO 1
#define LG_MODE_EVENT 2
#define LG_GPIO_MAX_EVENTS_PER_READ 16
typedef struct lgGPIOState_s
{
int mode;
int flags;
int group_size;
int fd;
uint32_t offset;
uint32_t *offsets;
uint8_t *values;
} lgGPIOState_t;
typedef struct lgChip_s
{
uint32_t magic;
char name[LG_GPIO_NAME_LEN];
char label[LG_GPIO_LABEL_LEN];
uint32_t lines;
int fd;
lgGPIOState_t *GPIOState;
char defaultClaimUser[LG_GPIO_USER_LEN];
uint32_t claimNextExtraFlags;
} lgChip_t, *lgChip;
void _lgGpioCloseChip(GPIOHandle_t *h)
{
int i;
lgChip chip;
chip = h->chip;
if ((chip == NULL) || (chip->magic != LG_GPIO_MAGIC)) return;
chip->magic = 0;
for (i=0; i<chip->lines; i++)
{
if (chip->GPIOState[i].mode != LG_MODE_UNUSED)
{
/* release GPIO */
LG_DBG(LG_DEBUG_ALLOC, "release GPIO: %d mode %d (%d of %d)",
i,
chip->GPIOState[i].mode,
chip->GPIOState[i].offset+1,
chip->GPIOState[i].group_size);
if (chip->GPIOState[i].offset == 0)
{
LG_DBG(LG_DEBUG_ALLOC, "close fd: %d", chip->GPIOState[i].fd);
close(chip->GPIOState[i].fd);
LG_DBG(LG_DEBUG_ALLOC, "free offsets: *%p, values: *%p",
(void*)chip->GPIOState[i].offsets,
(void*)chip->GPIOState[i].values);
free(chip->GPIOState[i].offsets);
free(chip->GPIOState[i].values);
}
}
}
LG_DBG(LG_DEBUG_ALLOC, "free GPIOState: *%p", (void*)chip->GPIOState);
free(chip->GPIOState);
LG_DBG(LG_DEBUG_ALLOC, "close chip fd: %d", chip->fd);
close(chip->fd);
LG_DBG(LG_DEBUG_ALLOC, "free chip: *%p", (void*)chip);
free(chip);
}
int lgGpioOpenChip(char *device)
{
int fd;
int handle;
GPIOHandle_t *h;
lgChip chip;
struct gpiochip_info info;
LG_DBG(LG_DEBUG_GPIO, "device=%s", device);
fd = open(device, 0);
if (fd < 0) return LG_CANNOT_OPEN_CHIP;
if (ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &info))
{
close(fd);
return LG_NOT_A_GPIOCHIP;
}
chip = calloc(1, sizeof(lgChip_t));
if (chip == NULL)
{
close(fd);
return LG_NOT_ENOUGH_MEMORY;
}
LG_DBG(LG_DEBUG_ALLOC, "alloc chip: *%p", (void*)chip);
chip->GPIOState = calloc(info.lines, sizeof(lgGPIOState_t));
if (chip->GPIOState == NULL)
{
free(chip);
close(fd);
return LG_NOT_ENOUGH_MEMORY;
}
handle = lgHdlAlloc(LG_HDL_TYPE_GPIO, sizeof(GPIOHandle_t), _lgGpioCloseChip);
if (handle >= 0)
{
h = lgHdlGetObj(handle);
h->chip = chip;
}
else
{
free(chip);
close(fd);
return LG_NOT_ENOUGH_MEMORY;
}
LG_DBG(LG_DEBUG_ALLOC, "alloc GPIOState: *%p", (void*)chip->GPIOState);
chip->magic = LG_GPIO_MAGIC;
strncpy(chip->name, info.name, sizeof(chip->name));
strncpy(chip->label, info.label, sizeof(chip->label));
chip->lines = info.lines;
chip->fd = fd;
strncpy(chip->defaultClaimUser, "lg", sizeof(chip->defaultClaimUser));
chip->claimNextExtraFlags = 0;
return handle;
}
int lgGpioCloseChip(int handle)
{
LG_DBG(LG_DEBUG_GPIO, "handle=%d", handle);
return lgHdlFree(handle, LG_HDL_TYPE_GPIO);
}
int lgGpioGetLineInfo(int handle, unsigned gpio, lgLine_t *line)
{
int status;
struct gpioline_info linfo;
GPIOHandle_t *h;
lgChip chip;
LG_DBG(LG_DEBUG_GPIO, "handle=%d gpio=%d line=*%p",
handle, gpio, (void*)line);
status = lgHdlCheckType(handle, LG_HDL_TYPE_GPIO);
if (status < 0) return status;
h = lgHdlGetObj(handle);
chip = h->chip;
if ((chip == NULL) || (chip->magic != LG_GPIO_MAGIC)) return LG_CHIP_NOT_OPEN;
if (gpio >= chip->lines) return LG_BAD_GPIO_NUMBER;
linfo.line_offset = gpio;
status = ioctl(chip->fd, GPIO_GET_LINEINFO_IOCTL, &linfo);
if (status == 0)
{
line->offset = linfo.line_offset;
line->flags = linfo.flags;
strncpy(line->name, linfo.name, sizeof(line->name));
strncpy(line->user, linfo.consumer, sizeof(line->user));
}
else status = LG_BAD_LINEINFO_IOCTL;
return status;
}
int lgGpioHandleRequest(int handle, struct gpiohandle_request *req)
{
int i, gpio;
int status;
uint32_t *offsets;
uint8_t *values;
GPIOHandle_t *h;
lgChip chip;
LG_DBG(LG_DEBUG_GPIO, "handle=%d req=*%p", handle, (void*)req);
LG_DBG(LG_DEBUG_ALLOC, "request %d with flags %d: GPIO=[%s]",
req->lines, req->flags, lgDbgInt2Str(req->lines, (int *)req->lineoffsets));
status = lgHdlCheckType(handle, LG_HDL_TYPE_GPIO);
if (status < 0) return status;
h = lgHdlGetObj(handle);
chip = h->chip;
if ((chip == NULL) || (chip->magic != LG_GPIO_MAGIC)) return LG_CHIP_NOT_OPEN;
status = ioctl(chip->fd, GPIO_GET_LINEHANDLE_IOCTL, req);
if (status == 0)
{
offsets = calloc(req->lines, sizeof(uint32_t));
values = calloc(GPIOHANDLES_MAX, sizeof(uint8_t));
if ((offsets == NULL) || (values == NULL))
{
free(offsets);
free(values);
close(req->fd);
return LG_NOT_ENOUGH_MEMORY;
}
LG_DBG(LG_DEBUG_ALLOC, "alloc offsets: *%p, values: *%p",
(void*)offsets, (void*)values);
for (i=0; i<req->lines; i++)
{
gpio = req->lineoffsets[i];
chip->GPIOState[gpio].mode = LG_MODE_GPIO;
chip->GPIOState[gpio].flags = req->flags;
chip->GPIOState[gpio].group_size = req->lines;
chip->GPIOState[gpio].fd = req->fd;
chip->GPIOState[gpio].offset = i;
chip->GPIOState[gpio].offsets = offsets;
chip->GPIOState[gpio].values = values;
offsets[i] = gpio;
values[i] = req->default_values[i];
}
}
else
{
if (errno == EBUSY) status = LG_GPIO_BUSY;
else
{
fprintf(stderr, "*** error %d (%s) ***\n", errno, strerror(errno));
status = LG_UNEXPECTED_ERROR;
}
}
return status;
}
int lgGpioRelease(int handle, unsigned id)
{
lgGPIOState_t *GPIO;
int i, g, status;
GPIOHandle_t *h;
lgChip chip;
LG_DBG(LG_DEBUG_GPIO, "handle=%d id=%d", handle, id);
status = lgHdlCheckType(handle, LG_HDL_TYPE_GPIO);
if (status < 0) return status;
h = lgHdlGetObj(handle);
chip = h->chip;
if ((chip == NULL) || (chip->magic != LG_GPIO_MAGIC)) return LG_CHIP_NOT_OPEN;
if (id >= chip->lines) return LG_BAD_GPIO_NUMBER;
GPIO = &chip->GPIOState[id];
switch (GPIO->mode)
{
case LG_MODE_UNUSED:
return LG_OKAY;
case LG_MODE_GPIO:
LG_DBG(LG_DEBUG_ALLOC, "release GPIO: %d (mode %d)", id, GPIO->mode);
for (i=0; i<GPIO->group_size; i++)
{
g = GPIO->offsets[i];
LG_DBG(LG_DEBUG_ALLOC, "set unused: %d", g);
chip->GPIOState[g].mode = LG_MODE_UNUSED;
}
LG_DBG(LG_DEBUG_ALLOC, "close fd: %d", GPIO->fd);
close(GPIO->fd);
LG_DBG(LG_DEBUG_ALLOC, "free offsets: *%p, values: *%p",
(void*)GPIO->offsets, (void*)GPIO->values);
free(GPIO->offsets);
free(GPIO->values);
return LG_OKAY;
case LG_MODE_EVENT:
close(GPIO->fd);
GPIO->mode = LG_MODE_UNUSED;
return LG_OKAY;
}
return LG_BAD_RELEASE_OBJECT;
}
int lgGpioSetClaimUserLabel(int handle, char *user)
{
int status;
GPIOHandle_t *h;
lgChip chip;
status = lgHdlCheckType(handle, LG_HDL_TYPE_GPIO);
if (status < 0) return status;
h = lgHdlGetObj(handle);
chip = h->chip;
strncpy(chip->defaultClaimUser, user, sizeof(chip->defaultClaimUser)-1);
return LG_ERR_NONE;
}
int lgGpioSetClaimExtraFlags(int handle, uint32_t flags)
{
int status;
GPIOHandle_t *h;
lgChip chip;
status = lgHdlCheckType(handle, LG_HDL_TYPE_GPIO);
if (status < 0) return status;
h = lgHdlGetObj(handle);
chip = h->chip;
chip->claimNextExtraFlags = flags;
return LG_ERR_NONE;
}
int lgGpioClaim(
int handle,
unsigned flags,
unsigned size,
unsigned *gpios,
unsigned *values)
{
int i;
uint32_t addedFlags;
int status;
GPIOHandle_t *h;
lgChip chip;
struct gpiohandle_request req;
LG_DBG(LG_DEBUG_GPIO, "handle=%d size=%d gpios=[%s] values=[%s] flags=%x",
handle, size, lgDbgInt2Str(size, (int*)gpios),
lgDbgInt2Str(size, (int*)values), flags);
/*
Any flags may be set by a direct call to lgClaim.
Only LG_GPIO_SET_ACTIVE_LOW, LG_GPIO_SET_OPEN_DRAIN, LG_GPIO_SET_OPEN_SOURCE
may be added as flags by lgGpioSetNextClaimAddFlags.
*/
status = lgHdlCheckType(handle, LG_HDL_TYPE_GPIO);
if (status < 0) return status;
h = lgHdlGetObj(handle);
chip = h->chip;
addedFlags = chip->claimNextExtraFlags &
(LG_GPIO_SET_ACTIVE_LOW|LG_GPIO_SET_OPEN_DRAIN|LG_GPIO_SET_OPEN_SOURCE);
chip->claimNextExtraFlags = 0;
if (size && (size <= GPIOHANDLES_MAX))
{
for (i=0; i<size; i++)
{
req.lineoffsets[i] = gpios[i];
if (values != NULL) req.default_values[i] = values[i];
else req.default_values[i] = 0;
}
req.flags = flags | addedFlags;
strncpy(req.consumer_label, chip->defaultClaimUser, sizeof(req.consumer_label));
req.lines = size;
return lgGpioHandleRequest(handle, &req);
}
else return LG_BAD_GROUP_SIZE;
}
int lgGpioClaimInput(int handle, unsigned gpio)
{
LG_DBG(LG_DEBUG_GPIO, "handle=%d gpio=%d", handle, gpio);
return lgGpioClaim(handle, GPIOHANDLE_REQUEST_INPUT, 1, &gpio, NULL);
}
int lgGpioClaimOutput(int handle, unsigned gpio, unsigned value)
{
LG_DBG(LG_DEBUG_GPIO, "handle=%d gpio=%d value=%d",
handle, gpio, value);
return lgGpioClaim(handle, GPIOHANDLE_REQUEST_OUTPUT, 1, &gpio, &value);
}
int lgGpioClaimInputGroup(int handle, unsigned size, unsigned *gpios)
{
LG_DBG(LG_DEBUG_GPIO, "handle=%d size=%d gpios=[%s]",
handle, size, lgDbgInt2Str(size, (int*)gpios));
return lgGpioClaim(handle, GPIOHANDLE_REQUEST_INPUT, size, gpios, NULL);
}
int lgGpioClaimOutputGroup(
int handle, unsigned size, unsigned *gpios, unsigned *values)
{
LG_DBG(LG_DEBUG_GPIO, "handle=%d size=%d gpios=[%s] values=[%s]",
handle, size, lgDbgInt2Str(size, (int*)gpios),
lgDbgInt2Str(size, (int*)values));
return lgGpioClaim(handle, GPIOHANDLE_REQUEST_OUTPUT, size, gpios, values);
}
int lgGpioClaimEvent(int handle, unsigned gpio, unsigned eFlags)
{
uint32_t addedFlags;
int status;
GPIOHandle_t *h;
lgChip chip;
struct gpioevent_request req;
LG_DBG(LG_DEBUG_GPIO, "handle=%d gpio=%d eFlags=%x",
handle, gpio, eFlags);
status = lgHdlCheckType(handle, LG_HDL_TYPE_GPIO);
if (status < 0) return status;
h = lgHdlGetObj(handle);
chip = h->chip;
addedFlags = chip->claimNextExtraFlags;
chip->claimNextExtraFlags = 0;
if ((chip == NULL) || (chip->magic != LG_GPIO_MAGIC)) return LG_CHIP_NOT_OPEN;
if (gpio >= chip->lines) return LG_BAD_GPIO_NUMBER;
req.lineoffset = gpio;
req.handleflags = LG_GPIO_SET_INPUT | addedFlags;
req.eventflags = eFlags;
strncpy(req.consumer_label, chip->defaultClaimUser, sizeof(req.consumer_label));
status = ioctl(chip->fd, GPIO_GET_LINEEVENT_IOCTL, &req);
if (status == 0)
{
chip->GPIOState[gpio].mode = LG_MODE_EVENT;
chip->GPIOState[gpio].flags = addedFlags;
chip->GPIOState[gpio].group_size = 1;
chip->GPIOState[gpio].fd = req.fd;
chip->GPIOState[gpio].offset = 0;
chip->GPIOState[gpio].offsets = NULL;
chip->GPIOState[gpio].values = NULL;
return LG_OKAY;
}
else
return LG_BAD_EVENT_REQUEST;
}
int lgGpioRead(int handle, unsigned gpio)
{
int status;
lgGPIOState_t *GPIO;
GPIOHandle_t *h;
lgChip chip;
LG_DBG(LG_DEBUG_GPIO, "handle=%d gpio=%d", handle, gpio);
status = lgHdlCheckType(handle, LG_HDL_TYPE_GPIO);
if (status < 0) return status;
h = lgHdlGetObj(handle);
chip = h->chip;
if ((chip == NULL) || (chip->magic != LG_GPIO_MAGIC)) return LG_CHIP_NOT_OPEN;
if (gpio >= chip->lines) return LG_BAD_GPIO_NUMBER;
GPIO = &chip->GPIOState[gpio];
if (GPIO->mode == LG_MODE_UNUSED) return LG_GPIO_NOT_ALLOCATED;
status = ioctl(GPIO->fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, GPIO->values);
if (status == 0)
return (GPIO->values[GPIO->offset]);
else
return LG_BAD_READ;
}
int lgGpioWrite(int handle, unsigned gpio, unsigned value)
{
int status;
lgGPIOState_t *GPIO;
GPIOHandle_t *h;
lgChip chip;
LG_DBG(LG_DEBUG_GPIO, "handle=%d gpio=%d value=%d",
handle, gpio, value);
status = lgHdlCheckType(handle, LG_HDL_TYPE_GPIO);
if (status < 0) return status;
h = lgHdlGetObj(handle);
chip = h->chip;
if ((chip == NULL) || (chip->magic != LG_GPIO_MAGIC)) return LG_CHIP_NOT_OPEN;
if (gpio >= chip->lines) return LG_BAD_GPIO_NUMBER;
GPIO = &chip->GPIOState[gpio];
if (GPIO->mode == LG_MODE_UNUSED) return LG_GPIO_NOT_ALLOCATED;
GPIO->values[GPIO->offset]=value;
status = ioctl(GPIO->fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, GPIO->values);
if (status == 0)
return 0;
else
return LG_BAD_WRITE;
}
int lgGpioReadGroup(int handle, unsigned group, unsigned *values)
{
int i;
lgGPIOState_t *GPIO;
int status;
GPIOHandle_t *h;
lgChip chip;
LG_DBG(LG_DEBUG_GPIO, "handle=%d group=%d values=*%p",
handle, group, (void*)values);
status = lgHdlCheckType(handle, LG_HDL_TYPE_GPIO);
if (status < 0) return status;
h = lgHdlGetObj(handle);
chip = h->chip;
if ((chip == NULL) || (chip->magic != LG_GPIO_MAGIC)) return LG_CHIP_NOT_OPEN;
if (group >= chip->lines) return LG_BAD_GPIO_NUMBER;
GPIO = &chip->GPIOState[group];
if (GPIO->mode == LG_MODE_UNUSED) return LG_GPIO_NOT_ALLOCATED;
status = ioctl(GPIO->fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, GPIO->values);
if (status == 0)
{
for (i=0; i<GPIO->group_size; i++) values[i] = GPIO->values[i];
return LG_OKAY;
}
else
return LG_BAD_READ;
}
int lgGpioWriteGroup(int handle, unsigned group, unsigned *values)
{
int status;
int i;
lgGPIOState_t *GPIO;
GPIOHandle_t *h;
lgChip chip;
LG_DBG(LG_DEBUG_GPIO, "handle=%d group=%d values=*%p",
handle, group, (void*)values);
status = lgHdlCheckType(handle, LG_HDL_TYPE_GPIO);
if (status < 0) return status;
h = lgHdlGetObj(handle);
chip = h->chip;
if ((chip == NULL) || (chip->magic != LG_GPIO_MAGIC)) return LG_CHIP_NOT_OPEN;
if (group >= chip->lines) return LG_BAD_GPIO_NUMBER;
GPIO = &chip->GPIOState[group];
if (GPIO->mode == LG_MODE_UNUSED) return LG_GPIO_NOT_ALLOCATED;
for (i=0; i<GPIO->group_size; i++) GPIO->values[i]=values[i];
status = ioctl(GPIO->fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, GPIO->values);
if (status == 0)
return LG_OKAY;
else
return LG_BAD_WRITE;
}
int lgGpioFetchEvents(
int handle,
unsigned num_gpio,
unsigned *gpios,
unsigned max_event,
lgEvent_t *events,
unsigned timeout)
{
int i, e;
int retval;
unsigned count;
unsigned how_many;
int bytes;
struct gpioevent_data evDat[LG_GPIO_MAX_EVENTS_PER_READ];
struct pollfd *pfd;
lgGPIOState_t *GPIO;
int status;
GPIOHandle_t *h;
lgChip chip;
LG_DBG(LG_DEBUG_GPIO,
"handle=%d num_gpio=%d gpios=[%s] max_event=%d events=*%p timeout=%d",
handle, num_gpio, lgDbgInt2Str(num_gpio, (int*)gpios),
max_event, (void*)events, timeout);
status = lgHdlCheckType(handle, LG_HDL_TYPE_GPIO);
if (status < 0) return status;
h = lgHdlGetObj(handle);
chip = h->chip;
if ((chip == NULL) || (chip->magic != LG_GPIO_MAGIC)) return LG_CHIP_NOT_OPEN;
if (num_gpio > chip->lines) return LG_TOO_MANY_GPIOS;
pfd = calloc(num_gpio, sizeof(struct pollfd));
if (pfd == NULL) return LG_NOT_ENOUGH_MEMORY;
for (i=0; i<num_gpio; i++)
{
GPIO = &chip->GPIOState[gpios[i]];
if (GPIO->mode == LG_MODE_UNUSED)
{
free(pfd);
return LG_GPIO_NOT_ALLOCATED;
}
pfd[i].fd = GPIO->fd;
pfd[i].events = POLLIN|POLLPRI;
}
count = 0;
while (count < max_event)
{
retval = poll(pfd, num_gpio, timeout);
if (retval < 0)
{
free(pfd);
if (count == 0) count = LG_POLL_FAILED;
return count;
}
else if (retval == 0)
{
break;
}
for (i=0; i<num_gpio; i++)
{
if (pfd[i].revents)
{
how_many = max_event - count;
if (how_many > LG_GPIO_MAX_EVENTS_PER_READ)
how_many = LG_GPIO_MAX_EVENTS_PER_READ;
bytes = read(pfd[i].fd, &evDat, how_many * sizeof(evDat[0]));
e = 0;
while (bytes >= sizeof(evDat[0]))
{
events[count].timestamp = evDat[e].timestamp;
events[count].edge = evDat[e].id;
events[count].gpio = gpios[i];
if (++count >= max_event) break;
bytes -= sizeof(evDat[0]);
e++;
}
if (bytes)
{
LG_DBG(LG_DEBUG_ALWAYS, "### bytes left=%d (%s)\n",
bytes, strerror(errno));
free(pfd);
if (count == 0) count = LG_POLL_FAILED;
return count;
}
}
}
}
free(pfd);
return count;
}