Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding GPIO interface #1855

Merged
merged 54 commits into from Mar 15, 2023
Merged

Adding GPIO interface #1855

merged 54 commits into from Mar 15, 2023

Conversation

modrisb
Copy link
Contributor

@modrisb modrisb commented Feb 22, 2023

Attempt to add support for UPSes using only open collector interface to report device state by connecting open collector pins to GPIO lines. Pins statuses are transformed to NUT states by configuration rules and is is device independent. Testing done on
CyberPower CyberShield CSN27U12V connected to Orange Pi Zero. Implementation uses libgpiod library to read GPIO lines statuses and does not change any other NUT components except build related.

General points

  • Described the changes in the PR submission or a separate issue, e.g.
    known published or discovered protocols, applicable hardware (expected
    compatible and actually tested/developed against), limitations, etc.

  • There may be multiple commits in the PR, aligned and commented with
    a functional change. Notably, coding style changes better belong in a
    separate PR, but certainly in a dedicated commit to simplify reviews
    of "real" changes in the other commits. Similarly for typo fixes in
    comments or text documents.

Frequent "underwater rocks" for driver addition/update PRs

  • Revised existing driver families and added a sub-driver if applicable
    (nutdrv_qx, usbhid-ups...) or added a brand new driver in the other
    case.

  • Did not extend obsoleted drivers with new hardware support features
    (notably blazer and other single-device family drivers for Qx protocols,
    except the new nutdrv_qx which should cover them all).

  • For updated existing device drivers, bumped the DRIVER_VERSION macro
    or its equivalent.

  • For USB devices (HID or not), revised that the driver uses unique
    VID/PID combinations, or raised discussions when this is not the case
    (several vendors do use same interface chips for unrelated protocols).

  • For new USB devices, built and committed the changes for the
    scripts/upower/95-upower-hid.hwdb file

  • Proposed NUT data mapping is aligned with existing docs/nut-names.txt
    file. If the device exposes useful data points not listed in the file, the
    experimental.* namespace can be used as documented there, and discussion
    should be raised on the NUT Developers mailing list to standardize the new
    concept.

  • Updated data/driver.list.in if applicable (new tested device info)

Frequent "underwater rocks" for general C code PRs

  • Did not "blindly assume" default integer type sizes and value ranges,
    structure layout and alignment in memory, endianness (layout of bytes and
    bits in memory for multi-byte numeric types), or use of generic int where
    language or libraries dictate the use of size_t (or ssize_t sometimes).
  • Progress and errors are handled with upsdebugx(), upslogx(),
    fatalx() and related methods, not with direct printf() or exit().
    Similarly, NUT helpers are used for error-checked memory allocation and
    string operations (except where customized error handling is needed,
    such as unlocking device ports, etc.)

  • Coding style (including whitespace for indentations) follows precedent
    in the code of the file, and examples/guide in docs/developers.txt file.

  • For newly added files, the Makefile.am recipes were updated and the
    make distcheck target passes.

General documentation updates

  • Updated docs/acknowledgements.txt (for vendor-backed device support)

  • Added or updated manual page information in docs/man/*.txt files
    and corresponding recipe lists in docs/man/Makefile.am for new pages

  • Passed make spellcheck, updated spell-checking dictionary in the
    docs/nut.dict file if needed (did not remove any words -- the make
    rule printout in case of changes suggests how to maintain it).

Additional work may be needed after posting this PR

  • Propose a PR for NUT DDL with detailed device data dumps from tests
    against real hardware (the more models, the better).

  • Address NUT CI farm build failures for the PR: testing on numerous
    platforms and toolkits can expose issues not seen on just one system.

  • Revise suggestions from LGTM.COM analysis about "new issues" with
    the changed codebase.

Copy link
Member

@jimklimov jimklimov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, quite a solid contribution - but some changes are desired, as commented in details.

  • GPL headers in sources
  • Make sure ASCIIDOC markup (man page) renders reasonably
  • Complete the configure.ac/m4/Makefile.am integration (and a few bugs in it)
  • Coding style can be made friendlier :)
  • Probably something else here and there...
  • A NEWS file entry makes sense BTW, not just for a new driver but for a new category of drivers.
  • Probably docs/configure.txt also needs a paragraph for the new option.

file (/dev/gpiochip0 for example) to allow the run-time NUT driver user
account to access it, like by adding the following rule to rules.d directory:
SUBSYSTEM=="gpio*", PROGRAM="/bin/sh -c '\
chown -R nut:nut /dev/gpiochip0 && chmod -R 700 /dev/gpiochip0\
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code example should be indented, and possibly preceded by a blank line.

(|)operations are supported for now. nut_state should correspond to NUT
state, line_num to GPIO line number connected to UPS open collector pin.
CyberShield CSN27U12V describes pins as:
ON BATTERY Low when operating from utility line
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think asciidoc table markup (with pipe characters) would do well here, and this may need blank lines around to differentiate from other paragraphs. If possible, build with HTML docs enabled to iterate the result-oriented markup more easily (or use an IDE with an asciidoc plugin to render it real-time as you type) :)

@@ -669,6 +669,17 @@ endif

HTML_LINUX_I2C_MANS = asem.html pijuice.html

SRC_LINUX_GPIO_PAGES = gpio.txt
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just in case: is it right to call this a LINUX_GPIO? The currently used libgpiod "backend" does say it is for linux, but original discussion implied this driver could be more generic with other platforms logically supporting the interface.


NUT_ARG_WITH([gpio], [build and install GPIO driver], [auto])

dnl ${nut_with_gpio}: any value except "yes" or "no" is treated as "auto".
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think somewhere here it should check for libgpiod (potentially other equivalents later).

Typically the check is done as a new method under m4 directory (copy and adapt an existing file), setting the LIBGPIO_CFLAGS, LIBGPIO_LDFLAGS, etc. as appropriate, manually or from pkg-config data where available.

The configure.ac code calls such method, and if things are found and requested mode was "auto" it becomes "yes", otherwise "no". If the request was "yes" but library was not found, it should cause a fatal AC_MSG_ERROR.


# distribute all drivers, even ones that are not built by default
EXTRA_PROGRAMS = $(SERIAL_DRIVERLIST) $(USB_DRIVERLIST) $(SERIAL_USB_DRIVERLIST)
EXTRA_PROGRAMS += $(SNMP_DRIVERLIST) $(NEONXML_DRIVERLIST) $(MACOSX_DRIVERLIST)
EXTRA_PROGRAMS += $(LINUX_I2C_DRIVERLIST)
EXTRA_PROGRAMS += $(NUTSW_DRIVERLIST)
EXTRA_PROGRAMS += $(GPIO_DRIVERLIST)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was supposed to add a new line, not forget about software-only drivers, right? ;)

#define GENERIC_GPIO_COMMON_H

#include <stdlib.h>
#include <gpiod.h>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is technically #if HAVE_GPIOD_H or #if WITH_LIBGPIOD - right?
The file claims to be common, but hardcodes a dependency on (currently the only) one backend here.

@@ -0,0 +1,48 @@
#ifndef GENERIC_GPIO_COMMON_H
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GPL header


typedef struct rulesint_t { /* structure to store processed rules configuration per each state */
char stateName[12]; /* NUT state name for rules in cRules */
int archVal; /* previous state value */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just in case: does this deal with architecture-dependent sizes of int or some wire-protocol specific (u)int32_t etc.? The nut_stdint.h takes care of providing those for different platforms.

* or docs/snmp-subdrivers.txt for SNMP devices
*/
/* ./configure --with-pidpath=/run/nut --with-altpidpath=/run/nut --with-statepath=/run/nut --sysconfdir=/etc/nut --with-gpio --with-user=nut --with-group=nut */
#pragma GCC optimize("O0")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar comments to above - GPL header, this pragma, spaces in clauses (code style/readability).

generic_gpio_libgpiod_SOURCES = generic_gpio_common.c generic_gpio_libgpiod.c
#gpio_CFLAGS = $(AM_CFLAGS)
#gpio_LDADD = $(LDADD_DRIVERS) -lgpiod
generic_gpio_libgpiod_LDADD = $(LDADD_DRIVERS) -lgpiod
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit split on whether explicit -lgpiod belongs here. Consistently, a LIBGPIOD_LIBS as detected by configure.ac and m4 snippet, would be better (especially if some more custom flags come later or on certain platforms).

Also, I think this block belongs lower in the file, among "non-serial drivers" - e.g. near I2C as the added lines neighbor elsewhere.

@jimklimov
Copy link
Member

By the way, does GPIO availability on systemd-aware OSes rely on some certain units? Should nut-driver-enumerator be updated to generate a special dependency for these drivers to start as soon as (but not sooner than) they are able to talk to hardware?

@modrisb
Copy link
Contributor Author

modrisb commented Feb 26, 2023 via email

@modrisb
Copy link
Contributor Author

modrisb commented Feb 26, 2023 via email

@modrisb
Copy link
Contributor Author

modrisb commented Feb 27, 2023 via email

@modrisb
Copy link
Contributor Author

modrisb commented Feb 27, 2023 via email

@jimklimov
Copy link
Member

Confused about latest comment: the nut-server.service.in in NUT sources includes the Restart option and a very different text (more comments, etc.) Is the version you have deployed coming from a recent build, or from NUT 2.7.4 packages by distro?

Also, you should not restart the data server (upsd) for hiccups with a driver. Driver may die, restart and reconnect to upsd.

@jimklimov jimklimov added feature GPIO Drivers talking to devices (or their chips) over GPIO interface labels Feb 28, 2023
@modrisb
Copy link
Contributor Author

modrisb commented Feb 28, 2023 via email

@modrisb
Copy link
Contributor Author

modrisb commented Mar 2, 2023 via email

@jimklimov
Copy link
Member

Unfortunately so far tests are rather nascent. There are a few one-off test programs for some aspects and libraries, but no sturdy framework to rule them all. Check under tests/ dir to see if any precedent makes sense for your test code or add another :)

@modrisb
Copy link
Contributor Author

modrisb commented Mar 13, 2023 via email

… out-of-tree builds the generic_gpio_test.txt resource file
@jimklimov
Copy link
Member

Well, actually this needed a lot of chizeling around the rough edges to work and not spew warnings, pass distcheck, etc. ;)

Normally C sources are built standalone and then linked together into a binary, not included directly - more so for testing of what should be library code. Was there a particular need to use much of drivers/main.c, and the dstate.c directly in the test, as if part of its source, rather than via linking? Was it due to testing of rules support?

I think something better is possible along the lines of drivers/Makefile.am:libdummy_la_SOURCES = main.c dstate.c, perhaps instrumenting drivers/main.c to wrap main() into some #ifndef MAIN_WITHOUT_MAIN sort of flag to build an object file that has almost everything.

…_gpio_common.c generic_gpio_liblocal.c generic_gpio_libgpiod.c" in test code
@jimklimov
Copy link
Member

At least on one system, these recent commits got rid of unidiomatic #include "somefile.c" and passed for in-tree and out-of-tree (distcheck) builds. Hoping CI likes it :)

@modrisb
Copy link
Contributor Author

modrisb commented Mar 13, 2023 via email

@modrisb
Copy link
Contributor Author

modrisb commented Mar 13, 2023 via email

@modrisb
Copy link
Contributor Author

modrisb commented Mar 13, 2023 via email

@jimklimov
Copy link
Member

jimklimov commented Mar 13, 2023

Take a look in the recent commits' proposal of ifdef DRIVERS_MAIN_WITHOUT_MAIN - I got to a similar conclusion about static so use of the keyword became conditional, depending on build circumstances. This macro is defined for the test-program builds, and so these test builds declare the methods and variables non-static (and the new helper library exposes them for the linker).

The Makefile changes, now explicitly referring to sources that were previously included, take care of building the test once per change of any source involved (previously this was not guaranteed - only that a change of timestamp of generic_gpio_utest.c would trigger rebuild of the test program) as well as building the same source into different object files with different settings (with the macro for test, without it for driver).

And indeed, having the libdummy_mockdrv.la now allows easier creation of more test programs that behave similarly to a real driver. This is a temporary library, not to be installed or packaged, just needed to compile those things once even if used by many tests in the future (similarly to how libcommon.la is linked into almost every program).

@modrisb
Copy link
Contributor Author

modrisb commented Mar 13, 2023 via email

@modrisb
Copy link
Contributor Author

modrisb commented Mar 13, 2023 via email

@jimklimov jimklimov changed the title Ading GPIO interface Adding GPIO interface Mar 14, 2023
@jimklimov jimklimov merged commit 72d3253 into networkupstools:master Mar 15, 2023
2 checks passed
@jimklimov
Copy link
Member

@modrisb : Thank you very much for this improvement! :)

@modrisb
Copy link
Contributor Author

modrisb commented Mar 15, 2023 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature GPIO Drivers talking to devices (or their chips) over GPIO interface
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants