"""
[http://abyz.me.uk/lg/py_rgpio.html]

rgpio is a Python module which allows remote control of the GPIO
and other functions of Linux SBCs running the rgpiod daemon.

The rgpiod daemon must be running on the SBCs you wish to control.

*Features*

o the rgpio Python module can run on Windows, Macs, or Linux
o controls one or more SBCs
o reading and writing GPIO singly and in groups
o software timed PWM and waves
o GPIO callbacks
o pipe notification of GPIO alerts
o I2C wrapper
o SPI wrapper
o serial link wrapper
o simple file handling
o creating and running scripts on the rgpiod daemon

*Exceptions*

By default a fatal exception is raised if you pass an invalid
argument to a rgpio function.

If you wish to handle the returned status yourself you should set
rgpio.exceptions to False.

You may prefer to check the returned status in only a few parts
of your code.  In that case do the following:

...
rgpio.exceptions = False

# Code where you want to test the error status.

rgpio.exceptions = True
...

*Usage*

The rgpiod daemon must be running on the SBCs whose GPIO
are to be manipulated.

The normal way to start rgpiod is during system start.

rgpiod &

Your Python program must import rgpio and create one or more
instances of the rgpio.sbc class.  This class gives access to
the specified SBC's GPIO.

...
sbc1 = rgpio.sbc()       # sbc1 accesses the local SBC's GPIO
sbc2 = rgpio.sbc('tom')  # sbc2 accesses tom's GPIO
sbc3 = rgpio.sbc('dick') # sbc3 accesses dick's GPIO
...

The later example code snippets assume that sbc is an instance of
the rgpio.sbc class.

*Licence*

This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to <http://unlicense.org/>

OVERVIEW

ESSENTIAL

rgpio.sbc                 Initialise sbc connection
stop                      Stop a sbc connection

FILES

file_open                 Opens a file
file_close                Closes a file

file_read                 Reads bytes from a file
file_write                Writes bytes to a file

file_seek                 Seeks to a position within a file

file_list                 List files which match a pattern

GPIO

gpiochip_open             Opens a gpiochip device
gpiochip_close            Closes a gpiochip device

gpio_get_chip_info        Get information about a gpiochip
gpio_get_line_info        Get information about a gpiochip line

gpio_get_mode             Gets the mode of a GPIO

gpio_claim_input          Claims a GPIO for input
gpio_claim_output         Claims a GPIO for output
gpio_claim_alert          Claims a GPIO for alerts
gpio_free                 Frees a GPIO

group_claim_input         Claims a group of GPIO for inputs
group_claim_output        Claims a group of GPIO for outputs
group_free                Frees a group of GPIO

gpio_read                 Reads a GPIO
gpio_write                Writes a GPIO

group_read                Reads a group of GPIO
group_write               Writes a group of GPIO

tx_pulse                  Starts pulses on a GPIO
tx_pwm                    Starts PWM on a GPIO
tx_servo                  Starts servo pulses on a GPIO
tx_wave                   Starts a wave on a group of GPIO
tx_busy                   See if tx is active on a GPIO or group
tx_room                   See if more room for tx on a GPIO or group

gpio_set_debounce_micros  Sets the debounce time for a GPIO
gpio_set_watchdog_micros  Sets the watchdog time for a GPIO

callback                  Starts a GPIO callback

I2C

i2c_open                  Opens an I2C device
i2c_close                 Closes an I2C device

i2c_write_quick           SMBus write quick

i2c_read_byte             SMBus read byte
i2c_write_byte            SMBus write byte

i2c_read_byte_data        SMBus read byte data
i2c_write_byte_data       SMBus write byte data

i2c_read_word_data        SMBus read word data
i2c_write_word_data       SMBus write word data

i2c_read_block_data       SMBus read block data
i2c_write_block_data      SMBus write block data

i2c_read_i2c_block_data   SMBus read I2C block data
i2c_write_i2c_block_data  SMBus write I2C block data

i2c_read_device           Reads the raw I2C device
i2c_write_device          Writes the raw I2C device

i2c_process_call          SMBus process call
i2c_block_process_call    SMBus block process call

i2c_zip                   Performs multiple I2C transactions

NOTIFICATIONS

notify_open               Request a notification handle
notify_close              Close a notification
notify_pause              Pause notifications
notify_resume             Resume notifications

SCRIPTS

script_store              Store a script
script_run                Run a stored script
script_update             Set a scripts parameters
script_status             Get script status and parameters
script_stop               Stop a running script
script_delete             Delete a stored script

SERIAL

serial_open               Opens a serial device
serial_close              Closes a serial device

serial_read_byte          Reads a byte from a serial device
serial_write_byte         Writes a byte to a serial device

serial_read               Reads bytes from a serial device
serial_write              Writes bytes to a serial device

serial_data_available     Returns number of bytes ready to be read

SHELL

shell                     Executes a shell command

SPI

spi_open                  Opens a SPI device
spi_close                 Closes a SPI device

spi_read                  Reads bytes from a SPI device
spi_write                 Writes bytes to a SPI device
spi_xfer                  Transfers bytes with a SPI device

UTILITIES

get_sbc_name              Get the SBC name

get_internal              Get an SBC configuration value
set_internal              Set an SBC configuration value

set_user                  Set the user (and associated permissions)
set_share_id              Set the share id for a resource
use_share_id              Use this share id when asking for a resource

rgpio.get_module_version  Get the rgpio Python module version
rgpio.error_text          Get the error text for an error code
"""

import sys
import socket
import struct
import time
import threading
import os
import atexit
import hashlib

RGPIO_PY_VERSION = 0x00000000

exceptions = True

MAGIC=1818715245

# GPIO levels

OFF = 0
LOW = 0
CLEAR = 0

ON = 1
HIGH = 1
SET = 1

TIMEOUT = 2

GROUP_ALL = 0xffffffffffffffff

# GPIO line flags

SET_ACTIVE_LOW = 4
SET_OPEN_DRAIN = 8
SET_OPEN_SOURCE = 16

# GPIO event flags

RISING_EDGE = 1
FALLING_EDGE = 2
BOTH_EDGES = 3

# tx constants

TX_PWM = 0
TX_WAVE = 1

# script run status

SCRIPT_INITING = 0
SCRIPT_READY = 1
SCRIPT_RUNNING = 2
SCRIPT_WAITING = 3
SCRIPT_ENDED = 4
SCRIPT_HALTED = 5
SCRIPT_FAILED = 6

# notification flags

NTFY_FLAGS_ALIVE = (1 << 0)

FILE_READ = 1
FILE_WRITE = 2
FILE_RW = 3

FILE_APPEND = 4
FILE_CREATE = 8
FILE_TRUNC = 16

FROM_START = 0
FROM_CURRENT = 1
FROM_END = 2

SPI_MODE_0 = 0
SPI_MODE_1 = 1
SPI_MODE_2 = 2
SPI_MODE_3 = 3

_SOCK_CMD_LEN = 16

# rgpiod command numbers

_CMD_FO = 1
_CMD_FC = 2
_CMD_FR = 3
_CMD_FW = 4
_CMD_FS = 5
_CMD_FL = 6
_CMD_GO = 10
_CMD_GC = 11
_CMD_GSIX = 12
_CMD_GSOX = 13
_CMD_GSAX = 14
_CMD_GSF = 15
_CMD_GSGIX = 16
_CMD_GSGOX = 17
_CMD_GSGF = 18
_CMD_GR = 19
_CMD_GW = 20
_CMD_GGR = 21
_CMD_GGWX = 22
_CMD_GPX = 23
_CMD_PX = 24
_CMD_SX = 25
_CMD_GWAVE = 26
_CMD_GBUSY = 27
_CMD_GROOM = 28
_CMD_GDEB = 29
_CMD_GWDOG = 30
_CMD_GIC = 31
_CMD_GIL = 32
_CMD_GMODE = 33
_CMD_I2CO = 40
_CMD_I2CC = 41
_CMD_I2CRD = 42
_CMD_I2CWD = 43
_CMD_I2CWQ = 44
_CMD_I2CRS = 45
_CMD_I2CWS = 46
_CMD_I2CRB = 47
_CMD_I2CWB = 48
_CMD_I2CRW = 49
_CMD_I2CWW = 50
_CMD_I2CRK = 51
_CMD_I2CWK = 52
_CMD_I2CRI = 53
_CMD_I2CWI = 54
_CMD_I2CPC = 55
_CMD_I2CPK = 56
_CMD_I2CZ = 57
_CMD_NO = 70
_CMD_NC = 71
_CMD_NR = 72
_CMD_NP = 73
_CMD_PARSE = 80
_CMD_PROC = 81
_CMD_PROCD = 82
_CMD_PROCP = 83
_CMD_PROCR = 84
_CMD_PROCS = 85
_CMD_PROCU = 86
_CMD_SERO = 90
_CMD_SERC = 91
_CMD_SERRB = 92
_CMD_SERWB = 93
_CMD_SERR = 94
_CMD_SERW = 95
_CMD_SERDA = 96
_CMD_SPIO = 100
_CMD_SPIC = 101
_CMD_SPIR = 102
_CMD_SPIW = 103
_CMD_SPIX = 104
_CMD_MICS = 113
_CMD_MILS = 114
_CMD_CGI = 115
_CMD_CSI = 116
_CMD_NOIB = 117
_CMD_SHELL = 118
_CMD_SBC = 120
_CMD_FREE = 121
_CMD_SHARE = 130
_CMD_USER = 131
_CMD_PASSW = 132
_CMD_LCFG = 133
_CMD_SHRU = 134
_CMD_SHRS = 135
_CMD_PWD = 136
_CMD_PCD = 137
_CMD_LGV = 140
_CMD_TICK = 141

# rgpiod error numbers

OKAY = 0
INIT_FAILED = -1
BAD_MICROS = -2
BAD_PATHNAME = -3
NO_HANDLE = -4
BAD_HANDLE = -5
BAD_SOCKET_PORT = -6
NOT_PERMITTED = -7
SOME_PERMITTED = -8
BAD_SCRIPT = -9
BAD_TX_TYPE = -10
GPIO_IN_USE = -11
BAD_PARAM_NUM = -12
DUP_TAG = -13
TOO_MANY_TAGS = -14
BAD_SCRIPT_CMD = -15
BAD_VAR_NUM = -16
NO_SCRIPT_ROOM = -17
NO_MEMORY = -18
SOCK_READ_FAILED = -19
SOCK_WRIT_FAILED = -20
TOO_MANY_PARAM = -21
SCRIPT_NOT_READY = -22
BAD_TAG = -23
BAD_MICS_DELAY = -24
BAD_MILS_DELAY = -25
I2C_OPEN_FAILED = -26
SERIAL_OPEN_FAILED = -27
SPI_OPEN_FAILED = -28
BAD_I2C_BUS = -29
BAD_I2C_ADDR = -30
BAD_SPI_CHANNEL = -31
BAD_I2C_FLAGS = -32
BAD_SPI_FLAGS = -33
BAD_SERIAL_FLAGS = -34
BAD_SPI_SPEED = -35
BAD_SERIAL_DEVICE = -36
BAD_SERIAL_SPEED = -37
BAD_FILE_PARAM = -38
BAD_I2C_PARAM = -39
BAD_SERIAL_PARAM = -40
I2C_WRITE_FAILED = -41
I2C_READ_FAILED = -42
BAD_SPI_COUNT = -43
SERIAL_WRITE_FAILED = -44
SERIAL_READ_FAILED = -45
SERIAL_READ_NO_DATA = -46
UNKNOWN_COMMAND = -47
SPI_XFER_FAILED = -48
BAD_POINTER = -49
MSG_TOOBIG = -50
BAD_MALLOC_MODE = -51
TOO_MANY_SEGS = -52
BAD_I2C_SEG = -53
BAD_SMBUS_CMD = -54
BAD_I2C_WLEN = -55
BAD_I2C_RLEN = -56
BAD_I2C_CMD = -57
FILE_OPEN_FAILED = -58
BAD_FILE_MODE = -59
BAD_FILE_FLAG = -60
BAD_FILE_READ = -61
BAD_FILE_WRITE = -62
FILE_NOT_ROPEN = -63
FILE_NOT_WOPEN = -64
BAD_FILE_SEEK = -65
NO_FILE_MATCH = -66
NO_FILE_ACCESS = -67
FILE_IS_A_DIR = -68
BAD_SHELL_STATUS = -69
BAD_SCRIPT_NAME = -70
CMD_INTERRUPTED = -71
BAD_EVENT_REQUEST = -72
BAD_GPIO_NUMBER = -73
BAD_GROUP_SIZE = -74
BAD_LINEINFO_IOCTL = -75
BAD_READ = -76
BAD_WRITE = -77
CANNOT_OPEN_CHIP = -78
GPIO_BUSY = -79
GPIO_NOT_ALLOCATED = -80
NOT_A_GPIOCHIP = -81
NOT_ENOUGH_MEMORY = -82
POLL_FAILED = -83
TOO_MANY_GPIOS = -84
UNEGPECTED_ERROR = -85
BAD_PWM_MICROS = -86
NOT_GROUP_LEADER = -87
SPI_IOCTL_FAILED = -88
BAD_GPIOCHIP = -89
BAD_CHIPINFO_IOCTL = -90
BAD_CONFIG_FILE = -91
BAD_CONFIG_VALUE = -92
NO_PERMISSIONS = -93
BAD_USERNAME = -94
BAD_SECRET = -95
TX_QUEUE_FULL = -96
BAD_CONFIG_ID = -97
BAD_DEBOUNCE_MICS = -98
BAD_WATCHDOG_MICS = -99
BAD_SERVO_FREQ = -100
BAD_SERVO_WIDTH = -101
BAD_PWM_FREQ = -102
BAD_PWM_DUTY = -103
GPIO_NOT_AN_OUTPUT = -104
INVALID_GROUP_ALERT = -105

# rgpiod error text

_errors=[
   [OKAY,  "No error"],
   [INIT_FAILED,  "initialisation failed"],
   [BAD_MICROS,  "micros not 0-999999"],
   [BAD_PATHNAME,  "can not open pathname"],
   [NO_HANDLE,  "no handle available"],
   [BAD_HANDLE,  "unknown handle"],
   [BAD_SOCKET_PORT,  "socket port not 1024-32000"],
   [NOT_PERMITTED,  "GPIO operation not permitted"],
   [SOME_PERMITTED,  "one or more GPIO not permitted"],
   [BAD_SCRIPT,  "invalid script"],
   [BAD_TX_TYPE,  "bad tx type for GPIO and group"],
   [GPIO_IN_USE,  "GPIO already in use"],
   [BAD_PARAM_NUM,  "script parameter id not 0-9"],
   [DUP_TAG,  "script has duplicate tag"],
   [TOO_MANY_TAGS,  "script has too many tags"],
   [BAD_SCRIPT_CMD,  "illegal script command"],
   [BAD_VAR_NUM,  "script variable id not 0-149"],
   [NO_SCRIPT_ROOM,  "no more room for scripts"],
   [NO_MEMORY,  "can not allocate temporary memory"],
   [SOCK_READ_FAILED,  "socket read failed"],
   [SOCK_WRIT_FAILED,  "socket write failed"],
   [TOO_MANY_PARAM,  "too many script parameters (> 10)"],
   [SCRIPT_NOT_READY,  "script initialising"],
   [BAD_TAG,  "script has unresolved tag"],
   [BAD_MICS_DELAY,  "bad MICS delay (too large)"],
   [BAD_MILS_DELAY,  "bad MILS delay (too large)"],
   [I2C_OPEN_FAILED,  "can not open I2C device"],
   [SERIAL_OPEN_FAILED,  "can not open serial device"],
   [SPI_OPEN_FAILED,  "can not open SPI device"],
   [BAD_I2C_BUS,  "bad I2C bus"],
   [BAD_I2C_ADDR,  "bad I2C address"],
   [BAD_SPI_CHANNEL,  "bad SPI channel"],
   [BAD_I2C_FLAGS,  "bad I2C open flags"],
   [BAD_SPI_FLAGS,  "bad SPI open flags"],
   [BAD_SERIAL_FLAGS,  "bad serial open flags"],
   [BAD_SPI_SPEED,  "bad SPI speed"],
   [BAD_SERIAL_DEVICE,  "bad serial device name"],
   [BAD_SERIAL_SPEED,  "bad serial baud rate"],
   [BAD_FILE_PARAM,  "bad file parameter"],
   [BAD_I2C_PARAM,  "bad I2C parameter"],
   [BAD_SERIAL_PARAM,  "bad serial parameter"],
   [I2C_WRITE_FAILED,  "i2c write failed"],
   [I2C_READ_FAILED,  "i2c read failed"],
   [BAD_SPI_COUNT,  "bad SPI count"],
   [SERIAL_WRITE_FAILED,  "ser write failed"],
   [SERIAL_READ_FAILED,  "ser read failed"],
   [SERIAL_READ_NO_DATA,  "ser read no data available"],
   [UNKNOWN_COMMAND,  "unknown command"],
   [SPI_XFER_FAILED,  "spi xfer/read/write failed"],
   [BAD_POINTER,  "bad (NULL) pointer"],
   [MSG_TOOBIG,  "socket/pipe message too big"],
   [BAD_MALLOC_MODE,  "bad memory allocation mode"],
   [TOO_MANY_SEGS,  "too many I2C transaction segments"],
   [BAD_I2C_SEG,  "an I2C transaction segment failed"],
   [BAD_SMBUS_CMD,  "SMBus command not supported by driver"],
   [BAD_I2C_WLEN,  "bad I2C write length"],
   [BAD_I2C_RLEN,  "bad I2C read length"],
   [BAD_I2C_CMD,  "bad I2C command"],
   [FILE_OPEN_FAILED,  "file open failed"],
   [BAD_FILE_MODE,  "bad file mode"],
   [BAD_FILE_FLAG,  "bad file flag"],
   [BAD_FILE_READ,  "bad file read"],
   [BAD_FILE_WRITE,  "bad file write"],
   [FILE_NOT_ROPEN,  "file not open for read"],
   [FILE_NOT_WOPEN,  "file not open for write"],
   [BAD_FILE_SEEK,  "bad file seek"],
   [NO_FILE_MATCH,  "no files match pattern"],
   [NO_FILE_ACCESS,  "no permission to access file"],
   [FILE_IS_A_DIR,  "file is a directory"],
   [BAD_SHELL_STATUS,  "bad shell return status"],
   [BAD_SCRIPT_NAME,  "bad script name"],
   [CMD_INTERRUPTED,  "Python socket command interrupted"],
   [BAD_EVENT_REQUEST,  "bad event request"],
   [BAD_GPIO_NUMBER,  "bad GPIO number"],
   [BAD_GROUP_SIZE,  "bad group size"],
   [BAD_LINEINFO_IOCTL,  "bad lineinfo IOCTL"],
   [BAD_READ,  "bad GPIO read"],
   [BAD_WRITE,  "bad GPIO write"],
   [CANNOT_OPEN_CHIP,  "can not open gpiochip"],
   [GPIO_BUSY,  "GPIO busy"],
   [GPIO_NOT_ALLOCATED,  "GPIO not allocated"],
   [NOT_A_GPIOCHIP,  "not a gpiochip"],
   [NOT_ENOUGH_MEMORY,  "not enough memory"],
   [POLL_FAILED,  "GPIO poll failed"],
   [TOO_MANY_GPIOS,  "too many GPIO"],
   [UNEGPECTED_ERROR,  "unexpected error"],
   [BAD_PWM_MICROS,  "bad PWM micros"],
   [NOT_GROUP_LEADER,  "GPIO not the group leader"],
   [SPI_IOCTL_FAILED,  "SPI iOCTL failed"],
   [BAD_GPIOCHIP,  "bad gpiochip"],
   [BAD_CHIPINFO_IOCTL,  "bad chipinfo IOCTL"],
   [BAD_CONFIG_FILE,  "bad configuration file"],
   [BAD_CONFIG_VALUE,  "bad configuration value"],
   [NO_PERMISSIONS,  "no permission to perform action"],
   [BAD_USERNAME,  "bad user name"],
   [BAD_SECRET,  "bad secret for user"],
   [TX_QUEUE_FULL,  "TX queue full"],
   [BAD_CONFIG_ID,  "bad configuration id"],
   [BAD_DEBOUNCE_MICS,  "bad debounce microseconds"],
   [BAD_WATCHDOG_MICS,  "bad watchdog microseconds"],
   [BAD_SERVO_FREQ,  "bad servo frequency"],
   [BAD_SERVO_WIDTH,  "bad servo pulsewidth"],
   [BAD_PWM_FREQ,  "bad PWM frequency"],
   [BAD_PWM_DUTY,  "bad PWM dutycycle"],
   [GPIO_NOT_AN_OUTPUT,  "GPIO not set as an output"],
   [INVALID_GROUP_ALERT,  "can not set a group to alert"],
]

_except_a = "############################################################\n{}"

_except_z = "############################################################"

_except_1 = """
Did you start the rgpiod daemon? E.g. rgpiod &

Did you specify the correct host/port in the environment
variables LG_ADDR and/or LG_PORT?
E.g. export LG_ADDR=soft, export LG_PORT=8889

Did you specify the correct host/port in the
rgpio.sbc() function? E.g. rgpio.sbc('soft', 8889)"""

_except_2 = """
Do you have permission to access the rgpiod daemon?
Perhaps it was started with lgd -nlocalhost"""

_except_3 = """
Can't create callback thread.
Perhaps too many simultaneous rgpiod connections."""

class _socklock:
   """
   A class to store socket and lock.
   """
   def __init__(self):
      self.s = None
      self.l = threading.Lock()

class error(Exception):
   """
   rgpio module exception
   """
   def __init__(self, value):
      self.value = value
   def __str__(self):
      return repr(self.value)

class pulse:
   """
   A class to store pulse information.
   """

   def __init__(self, group_bits, group_mask, pulse_delay):
      """
      Initialises a pulse.

      group_bits:= the levels to set if the corresponding bit in
                   group_mask is set.
      group_mask:= a mask indicating the group GPIO to be updated.
           delay:= the delay in microseconds before the next pulse.
      """
      self.group_bits = group_bits
      self.group_mask = group_mask
      self.pulse_delay = pulse_delay


# A couple of hacks to cope with different string handling
# between various Python versions
# 3 != 2.7.8 != 2.7.3

if sys.hexversion < 0x03000000:
   def _b(x):
      return x
else:
   def _b(x):
      return x.encode('latin-1')

if sys.hexversion < 0x02070800:
   def _str(x):
      return buffer(x)
else:
   def _str(x):
      return x

def u2i(uint32):
   """
   Converts a 32 bit unsigned number to signed.

   uint32:= an unsigned 32 bit number

   Returns a signed number.

   ...
   print(u2i(4294967272))
   -24
   print(u2i(37))
   37
   ...
   """
   mask = (2 ** 32) - 1
   if uint32 & (1 << 31):
      v = uint32 | ~mask
   else:
      v = uint32 & mask
   return v

def _u2i(status):
   """
   If the status is negative it indicates an error.  On error
   a rgpio exception will be raised if exceptions is True.
   """
   v = u2i(status)
   if v < 0:
      if exceptions:
         raise error(error_text(v))
   return v

def _u2i_list(lst):
   """
   Checks a returned list.  The first member is the status.
   If the status is negative it indicates an error.  On error
   a rgpio exception will be raised if exceptions is True.
   """
   lst[0] = u2i(lst[0])
   if lst[0] < 0:
      if exceptions:
         raise error(error_text(lst[0]))
   return lst

def _lg_command(sl, cmd, Q=0, L=0, H=0):
   """
   """
   status = CMD_INTERRUPTED
   with sl.l:
      sl.s.send(struct.pack('IIHHHH', MAGIC, 0, cmd, Q, L, H))
      status, dummy = struct.unpack('I12s', sl.s.recv(_SOCK_CMD_LEN))
   return status

def _lg_command_nolock(sl, cmd, Q=0, L=0, H=0):
   """
   """
   status = CMD_INTERRUPTED
   sl.s.send(struct.pack('IIHHHH', MAGIC, 0, cmd, Q, L, H))
   status, dummy = struct.unpack('I12s', sl.s.recv(_SOCK_CMD_LEN))
   return status

def _lg_command_ext(sl, cmd, p3, extents, Q=0, L=0, H=0):
   """
   """
   ext = bytearray(struct.pack('IIHHHH', MAGIC, p3, cmd, Q, L, H))
   for x in extents:
      if type(x) == type(""):
         ext.extend(_b(x))
      else:
         ext.extend(x)
   status = CMD_INTERRUPTED
   with sl.l:
      sl.s.sendall(ext)
      status, dummy = struct.unpack('I12s', sl.s.recv(_SOCK_CMD_LEN))
   return status

def _lg_command_ext_nolock(sl, cmd, p3, extents, Q=0, L=0, H=0):
   """
   """
   status = CMD_INTERRUPTED
   ext = bytearray(struct.pack('IIHHHH', MAGIC, p3, cmd, Q, L, H))
   for x in extents:
      if type(x) == type(""):
         ext.extend(_b(x))
      else:
         ext.extend(x)
   sl.s.sendall(ext)
   status, dummy = struct.unpack('I12s', sl.s.recv(_SOCK_CMD_LEN))
   return status

class _callback_ADT:
   """
   An ADT class to hold callback information.
   """

   def __init__(self, gpiochip, gpio, edge, func):
      """
      Initialises a callback ADT.

      gpiochip:= gpiochip device number.
          gpio:= gpio number in device.
          edge:= BOTH_EDGES, RISING_EDGE, or FALLING_EDGE.
          func:= a user function taking four arguments
                 (gpiochip, gpio, level, tick).
      """
      self.chip = gpiochip
      self.gpio = gpio
      self.edge = edge
      self.func = func

class _callback_thread(threading.Thread):
   """
   A class to encapsulate rgpio notification callbacks.
   """

   def __init__(self, control, host, port):
      """
      Initialises notifications.
      """
      threading.Thread.__init__(self)
      self.control = control
      self.sl = _socklock()
      self.go = False
      self.daemon = True
      self.monitor = 0
      self.callbacks = []
      self.sl.s = socket.create_connection((host, port), None)
      self.lastLevel = 0
      self.handle = _u2i(_lg_command(self.sl, _CMD_NOIB))
      self.go = True
      self.start()

   def stop(self):
      """
      Stops notifications.
      """
      if self.go:
         self.go = False
         ext = [struct.pack("I", self.handle)]
         _lg_command_ext(self.control, _CMD_NC, 4, ext, L=1)

   def append(self, callb):
      """
      Adds a callback to the notification thread.
      """
      self.callbacks.append(callb)

   def remove(self, callb):
      """
      Removes a callback from the notification thread.
      """
      if callb in self.callbacks:
         self.callbacks.remove(callb)

   def run(self):
      """
      Runs the notification thread.
      """

      lastLevel = self.lastLevel
      RECV_SIZ = 4096
      MSG_SIZ = 16 # 4 bytes of padding in each message

      buf = bytes()
      while self.go:

         buf += self.sl.s.recv(RECV_SIZ)
         offset = 0

         while self.go and (len(buf) - offset) >= MSG_SIZ:
            msgbuf = buf[offset:offset + MSG_SIZ]
            offset += MSG_SIZ
            tick, chip, gpio, level, flags, pad = (
               struct.unpack('QBBBBI', msgbuf))

            if flags == 0:
               for cb in self.callbacks:
                  if cb.gpio == gpio:
                     cb.func(chip, gpio, level, tick)
            else: # no flags currently defined, ignore.
               pass

         buf = buf[offset:]

      self.sl.s.close()

class _callback:
   """
   A class to provide GPIO level change callbacks.
   """

   def __init__(self, notify, chip, gpio, edge=RISING_EDGE, func=None):
      """
      Initialise a callback and adds it to the notification thread.
      """
      self._notify = notify
      self.count=0
      self._reset = False
      if func is None:
         func=self._tally
      self.callb = _callback_ADT(chip, gpio, edge, func)
      self._notify.append(self.callb)

   def cancel(self):
      """
      Cancels a callback by removing it from the notification thread.
      """
      self._notify.remove(self.callb)

   def _tally(self, chip, gpio, level, tick):
      """
      Increment the callback called count.
      """
      if self._reset:
         self._reset = False
         self.count = 0
      self.count += 1

   def tally(self):
      """
      Provides a count of how many times the default tally
      callback has triggered.

      The count will be zero if the user has supplied their own
      callback function.
      """
      return self.count

   def reset_tally(self):
      """
      Resets the tally count to zero.
      """
      self._reset = True
      self.count = 0

def error_text(errnum):
   """
   Returns a description of an error number.

   errnum:= <0, the error number

   ...
   print(rgpio.error_text(-5))
   level not 0-1
   ...
   """
   for e in _errors:
      if e[0] == errnum:
         return e[1]
   return "unknown error"

def get_module_version():
   """
   Returns the version number of the rgpio Python module as a dotted
   quad.

   A.B.C.D
   
   A. API major version, changed if breaks previous API 
   B. API minor version, changed when new function added 
   C. bug fix 
   D. documentation changegi
   """
   return "rgpio.py_{}.{}.{}.{}".format(
      (RGPIO_PY_VERSION>>24)&0xff, (RGPIO_PY_VERSION>>16)&0xff,
      (RGPIO_PY_VERSION>>8)&0xff, RGPIO_PY_VERSION&0xff)

class sbc():

   def _rxbuf(self, count):
      """
      Returns count bytes from the command socket.
      """
      ext = bytearray(self.sl.s.recv(count))
      while len(ext) < count:
         ext.extend(self.sl.s.recv(count - len(ext)))
      return ext

   def __repr__(self):
      """
      Returns details of the sbc connection.
      """
      return "<rgpio.sbc host={} port={}>".format(self._host, self._port)

   # ESSENTIAL

   def __init__(self,
                host = os.getenv("LG_ADDR", 'localhost'),
                port = os.getenv("LG_PORT", 8889),
                show_errors = True):
      """
      Establishes a connection to the rgpiod daemon running on a SBC.

      host:= the host name of the SBC on which the rgpiod daemon is
             running.  The default is localhost unless overridden by
             the ADDR environment variable.
      port:= the port number on which the rgpiod daemon is listening.
             The default is 8889 unless overridden by the PORT
             environment variable.  The rgpiod daemon must have been
             started with the same port number.

      This connects to the rgpiod daemon and reserves resources
      to be used for sending commands and receiving notifications.

      An instance attribute [*connected*] may be used to check the
      success of the connection.  If the connection is established
      successfully [*connected*] will be True, otherwise False.

      ...
      sbc = rgpio.sbc()             # use defaults
      sbc = rgpio.sbc('mypi')       # specify host, default port
      sbc = rgpio.sbc('mypi', 7777) # specify host and port

      sbc = rgpio.sbc()             # exit script if no connection
      if not sbc.connected:
         exit()
      ...
      """
      self.connected = True

      self.sl = _socklock()
      self._notify  = None

      port = int(port)

      if host == '':
         host = "localhost"

      self._host = host
      self._port = port

      try:
         self.sl.s = socket.create_connection((host, port), None)

         # Disable the Nagle algorithm.
         self.sl.s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)

         self._notify = _callback_thread(self.sl, host, port)

      except socket.error:
         exception = 1

      except struct.error:
         exception = 2

      except error:
         # assumed to be no handle available
         exception = 3

      else:
         exception = 0
         atexit.register(self.stop)

      if exception != 0:

         self.connected = False

         if self.sl.s is not None:
            self.sl.s = None

         if show_errors:

            s = "Can't connect to rgpiod at {}({})".format(host, str(port))


            print(_except_a.format(s))
            if exception == 1:
                print(_except_1)
            elif exception == 2:
                print(_except_2)
            else:
                print(_except_3)
            print(_except_z)

   def stop(self):
      """
      Disconnects from the rgpiod daemon and releases any used resources.

      ...
      sbc.stop()
      ...
      """

      self.connected = False

      if self._notify is not None:
         self._notify.stop()
         self._notify = None

      if self.sl.s is not None:
         # Free all resources allocated to this connection
         _lg_command(self.sl, _CMD_FREE)
         self.sl.s.close()
         self.sl.s = None

   # FILES

   def file_open(self, file_name, file_mode):
      """
      This function returns a handle to a file opened in a specified mode.

      This is a privileged command. See [+Permits+].

      file_name:= the file to open.
      file_mode:= the file open mode.

      If OK returns a handle (>= 0).

      On failure returns a negative error code.

      Mode

      The mode may have the following values:

      Constant   @ Value @ Meaning
      FILE_READ  @   1   @ open file for reading
      FILE_WRITE @   2   @ open file for writing
      FILE_RW    @   3   @ open file for reading and writing

      The following values may be or'd into the mode:

      Name        @ Value @ Meaning
      FILE_APPEND @ 4     @ All writes append data to the end of the file
      FILE_CREATE @ 8     @ The file is created if it doesn't exist
      FILE_TRUNC  @ 16    @ The file is truncated

      Newly created files are owned by the user who launched the daemon.
      They will have permissions owner read and write.

      ...
      #!/usr/bin/env python

      import rgpio

      sbc = rgpio.sbc()

      if not sbc.connected:
         exit()

      handle = sbc.file_open("/ram/lg.c", rgpio.FILE_READ)

      done = False

      while not done:
         c, d = sbc.file_read(handle, 60000)
         if c > 0:
            print(d)
         else:
            done = True

      sbc.file_close(handle)

      sbc.stop()
      ...
      """
      ext = [struct.pack("I", file_mode)] + [file_name]
      return _u2i(_lg_command_ext(
         self.sl, _CMD_FO, 4+len(file_name), ext, L=1))

   def file_close(self, handle):
      """
      Closes a file.

      handle:= >= 0 (as returned by [*file_open*]).

      If OK returns 0.

      On failure returns a negative error code.

      ...
      sbc.file_close(handle)
      ...
      """
      ext = [struct.pack("I", handle)]
      return _u2i(_lg_command_ext(self.sl, _CMD_FC, 4, ext, L=1))

   def file_read(self, handle, count):
      """
      Reads up to count bytes from the file.

      handle:= >= 0 (as returned by [*file_open*]).
       count:= >0, the number of bytes to read.

      If OK returns a list of the number of bytes read and a
      bytearray containing the bytes.

      On failure returns a list of a negative error code and an
      empty string.

      ...
      (b, d) = sbc.file_read(h2, 100)
      if b > 0:
         # process read data
      ...
      """
      bytes = CMD_INTERRUPTED
      ext = [struct.pack("II", handle, count)]
      rdata = ""
      with self.sl.l:
         bytes = u2i(
            _lg_command_ext_nolock(self.sl, _CMD_FR, 8, ext, L=2))
         if bytes > 0:
            rdata = self._rxbuf(bytes)
      return _u2i_list([bytes, rdata])

   def file_write(self, handle, data):
      """
      Writes the data bytes to the file.

      handle:= >= 0 (as returned by [*file_open*]).
        data:= the bytes to write.

      If OK returns 0.

      On failure returns a negative error code.

      ...
      sbc.file_write(h1, b'\\x02\\x03\\x04')

      sbc.file_write(h2, b'help')

      sbc.file_write(h2, "hello")

      sbc.file_write(h1, [2, 3, 4])
      ...
      """
      ext = [struct.pack("I", handle)] + [data]
      return _u2i(_lg_command_ext(self.sl, _CMD_FW, 4+len(data), ext, L=1))

   def file_seek(self, handle, seek_offset, seek_from):
      """
      Seeks to a position relative to the start, current position,
      or end of the file.  Returns the new position.

           handle:= >= 0 (as returned by [*file_open*]).
      seek_offset:= byte offset.
        seek_from:= FROM_START, FROM_CURRENT, or FROM_END.

      If OK returns the new file position.

      On failure returns a negative error code.

      ...
      new_pos = sbc.file_seek(h, 100, rgpio.FROM_START)

      cur_pos = sbc.file_seek(h, 0, rgpio.FROM_CURRENT)

      file_size = sbc.file_seek(h, 0, rgpio.FROM_END)
      ...
      """
      ext = [struct.pack("IiI", handle, seek_offset, seek_from)]
      return _u2i(_lg_command_ext(self.sl, _CMD_FS, 12, ext, L=3))

   def file_list(self, fpattern):
      """
      Returns a list of files which match a pattern.

      This is a privileged command. See [+Permits+].

      fpattern:= file pattern to match.

      If OK returns a list of the number of bytes read and a
      bytearray containing the matching filenames (the filenames
      are separated by newline characters).

      On failure returns a list of a negative error code and an
      empty string.

      ...
      #!/usr/bin/env python

      import rgpio

      sbc = rgpio.sbc()

      if not sbc.connected:
         exit()

      c, d = sbc.file_list("/ram/p*.c")
      if c > 0:
         print(d)

      sbc.stop()
      ...
      """
      bytes = CMD_INTERRUPTED
      ext = [struct.pack("I", 60000)] + [fpattern]
      rdata = ""
      with self.sl.l:
         bytes = u2i(_lg_command_ext_nolock(
            self.sl, _CMD_FL, 4+len(fpattern), ext, L=1))
         if bytes > 0:
            rdata = self._rxbuf(bytes)
      return _u2i_list([bytes, rdata])

   # GPIO

   def gpiochip_open(self, gpiochip):
      """
      This returns a handle to a gpiochip device.

      This is a privileged command.  See [+permits+].

      gpiochip:= >= 0

      If OK returns a handle (>= 0).

      On failure returns a negative error code.

      ...
      h = gpiochip_open(0) # open /dev/gpiochip0
      if h >= 0:
         # open okay
      else:
         # open error
      ...
      """
      ext = [struct.pack("I", gpiochip)]
      handle = u2i(_lg_command_ext(self.sl, _CMD_GO, 4, ext, L=1))
      if handle >= 0:
         handle = handle | (gpiochip << 16)

      return _u2i(handle)

   def gpiochip_close(self, handle):
      """
      This closes a gpiochip device.

      handle:= >= 0 (as returned by [*gpiochip_open*]).

      If OK returns 0.

      On failure returns a negative error code.

      ...
      sbc.gpiochip_close(h)
      ...
      """
      ext = [struct.pack("I", handle&0xffff)]
      return _u2i(_lg_command_ext(self.sl, _CMD_GC, 4, ext, L=1))

   def gpio_get_chip_info(self, handle):
      """
      This returns summary information of an opened gpiochip.

      handle:= >= 0 (as returned by [*gpiochip_open*]).

      If OK returns a list of okay status, number of
      lines, name, and label.

      On failure returns a negative error code.
      """
      bytes = CMD_INTERRUPTED
      ext = [struct.pack("I", handle&0xffff)]
      rdata = ""
      with self.sl.l:
         bytes = u2i(
            _lg_command_ext_nolock(self.sl, _CMD_GIC, 4, ext, L=1))
         if bytes > 0:
            rdata = self._rxbuf(bytes)
            lines, name, label = struct.unpack("I32s32s", rdata)
            bytes = OKAY
         else:
            lines, name, label = 0, "", ""
      return _u2i_list([bytes, lines, name.rstrip('\0'), label.rstrip('\0')])


   def gpio_get_line_info(self, handle, gpio):
      """
      This returns detailed information of a GPIO of
      an opened gpiochip.

      handle:= >= 0 (as returned by [*gpiochip_open*]).
        gpio:= the GPIO.

      If OK returns a list of okay status, offset,
      line flags, name, and user.

      On failure returns a negative error code.
      """
      bytes = CMD_INTERRUPTED
      ext = [struct.pack("II", handle&0xffff, gpio)]
      rdata = ""
      with self.sl.l:
         bytes = u2i(
            _lg_command_ext_nolock(self.sl, _CMD_GIL, 8, ext, L=2))
         if bytes > 0:
            rdata = self._rxbuf(bytes)
            offset, flags, name, user = struct.unpack("II32s32s", rdata)
            bytes = OKAY
         else:
            offset, flags, name, user = 0, 0, "", ""
      return _u2i_list(
         [bytes, offset, flags, name.rstrip('\0'), user.rstrip('\0')])

   def gpio_get_mode(self, handle, gpio):
      """
      This returns the mode of a GPIO.

      handle:= >= 0 (as returned by [*gpiochip_open*]).
        gpio:= the GPIO.

      If OK returns the mode of the GPIO.

      On failure returns a negative error code.

      Mode bit @ Value @ Meaning
      0        @  1    @ Kernel: In use by the kernel
      1        @  2    @ Kernel: Output
      2        @  4    @ Kernel: Active low
      3        @  8    @ Kernel: Open drain
      4        @ 16    @ Kernel: Open source
      5        @ 32    @ Kernel: ---
      6        @ 64    @ Kernel: ---
      7        @ 128   @ Kernel: ---
      8        @ 256   @ LG: Input
      9        @ 512   @ LG: Output
      10       @ 1024  @ LG: Alert
      11       @ 2048  @ LG: Group
      12       @ 4096  @ LG: ---
      13       @ 8192  @ LG: ---
      14       @ 16384 @ LG: ---
      15       @ 32768 @ LG: ---
      """
      ext = [struct.pack("II", handle&0xffff, gpio)]
      return _u2i(_lg_command_ext(self.sl, _CMD_GMODE, 8, ext, L=2))

   def gpio_claim_input(self, handle, gpio, lFlags=0):
      """
      This claims a GPIO for input.

      handle:= >= 0 (as returned by [*gpiochip_open*]).
        gpio:= the GPIO to be claimed.
      lFlags:= line flags for the GPIO.

      If OK returns 0.

      On failure returns a negative error code.

      The line flags may be used to set the GPIO
      as active low, open drain, or open source.

      ...
      sbc.gpio_claim_input(h, 23) # open GPIO 23 for input.
      ...
      """
      ext = [struct.pack("III", handle&0xffff, lFlags, gpio)]
      return _u2i(_lg_command_ext(self.sl, _CMD_GSIX, 12, ext, L=3))

   def gpio_claim_output(self, handle, gpio, level=0, lFlags=0):
      """
      This claims a GPIO for output.

      handle:= >= 0 (as returned by [*gpiochip_open*]).
        gpio:= the GPIO to be claimed.
       level:= the initial value for the GPIO.
      lFlags:= line flags for the GPIO.

      If OK returns 0.

      On failure returns a negative error code.

      The line flags may be used to set the GPIO
      as active low, open drain, or open source.

      If level is zero the GPIO will be initialised low (0).  If any other
      value is used the GPIO will be initialised high (1).

      ...
      sbc.gpio_claim_output(h, 3) # open GPIO 3 for low output.
      ...
      """
      ext = [struct.pack("IIII", handle&0xffff, lFlags, gpio, level)]
      return _u2i(_lg_command_ext(self.sl, _CMD_GSOX, 16, ext, L=4))

   def gpio_free(self, handle, gpio):
      """
      This frees a GPIO.

      handle:= >= 0 (as returned by [*gpiochip_open*]).
        gpio:= the GPIO to be freed.

      If OK returns 0.

      On failure returns a negative error code.

      The GPIO may now be claimed by another user or for
      a different purpose.
      """
      ext = [struct.pack("II", handle&0xffff, gpio)]
      return _u2i(_lg_command_ext(self.sl, _CMD_GSF, 8, ext, L=2))

   def group_claim_input(self, handle, gpio, lFlags=0):
      """
      This claims a group of GPIO for inputs.

      handle:= >= 0 (as returned by [*gpiochip_open*]).
       gpios:= a list of GPIO to be claimed.
      lFlags:= line flags for the group of GPIO.

      If OK returns 0.

      On failure returns a negative error code.

      The line flags may be used to set the group
      as active low, open drain, or open source.

      gpio is a list of one or more GPIO.  The first GPIO in the
      list is called the group leader and is used to reference the
      group as a whole.

      """
      if len(gpio):
         ext = bytearray()
         ext.extend(struct.pack("II", handle&0xffff, lFlags))
         for g in gpio:
            ext.extend(struct.pack("I", g))
         return _u2i(_lg_command_ext(
            self.sl, _CMD_GSGIX, (len(gpio)+2)*4, [ext], L=len(gpio)+2))
      else:
         return 0

   def group_claim_output(self, handle, gpio, levels=[0], lFlags=0):
      """
      This claims a group of GPIO for outputs.

      handle:= >= 0 (as returned by [*gpiochip_open*]).
        gpio:= a list of GPIO to be claimed.
      levels:= a list of the initial value for each GPIO.
      lFlags:= line flags for the group of GPIO.

      If OK returns 0.

      On failure returns a negative error code.

      The line flags may be used to set the group
      as active low, open drain, or open source.

      gpio is a list of one or more GPIO.  The first GPIO in the list is
      called the group leader and is used to reference the group as a whole.

      levels is a list of initialisation values for the GPIO. If a value is
      zero the corresponding GPIO will be initialised low (0).  If any other
      value is used the corresponding GPIO will be initialised high (1).

      """
      if len(gpio):
         diff = len(gpio)-len(levels)
         if diff > 0:
            levels = levels + [0]*diff
         ext = bytearray()
         ext.extend(struct.pack("II", handle&0xffff, lFlags))
         for g in gpio:
            ext.extend(struct.pack("I", g))
         for v in range(len(gpio)):
            ext.extend(struct.pack("I", levels[v]))
         return _u2i(_lg_command_ext(
            self.sl, _CMD_GSGOX,
            (2+(len(gpio)*2))*4, [ext], L=2+(len(gpio)*2)))
      else:
         return 0

   def group_free(self, handle, gpio):
      """
      This frees all the GPIO associated with a group.

      handle:= >= 0 (as returned by [*gpiochip_open*]).
        gpio:= the group leader.

      If OK returns 0.

      On failure returns a negative error code.

      The GPIO may now be claimed by another user or for a different purpose.

      """
      ext = [struct.pack("II", handle&0xffff, gpio)]
      return _u2i(_lg_command_ext(self.sl, _CMD_GSGF, 8, ext, L=2))

   def gpio_read(self, handle, gpio):
      """
      This returns the level of a GPIO.

      handle:= >= 0 (as returned by [*gpiochip_open*]).
        gpio:= the GPIO to be read.

      If OK returns 0 (low) or 1 (high).

      On failure returns a negative error code.

      This command will work for any claimed GPIO (even if a member
      of a group).  For an output GPIO the value returned
      will be that last written to the GPIO.

      """
      ext = [struct.pack("II", handle&0xffff, gpio)]
      return _u2i(_lg_command_ext(self.sl, _CMD_GR, 8, ext, L=2))

   def gpio_write(self, handle, gpio, level):
      """
      This sets the level of an output GPIO.

      handle:= >= 0 (as returned by [*gpiochip_open*]).
        gpio:= the GPIO to be written.
       level:= the value to write.

      If OK returns 0.

      On failure returns a negative error code.

      This command will work for any GPIO claimed as an output
      (even if a member of a group).

      If level is zero the GPIO will be set low (0).
      If any other value is used the GPIO will be set high (1).

      """
      ext = [struct.pack("III", handle&0xffff, gpio, level)]
      return _u2i(_lg_command_ext(self.sl, _CMD_GW, 12, ext, L=3))


   def group_read(self, handle, gpio):
      """
      This returns the levels read from a group.

      handle:= >= 0 (as returned by [*gpiochip_open*]).
        gpio:= the group to be read.

      If OK returns a list of group size and levels.

      On failure returns a list of negative error code and a dummy.

      This command will work for an output group as well as an input
      group.  For an output group the value returned
      will be that last written to the group GPIO.

      Note that this command will also work on an individual GPIO claimed
      as an input or output as that is treated as a group with one member.

      After a successful read levels is set as follows.

      Bit 0 is the level of the group leader. 
      Bit 1 is the level of the second GPIO in the group. 
      Bit x is the level of GPIO x+1 of the group.

      """
      ext = [struct.pack("II", handle&0xffff, gpio)]
      status = CMD_INTERRUPTED
      levels = 0
      with self.sl.l:
         bytes = u2i(
            _lg_command_ext_nolock(self.sl, _CMD_GGR, 8, ext, L=2))
         if bytes > 0:
            data = self._rxbuf(bytes)
            levels, status = struct.unpack('QI', _str(data))
         else:
            status = bytes
      return _u2i_list([status, levels])

   def group_write(self, handle, gpio, group_bits, group_mask=GROUP_ALL):
      """
      This sets the levels of an output group.

          handle:= >= 0 (as returned by [*gpiochip_open*]).
            gpio:= the group to be written.
      group_bits:= the level to set if the corresponding bit in
                   group_mask is set.
      group_mask:= a mask indicating the group GPIO to be updated.

      If OK returns 0.

      On failure returns a negative error code.

      The values of each GPIO of the group are set according to the bits 
      of group_bits.

      Bit 0 sets the level of the group leader. 
      Bit 1 sets the level of the second GPIO in the group. 
      Bit x sets the level of GPIO x+1 in the group.

      However this may be overridden by the group_mask.  A GPIO is only
      updated if the corresponding bit in the mask is 1.

      """
      ext = [struct.pack(
         "QQII", group_bits, group_mask, handle&0xffff, gpio)]
      return _u2i(_lg_command_ext(self.sl, _CMD_GGWX, 24, ext, Q=2, L=2))


   def tx_pulse(self, handle, gpio,
      pulse_on, pulse_off, pulse_offset=0, pulse_cycles=0):
      """
      This starts software timed pulses on an output GPIO.

            handle:= >= 0 (as returned by [*gpiochip_open*]).
              gpio:= the GPIO to be pulsed.
          pulse_on:= pulse high time in microseconds.
         pulse_off:= pulse low time in microsseconds.
      pulse_offset:= offset from nominal pulse start position.
      pulse_cycles:= the number of cycles to be sent, 0 for infinite.

      If OK returns the number of entries left in the PWM queue for the GPIO.

      On failure returns a negative error code.

      If both pulse_on and pulse_off are zero pulses will be
      switched off for that GPIO.  The active pulse, if any,
      will be stopped and any queued pulses will be deleted.

      Each successful call to this function consumes one PWM queue entry.

      pulse_cycles cycles are transmitted (0 means infinite).  Each
      cycle consists of pulse_on microseconds of GPIO high followed by
      pulse_off microseconds of GPIO low.

      PWM is characterised by two values, its frequency (number of cycles
      per second) and its dutycycle (percentage of high time per cycle).

      The set frequency will be 1000000 / (pulse_on + pulse_off) Hz.

      The set dutycycle will be pulse_on / (pulse_on + pulse_off) * 100 %.

      E.g. if pulse_on is 50 and pulse_off is 100 the frequency will be
      6666.67 Hz and the dutycycle will be 33.33 %.

      pulse_offset is a microsecond offset from the natural start of
      the pulse cycle.

      For instance if the PWM frequency is 10 Hz the natural start of each
      cycle is at seconds 0, then 0.1, 0.2, 0.3 etc.  In this case if
      the offset is 20000 microseconds the cycle will start at seconds
      0.02, 0.12, 0.22, 0.32 etc.

      Another pulse command may be issued to the GPIO before the last
      has finished.

      If the last pulse had infinite cycles (pulse_cycles of 0) then it
      will be replaced by the new settings at the end of the current
      cycle.  Otherwise it will be replaced by the new settings at
      the end of pulse_cycles cycles.

      Multiple pulse settings may be queued in this way.

      """
      ext = [struct.pack("IIIIII", handle&0xffff, gpio,
         pulse_on, pulse_off, pulse_offset, pulse_cycles)]
      return _u2i(_lg_command_ext(self.sl, _CMD_GPX, 24, ext, L=6))


   def tx_pwm(self, handle, gpio,
      pwm_frequency, pwm_duty_cycle, pulse_offset=0, pulse_cycles=0):
      """
      This starts software timed PWM on an output GPIO.

               handle:= >= 0 (as returned by [*gpiochip_open*]).
                 gpio:= the GPIO to be pulsed.
        pwm_frequency:= PWM frequency in Hz (0=off, 0.1-10000).
       pwm_duty_cycle:= PWM duty cycle in % (0-100).
        pulse_offset:= offset from nominal pulse start position.
        pulse_cycles:= the number of cycles to be sent, 0 for infinite.

      If OK returns the number of entries left in the PWM queue for the GPIO.

      On failure returns a negative error code.

      Each successful call to this function consumes one PWM queue entry.

      pulse_cycles cycles are transmitted (0 means infinite).

      PWM is characterised by two values, its frequency (number of cycles
      per second) and its dutycycle (percentage of high time per cycle).

      pulse_offset is a microsecond offset from the natural start of
      the pulse cycle.

      For instance if the PWM frequency is 10 Hz the natural start of each
      cycle is at seconds 0, then 0.1, 0.2, 0.3 etc.  In this case if
      the offset is 20000 microseconds the cycle will start at seconds
      0.02, 0.12, 0.22, 0.32 etc.

      Another PWM command may be issued to the GPIO before the last
      has finished.

      If the last pulse had infinite cycles then it will be replaced by
      the new settings at the end of the current cycle. Otherwise it will
      be replaced by the new settings when all its cycles are complete.

      Multiple pulse settings may be queued in this way.

      """
      ext = [struct.pack("IIIIII", handle&0xffff, gpio,
         int(pwm_frequency*1000), int(pwm_duty_cycle*1000),
         pulse_offset, pulse_cycles)]
      return _u2i(_lg_command_ext(self.sl, _CMD_PX, 24, ext, L=6))


   def tx_servo(self, handle, gpio, pulse_width,
      servo_frequency=50, pulse_offset=0, pulse_cycles=0):
      """
      This starts software timed servo pulses on an output GPIO.

      I would only use software timed servo pulses for testing
      purposes.  The timing jitter will cause the servo to fidget.
      This may cause it to overheat and wear out prematurely.

               handle:= >= 0 (as returned by [*gpiochip_open*]).
                 gpio:= the GPIO to be pulsed.
          pulse_width:= pulse high time in microseconds (0=off, 500-2500).
      servo_frequency:= the number of pulses per second (40-500).
         pulse_offset:= offset from nominal pulse start position.
         pulse_cycles:= the number of cycles to be sent, 0 for infinite.

      If OK returns the number of entries left in the PWM queue for the GPIO.

      On failure returns a negative error code.

      Each successful call to this function consumes one PWM queue entry.

      pulse_cycles cycles are transmitted (0 means infinite).

      pulse_offset is a microsecond offset from the natural start of
      the pulse cycle.

      Another servo command may be issued to the GPIO before the last
      has finished.

      If the last pulse had infinite cycles then it will be replaced by
      the new settings at the end of the current cycle. Otherwise it will
      be replaced by the new settings when all its cycles are compete.

      Multiple servo settings may be queued in this way.

      """
      ext = [struct.pack("IIIIII", handle&0xffff, gpio,
         pulse_width, servo_frequency, pulse_offset, pulse_cycles)]
      return _u2i(_lg_command_ext(self.sl, _CMD_SX, 24, ext, L=6))


   def tx_wave(self, handle, gpio, pulses):
      """
      This starts a software timed wave on an output group.

      handle:= >= 0 (as returned by [*gpiochip_open*]).
        gpio:= the group to be pulsed.
      pulses:= the pulses to transmit.

      If OK returns the number of entries left in the wave queue
      for the group.

      On failure returns a negative error code.

      Each successful call to this function consumes one wave queue entry.

      This command starts a wave of pulses.

      pulses is an array of pulses to be transmitted on the group.

      Each pulse is defined by the following triplet:

      bits:  the levels to set for the selected GPIO 
      mask:  the GPIO to select 
      delay: the delay in microseconds before the next pulse

      Another wave command may be issued to the group before the
      last has finished transmission. The new wave will start when
      the previous wave has competed.

      Multiple waves may be queued in this way.

      """
      if len(pulses):
         q = 3 * len(pulses)
         l = 2
         size = (q*8) + (l*4)
         ext1 = bytearray()
         for p in pulses:
            ext1.extend(struct.pack(
               "QQQ", p.group_bits, p.group_mask, p.pulse_delay))
         ext2 = struct.pack("II", handle&0xffff, gpio)
         ext = [ext1, ext2]
         return _u2i(_lg_command_ext(self.sl, _CMD_GWAVE, size, ext, Q=q, L=l))
      else:
         return 0


   def tx_busy(self, handle, gpio, kind):
      """
      This returns true if transmissions of the specified kind
      are active on the GPIO or group.

      handle:= >= 0 (as returned by [*gpiochip_open*]).
        gpio:= the GPIO or group to be checked.
        kind:= TX_PWM or TX_WAVE.

      If OK returns 1 for busy and 0 for not busy.

      On failure returns a negative error code.

      """
      ext = [struct.pack("III", handle&0xffff, gpio, kind)]
      return _u2i(_lg_command_ext(self.sl, _CMD_GBUSY, 12, ext, L=3))

   def tx_room(self, handle, gpio, kind):
      """
      This returns the number of slots there are to queue further
      transmissions of the specified kind on a GPIO or group.

      handle:= >= 0 (as returned by [*gpiochip_open*]).
        gpio:= the GPIO or group to be checked.
        kind:= TX_PWM or TX_WAVE.

      If OK returns the number of free entries (0 for none).

      On failure returns a negative error code.

      """
      ext = [struct.pack("III", handle&0xffff, gpio, kind)]
      return _u2i(_lg_command_ext(self.sl, _CMD_GROOM, 12, ext, L=3))

   def gpio_set_debounce_micros(self, handle, gpio, debounce_micros):
      """
      This sets the debounce time for a GPIO.

               handle:= >= 0 (as returned by [*gpiochip_open*]).
                 gpio:= the GPIO to be configured.
      debounce_micros:= the value to set.

      If OK returns 0.

      On failure returns a negative error code.

      This only affects alerts.

      An alert will only be issued if the edge has been stable for
      at least debounce microseconds.

      Generally this is used to debounce mechanical switches
      (e.g. contact bounce).

      Suppose that a square wave at 5 Hz is being generated on a GPIO.
      Each edge will last 100000 microseconds.  If a debounce time
      of 100001 is set no alerts will be generated,  If a debounce
      time of 99999 is set 10 alerts will be generated per second.

      Note that level changes will be timestamped debounce microseconds
      after the actual level change.

      """
      ext = [struct.pack("III", handle&0xffff, gpio, debounce_micros)]
      return _u2i(_lg_command_ext(self.sl, _CMD_GDEB, 12, ext, L=3))


   def gpio_set_watchdog_micros(self, handle, gpio, watchdog_micros):
      """
      This sets the watchdog time for a GPIO.

               handle:= >= 0 (as returned by [*gpiochip_open*]).
                 gpio:= the GPIO to be configured.
      watchdog_micros:= the value to set.

      If OK returns 0.

      On failure returns a negative error code.

      This only affects alerts.

      A watchdog alert will be sent if no edge alert has been issued
      for that GPIO in the previous watchdog microseconds.

      Note that only one watchdog alert will be sent per stream of
      edge alerts.  The watchdog is reset by the sending of a new
      edge alert.

      The level is set to TIMEOUT (2) for a watchdog alert.
      """
      ext = [struct.pack("III", handle&0xffff, gpio, watchdog_micros)]
      return _u2i(_lg_command_ext(self.sl, _CMD_GWDOG, 12, ext, L=3))


   def gpio_claim_alert(
      self, handle, gpio, eFlags, lFlags=0, notify_handle=None):
      """
      This claims a GPIO to be used as a source of alerts on level changes.

              handle:= >= 0 (as returned by [*gpiochip_open*]).
                gpio:= >= 0, as legal for the gpiochip.  
              eFlags:= event flags for the GPIO.
              lFlags:= line flags for the GPIO.
      notifiy_handle: >=0 (as returned by [*notify_open*]).

      If OK returns 0.

      On failure returns a negative error code.

      The line flags may be used to set the GPIO
      as active low, open drain, or open source.

      The event flags are used to generate alerts for a rising edge,
      falling edge, or both edges.

      Use the default notification handle of None unless you plan
      to read the alerts from a notification pipe you have opened.

      """
      if notify_handle is None:
         notify_handle = self._notify.handle
      ext = [struct.pack(
         "IIIII", handle&0xffff, lFlags, eFlags, gpio, notify_handle)]
      return _u2i(_lg_command_ext(self.sl, _CMD_GSAX, 20, ext, L=5))



   def callback(self, handle, gpio, edge=RISING_EDGE, func=None):
      """
      Calls a user supplied function (a callback) whenever the
      specified GPIO edge is detected.

      handle:= >= 0 (as returned by [*gpiochip_open*]).
        gpio:= >= 0, as legal for the gpiochip.  
        edge:= BOTH_EDGES, RISING_EDGE (default), or FALLING_EDGE.
        func:= user supplied callback function.

      Returns a callback instance.

      The user supplied callback receives four parameters, the chip,
      the GPIO, the level, and the timestamp.

      The reported level will be one of

      0: change to low (a falling edge) 
      1: change to high (a rising edge) 
      2: no level change (a watchdog timeout)

      The timestamp is when the change happened reported as the
      number of nanoseconds since the epoch (start of 1970).

      If a user callback is not specified a default tally callback is
      provided which simply counts edges.  The count may be retrieved
      by calling the callback instance's tally() method.  The count may
      be reset to zero by calling the callback instance's reset_tally()
      method.

      The callback may be cancelled by calling the callback
      instance's cancel() method.

      A GPIO may have multiple callbacks (although I can't think of
      a reason to do so).

      If you want to track the level of more than one GPIO do so by
      maintaining the state in the callback.  Do not use [*gpio_read*].
      Remember the alert that triggered the callback may have
      happened several milliseconds before and the GPIO may have
      changed level many times since then.

      ...
      def cbf(chip, gpio, level, timestamp):
         print(chip, gpio, level, timestamp)

      cb1 = sbc.callback(0, 22, rgpio.BOTH_EDGES, cbf)

      cb2 = sbc.callback(0, 4, rgpio.BOTH_EDGES)

      cb3 = sbc.callback(0, 17)

      print(cb3.tally())

      cb3.reset_tally()

      cb1.cancel() # To cancel callback cb1.
      ...
      """
      return _callback(self._notify, handle>>16, gpio, edge, func)


   # I2C

   def i2c_open(self, i2c_bus, i2c_address, i2c_flags=0):
      """
      Returns a handle (>= 0) for the device at the I2C bus address.

      This is a privileged command. See [+Permits+].

          i2c_bus:= >= 0.
      i2c_address:= 0-0x7F.
        i2c_flags:= 0, no flags are currently defined.

      If OK returns a handle (>= 0).

      On failure returns a negative error code.

      For the SMBus commands the low level transactions are shown
      at the end of the function description.  The following
      abbreviations are used:

      . .
      S     (1 bit) : Start bit
      P     (1 bit) : Stop bit
      Rd/Wr (1 bit) : Read/Write bit. Rd equals 1, Wr equals 0.
      A, NA (1 bit) : Accept and not accept bit.
      Addr  (7 bits): I2C 7 bit address.
      reg   (8 bits): Command byte, which often selects a register.
      Data  (8 bits): A data byte.
      Count (8 bits): A byte defining the length of a block operation.

      [..]: Data sent by the device.
      . .

      ...
      h = sbc.i2c_open(1, 0x53) # open device at address 0x53 on bus 1
      ...
      """
      ext = [struct.pack("III", i2c_bus, i2c_address, i2c_flags)]
      return _u2i(_lg_command_ext(self.sl, _CMD_I2CO, 12, ext, L=3))

   def i2c_close(self, handle):
      """
      Closes the I2C device.

      handle:= >= 0 (as returned by [*i2c_open*]).

      If OK returns 0.

      On failure returns a negative error code.

      ...
      sbc.i2c_close(h)
      ...
      """
      ext = [struct.pack("I", handle)]
      return _u2i(_lg_command_ext(self.sl, _CMD_I2CC, 4, ext, L=1))

   def i2c_write_quick(self, handle, bit):
      """
      Sends a single bit to the device.

      handle:= >= 0 (as returned by [*i2c_open*]).
         bit:= 0 or 1, the value to write.

      If OK returns 0.

      On failure returns a negative error code.

      SMBus 2.0 5.5.1 - Quick command.
      . .
      S Addr bit [A] P
      . .

      ...
      sbc.i2c_write_quick(0, 1) # send 1 to handle 0
      sbc.i2c_write_quick(3, 0) # send 0 to handle 3
      ...
      """
      ext = [struct.pack("II", handle, bit)]
      return _u2i(_lg_command_ext(self.sl, _CMD_I2CWQ, 8, ext, L=2))

   def i2c_write_byte(self, handle, byte_val):
      """
      Sends a single byte to the device.

        handle:= >= 0 (as returned by [*i2c_open*]).
      byte_val:= 0-255, the value to write.

      If OK returns 0.

      On failure returns a negative error code.

      SMBus 2.0 5.5.2 - Send byte.
      . .
      S Addr Wr [A] byte_val [A] P
      . .

      ...
      sbc.i2c_write_byte(1, 17)   # send byte   17 to handle 1
      sbc.i2c_write_byte(2, 0x23) # send byte 0x23 to handle 2
      ...
      """
      ext = [struct.pack("II", handle, byte_val)]
      return _u2i(_lg_command_ext(self.sl, _CMD_I2CWS, 8, ext, L=2))

   def i2c_read_byte(self, handle):
      """
      Reads a single byte from the device.

      handle:= >= 0 (as returned by [*i2c_open*]).

      If OK returns the read byte (0-255).

      On failure returns a negative error code.

      SMBus 2.0 5.5.3 - Receive byte.
      . .
      S Addr Rd [A] [Data] NA P
      . .

      ...
      b = sbc.i2c_read_byte(2) # read a byte from handle 2
      ...
      """
      ext = [struct.pack("I", handle)]
      return _u2i(_lg_command_ext(self.sl, _CMD_I2CRS, 4, ext, L=1))

   def i2c_write_byte_data(self, handle, reg, byte_val):
      """
      Writes a single byte to the specified register of the device.

        handle:= >= 0 (as returned by [*i2c_open*]).
           reg:= >= 0, the device register.
      byte_val:= 0-255, the value to write.

      If OK returns 0.

      On failure returns a negative error code.

      SMBus 2.0 5.5.4 - Write byte.
      . .
      S Addr Wr [A] reg [A] byte_val [A] P
      . .

      ...
      # send byte 0xC5 to reg 2 of handle 1
      sbc.i2c_write_byte_data(1, 2, 0xC5)

      # send byte 9 to reg 4 of handle 2
      sbc.i2c_write_byte_data(2, 4, 9)
      ...
      """
      ext = [struct.pack("III", handle, reg, byte_val)]
      return _u2i(_lg_command_ext(self.sl, _CMD_I2CWB, 12, ext, L=3))

   def i2c_write_word_data(self, handle, reg, word_val):
      """
      Writes a single 16 bit word to the specified register of the
      device.

        handle:= >= 0 (as returned by [*i2c_open*]).
           reg:= >= 0, the device register.
      word_val:= 0-65535, the value to write.

      If OK returns 0.

      On failure returns a negative error code.

      SMBus 2.0 5.5.4 - Write word.
      . .
      S Addr Wr [A] reg [A] word_val_Low [A] word_val_High [A] P
      . .

      ...
      # send word 0xA0C5 to reg 5 of handle 4
      sbc.i2c_write_word_data(4, 5, 0xA0C5)

      # send word 2 to reg 2 of handle 5
      sbc.i2c_write_word_data(5, 2, 23)
      ...
      """
      ext = [struct.pack("III", handle, reg, word_val)]
      return _u2i(_lg_command_ext(self.sl, _CMD_I2CWW, 12, ext, L=3))

   def i2c_read_byte_data(self, handle, reg):
      """
      Reads a single byte from the specified register of the device.

      handle:= >= 0 (as returned by [*i2c_open*]).
         reg:= >= 0, the device register.

      If OK returns the read byte (0-255).

      On failure returns a negative error code.

      SMBus 2.0 5.5.5 - Read byte.
      . .
      S Addr Wr [A] reg [A] S Addr Rd [A] [Data] NA P
      . .

      ...
      # read byte from reg 17 of handle 2
      b = sbc.i2c_read_byte_data(2, 17)

      # read byte from reg  1 of handle 0
      b = sbc.i2c_read_byte_data(0, 1)
      ...
      """
      ext = [struct.pack("II", handle, reg)]
      return _u2i(_lg_command_ext(self.sl, _CMD_I2CRB, 8, ext, L=2))

   def i2c_read_word_data(self, handle, reg):
      """
      Reads a single 16 bit word from the specified register of the
      device.

      handle:= >= 0 (as returned by [*i2c_open*]).
         reg:= >= 0, the device register.

      If OK returns the read word (0-65535).

      On failure returns a negative error code.

      SMBus 2.0 5.5.5 - Read word.
      . .
      S Addr Wr [A] reg [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P
      . .

      ...
      # read word from reg 2 of handle 3
      w = sbc.i2c_read_word_data(3, 2)

      # read word from reg 7 of handle 2
      w = sbc.i2c_read_word_data(2, 7)
      ...
      """
      ext = [struct.pack("II", handle, reg)]
      return _u2i(_lg_command_ext(self.sl, _CMD_I2CRW, 8, ext, L=2))

   def i2c_process_call(self, handle, reg, word_val):
      """
      Writes 16 bits of data to the specified register of the device
      and reads 16 bits of data in return.

        handle:= >= 0 (as returned by [*i2c_open*]).
           reg:= >= 0, the device register.
      word_val:= 0-65535, the value to write.

      If OK returns the read word (0-65535).

      On failure returns a negative error code.

      SMBus 2.0 5.5.6 - Process call.
      . .
      S Addr Wr [A] reg [A] word_val_Low [A] word_val_High [A]
         S Addr Rd [A] [DataLow] A [DataHigh] NA P
      . .

      ...
      r = sbc.i2c_process_call(h, 4, 0x1231)
      r = sbc.i2c_process_call(h, 6, 0)
      ...
      """
      ext = [struct.pack("III", handle, reg, word_val)]
      return _u2i(_lg_command_ext(self.sl, _CMD_I2CPC, 12, ext, L=3))

   def i2c_write_block_data(self, handle, reg, data):
      """
      Writes up to 32 bytes to the specified register of the device.

      handle:= >= 0 (as returned by [*i2c_open*]).
         reg:= >= 0, the device register.
        data:= the bytes to write.

      If OK returns 0.

      On failure returns a negative error code.

      SMBus 2.0 5.5.7 - Block write.
      . .
      S Addr Wr [A] reg [A] len(data) [A] data0 [A] data1 [A] ... [A]
         datan [A] P
      . .

      ...
      sbc.i2c_write_block_data(4, 5, b'hello')

      sbc.i2c_write_block_data(4, 5, "data bytes")

      sbc.i2c_write_block_data(5, 0, b'\\x00\\x01\\x22')

      sbc.i2c_write_block_data(6, 2, [0, 1, 0x22])
      ...
      """
      ext = [struct.pack("II", handle, reg)] + [data]
      return _u2i(_lg_command_ext(self.sl, _CMD_I2CWK, 8+len(data), ext, L=2))

   def i2c_read_block_data(self, handle, reg):
      """
      Reads a block of up to 32 bytes from the specified register of
      the device.

      handle:= >= 0 (as returned by [*i2c_open*]).
         reg:= >= 0, the device register.

      If OK returns a list of the number of bytes read and a
      bytearray containing the bytes.

      On failure returns a list of a negative error code and
      a null string.

      SMBus 2.0 5.5.7 - Block read.
      . .
      S Addr Wr [A] reg [A]
         S Addr Rd [A] [Count] A [Data] A [Data] A ... A [Data] NA P
      . .

      The amount of returned data is set by the device.

      ...
      (b, d) = sbc.i2c_read_block_data(h, 10)
      if b >= 0:
         # process data
      else:
         # process read failure
      ...
      """
      bytes = CMD_INTERRUPTED
      rdata = ""
      ext = [struct.pack("II", handle, reg)]
      with self.sl.l:
         bytes = u2i(_lg_command_ext_nolock(self.sl, _CMD_I2CRK, 8, ext, L=2))
         if bytes > 0:
            rdata = self._rxbuf(bytes)
      return _u2i_list([bytes, rdata])

   def i2c_block_process_call(self, handle, reg, data):
      """
      Writes data bytes to the specified register of the device
      and reads a device specified number of bytes of data in return.

      handle:= >= 0 (as returned by [*i2c_open*]).
         reg:= >= 0, the device register.
        data:= the bytes to write.

      If OK returns a list of the number of bytes read and a
      bytearray containing the bytes.

      On failure returns a list of a negative error code and
      a null string.

      The SMBus 2.0 documentation states that a minimum of 1 byte may
      be sent and a minimum of 1 byte may be received.  The total
      number of bytes sent/received must be 32 or less.

      SMBus 2.0 5.5.8 - Block write-block read.
      . .
      S Addr Wr [A] reg [A] len(data) [A] data0 [A] ... datan [A]
         S Addr Rd [A] [Count] A [Data] ... A P
      . .

      ...
      (b, d) = sbc.i2c_block_process_call(h, 10, b'\\x02\\x05\\x00')

      (b, d) = sbc.i2c_block_process_call(h, 10, b'abcdr')

      (b, d) = sbc.i2c_block_process_call(h, 10, "abracad")

      (b, d) = sbc.i2c_block_process_call(h, 10, [2, 5, 16])
      ...
      """
      bytes = CMD_INTERRUPTED
      rdata = ""
      ext = [struct.pack("II", handle, reg)] + [data]
      with self.sl.l:
         bytes = u2i(_lg_command_ext_nolock(self.sl, _CMD_I2CPK, 8+len(data), ext, L=2))
         if bytes > 0:
            rdata = self._rxbuf(bytes)
      return _u2i_list([bytes, rdata])

   def i2c_write_i2c_block_data(self, handle, reg, data):
      """
      Writes data bytes to the specified register of the device.
      1-32 bytes may be written.

      handle:= >= 0 (as returned by [*i2c_open*]).
         reg:= >= 0, the device register.
        data:= the bytes to write.

      If OK returns 0.

      On failure returns a negative error code.

      . .
      S Addr Wr [A] reg [A] data0 [A] data1 [A] ... [A] datan [NA] P
      . .

      ...
      sbc.i2c_write_i2c_block_data(4, 5, 'hello')

      sbc.i2c_write_i2c_block_data(4, 5, b'hello')

      sbc.i2c_write_i2c_block_data(5, 0, b'\\x00\\x01\\x22')

      sbc.i2c_write_i2c_block_data(6, 2, [0, 1, 0x22])
      ...
      """
      ext = [struct.pack("II", handle, reg)] + [data]
      return _u2i(_lg_command_ext(self.sl, _CMD_I2CWI, 8+len(data), ext, L=2))

   def i2c_read_i2c_block_data(self, handle, reg, count):
      """
      Reads count bytes from the specified register of the device.
      The count may be 1-32.

      handle:= >= 0 (as returned by [*i2c_open*]).
         reg:= >= 0, the device register.
       count:= >0, the number of bytes to read.

      If OK returns a list of the number of bytes read and a
      bytearray containing the bytes.

      On failure returns a list of a negative error code and
      a null string.

      . .
      S Addr Wr [A] reg [A]
         S Addr Rd [A] [Data] A [Data] A ... A [Data] NA P
      . .

      ...
      (b, d) = sbc.i2c_read_i2c_block_data(h, 4, 32)
      if b >= 0:
         # process data
      else:
         # process read failure
      ...
      """
      bytes = CMD_INTERRUPTED
      rdata = ""
      ext = [struct.pack("III", handle, reg, count)]
      with self.sl.l:
         bytes = u2i(_lg_command_ext_nolock(self.sl, _CMD_I2CRI, 12, ext, L=3))
         if bytes > 0:
            rdata = self._rxbuf(bytes)
      return _u2i_list([bytes, rdata])

   def i2c_read_device(self, handle, count):
      """
      Returns count bytes read from the raw device associated
      with handle.

      handle:= >= 0 (as returned by [*i2c_open*]).
       count:= >0, the number of bytes to read.

      If OK returns a list of the number of bytes read and a
      bytearray containing the bytes.

      On failure returns a list of a negative error code and
      a null string.

      . .
      S Addr Rd [A] [Data] A [Data] A ... A [Data] NA P
      . .

      ...
      (count, data) = sbc.i2c_read_device(h, 12)
      ...
      """
      bytes = CMD_INTERRUPTED
      rdata = ""
      ext = [struct.pack("II", handle, count)]
      with self.sl.l:
         bytes = u2i(
            _lg_command_ext_nolock(self.sl, _CMD_I2CRD, 8, ext, L=2))
         if bytes > 0:
            rdata = self._rxbuf(bytes)
      return _u2i_list([bytes, rdata])

   def i2c_write_device(self, handle, data):
      """
      Writes the data bytes to the raw device.

      handle:= >= 0 (as returned by [*i2c_open*]).
        data:= the bytes to write.

      If OK returns 0.

      On failure returns a negative error code.

      . .
      S Addr Wr [A] data0 [A] data1 [A] ... [A] datan [A] P
      . .

      ...
      sbc.i2c_write_device(h, b"\\x12\\x34\\xA8")

      sbc.i2c_write_device(h, b"help")

      sbc.i2c_write_device(h, 'help')

      sbc.i2c_write_device(h, [23, 56, 231])
      ...
      """
      ext = [struct.pack("I", handle)] + [data]
      return _u2i(_lg_command_ext(self.sl, _CMD_I2CWD, 4+len(data), ext, L=1))


   def i2c_zip(self, handle, data):
      """
      This function executes a sequence of I2C operations.  The
      operations to be performed are specified by the contents of data
      which contains the concatenated command codes and associated data.

      handle:= >= 0 (as returned by [*i2c_open*]).
        data:= the concatenated I2C commands, see below

      If OK returns a list of the number of bytes read and a
      bytearray containing the bytes.

      On failure returns a list of a negative error code and
      a null string.

      ...
      (count, data) = sbc.i2c_zip(h, [4, 0x53, 7, 1, 0x32, 6, 6, 0])
      ...

      The following command codes are supported:

      Name    @ Cmd & Data @ Meaning
      End     @ 0          @ No more commands
      Escape  @ 1          @ Next P is two bytes
      Address @ 2 P        @ Set I2C address to P
      Flags   @ 3 lsb msb  @ Set I2C flags to lsb + (msb << 8)
      Read    @ 4 P        @ Read P bytes of data
      Write   @ 5 P ...    @ Write P bytes of data

      The address, read, and write commands take a parameter P.
      Normally P is one byte (0-255).  If the command is preceded by
      the Escape command then P is two bytes (0-65535, least significant
      byte first).

      The address defaults to that associated with the handle.
      The flags default to 0.  The address and flags maintain their
      previous value until updated.

      Any read I2C data is concatenated in the returned bytearray.

      ...
      Set address 0x53, write 0x32, read 6 bytes
      Set address 0x1E, write 0x03, read 6 bytes
      Set address 0x68, write 0x1B, read 8 bytes
      End

      2 0x53  5 1 0x32  4 6
      2 0x1E  5 1 0x03  4 6
      2 0x68  5 1 0x1B  4 8
      0
      ...
      """
      bytes = CMD_INTERRUPTED
      rdata = ""
      ext = [struct.pack("I", handle)] + [data]
      with self.sl.l:
         bytes = u2i(_lg_command_ext_nolock(
            self.sl, _CMD_I2CZ, 4+len(data), ext, L=1))
         if bytes > 0:
            rdata = self._rxbuf(bytes)
      return _u2i_list([bytes, rdata])

   # NOTIFICATIONS

   def notify_open(self):
      """
      Opens a notification pipe.

      This is a privileged command. See [+Permits+].

      If OK returns a handle (>= 0).

      On failure returns a negative error code.

      A notification is a method for being notified of GPIO
      alerts via a pipe.

      Pipes are only accessible from the local machine so this
      function serves no purpose if you are using Python from a
      remote machine.  The in-built (socket) notifications
      provided by [*callback*] should be used instead.

      The pipes are created in the library's working directory.

      Notifications for handle x will be available at the pipe
      named lgd-nfyx (where x is the handle number).

      E.g. if the function returns 15 then the notifications must be
      read from lgd-nfy15.

      Notifications have the following structure:

      . .
      Q timestamp
      B chip
      B gpio
      B level
      B flags
      . .

      timestamp: the number of nanoseconds since the epoch (start of 1970). 
      chip: the gpiochip device number (NOT the handle). 
      gpio: the GPIO. 
      level: indicates the level of the GPIO (0=low, 1=high, 2=timeout). 
      flags: no flags are currently defined.

      ...
      h = sbc.notify_open()
      if h >= 0:
         sbc.notify_resume(h)
      ...
      """
      return _u2i(_lg_command(self.sl, _CMD_NO))

   def notify_pause(self, handle):
      """
      Pauses notifications on a handle.

      handle:= >= 0 (as returned by [*notify_open*])

      If OK returns 0.

      On failure returns a negative error code.

      Notifications for the handle are suspended until
      [*notify_resume*] is called.

      ...
      h = sbc.notify_open()
      if h >= 0:
         sbc.notify_resume(h)
         ...
         sbc.notify_pause(h)
         ...
         sbc.notify_resume(h)
         ...
      ...
      """
      ext = [struct.pack("I", handle)]
      return _u2i(_lg_command_ext(self.sl, _CMD_NP, 4, ext, L=1))

   def notify_resume(self, handle):
      """
      Resumes notifications on a handle.

      handle:= >= 0 (as returned by [*notify_open*])

      If OK returns 0.

      On failure returns a negative error code.

      ...
      h = sbc.notify_open()
      if h >= 0:
         sbc.notify_resume(h)
      ...
      """
      ext = [struct.pack("I", handle)]
      return _u2i(_lg_command_ext(self.sl, _CMD_NR, 4, ext, L=1))

   def notify_close(self, handle):
      """
      Stops notifications on a handle and frees the handle for reuse.

      handle:= >= 0 (as returned by [*notify_open*])

      If OK returns 0.

      On failure returns a negative error code.

      ...
      h = sbc.notify_open()
      if h >= 0:
         sbc.notify_resume(h)
         ...
         sbc.notify_close(h)
         ...
      ...
      """
      ext = [struct.pack("I", handle)]
      return _u2i(_lg_command_ext(self.sl, _CMD_NC, 4, ext, L=1))

   # SCRIPTS

   def script_store(self, script):
      """
      Store a script for later execution.

      This is a privileged command. See [+Permits+].

      script:= the script text as a series of bytes.

      If OK returns a handle (>= 0).

      On failure returns a negative error code.

      ...
      h = sbc.script_store(
         b'tag 0 w 22 1 mils 100 w 22 0 mils 100 dcr p0 jp 0')
      ...
      """
      if len(script):
         return _u2i(_lg_command_ext(
            self.sl, _CMD_PROC, len(script)+1, [script+'\0']))
      else:
         return 0

   def script_run(self, handle, params=None):
      """
      Runs a stored script.

      handle:= >=0 (as returned by [*script_store*]).
      params:= up to 10 parameters required by the script.

      If OK returns 0.

      On failure returns a negative error code.

      ...
      s = sbc.script_run(h, [par1, par2])

      s = sbc.script_run(h)

      s = sbc.script_run(h, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
      ...
      """
      ext = struct.pack("I", handle)
      nump = 1
      if params is not None:
         for p in params:
            ext += struct.pack("I", p)
         nump = 1 + len(params)
      return _u2i(_lg_command_ext(self.sl, _CMD_PROCR, nump*4, [ext], L=nump))

   def script_update(self, handle, params=None):
      """
      Sets the parameters of a script.  The script may or
      may not be running.  The parameters of the script are
      overwritten with the new values.

      handle:= >=0 (as returned by [*script_store*]).
      params:= up to 10 parameters required by the script.

      If OK returns 0.

      On failure returns a negative error code.

      ...
      s = sbc.script_update(h, [par1, par2])

      s = sbc.script_update(h, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
      ...
      """
      ext = struct.pack("I", handle)
      nump = 1
      if params is not None:
         for p in params:
            ext += struct.pack("I", p)
         nump = 1 + len(params)
      return _u2i(_lg_command_ext(self.sl, _CMD_PROCU, nump*4, [ext], L=nump))

   def script_status(self, handle):
      """
      Returns the run status of a stored script as well as the
      current values of parameters 0 to 9.

      handle:= >=0 (as returned by [*script_store*]).

      If OK returns a list of the run status and a list of
      the 10 parameters.

      On failure returns a negative error code and a null list.

      The run status may be

      . .
      SCRIPT_INITING
      SCRIPT_READY
      SCRIPT_RUNNING
      SCRIPT_WAITING
      SCRIPT_ENDED
      SCRIPT_HALTED
      SCRIPT_FAILED
      . .

      ...
      (s, pars) = sbc.script_status(h)
      ...
      """
      status = CMD_INTERRUPTED
      params = ()
      ext = [struct.pack("I", handle)]
      with self.sl.l:
         bytes = u2i(
            _lg_command_ext_nolock(self.sl, _CMD_PROCP, 4, ext, L=1))
         if bytes > 0:
            data = self._rxbuf(bytes)
            pars = struct.unpack('11i', _str(data))
            status = pars[0]
            params = pars[1:]
         else:
            status = bytes
      return _u2i_list([status, params])

   def script_stop(self, handle):
      """
      Stops a running script.

      handle:= >=0 (as returned by [*script_store*]).

      If OK returns 0.

      On failure returns a negative error code.

      ...
      status = sbc.script_stop(h)
      ...
      """
      ext = [struct.pack("I", handle)]
      return _u2i(_lg_command_ext(self.sl, _CMD_PROCS, 4, ext, L=1))

   def script_delete(self, handle):
      """
      Deletes a stored script.

      handle:= >=0 (as returned by [*script_store*]).

      If OK returns 0.

      On failure returns a negative error code.

      ...
      status = sbc.script_delete(h)
      ...
      """
      ext = [struct.pack("I", handle)]
      return _u2i(_lg_command_ext(self.sl, _CMD_PROCD, 4, ext, L=1))

   # SERIAL

   def serial_open(self, tty, baud, ser_flags=0):
      """
      Returns a handle for the serial tty device opened
      at baud bits per second.  The device muse be in /dev.

      This is a privileged command. See [+Permits+].

            tty:= the serial device to open.
           baud:= baud rate in bits per second, see below.
      ser_flags:= 0, no flags are currently defined.

      If OK returns a handle (>= 0).

      On failure returns a negative error code.

      The baud rate must be one of 50, 75, 110, 134, 150,
      200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200,
      38400, 57600, 115200, or 230400.

      ...
      h1 = sbc.serial_open("ttyAMA0", 300)

      h2 = sbc.serial_open("ttyUSB1", 19200, 0)

      h3 = sbc.serial_open("serial0", 9600)
      ...
      """
      ext = [struct.pack("II", baud, ser_flags)] + [tty]
      return _u2i(_lg_command_ext(self.sl, _CMD_SERO, 8+len(tty), ext, L=2))

   def serial_close(self, handle):
      """
      Closes the serial device.

      handle:= >= 0 (as returned by [*serial_open*]).

      If OK returns 0.

      On failure returns a negative error code.

      ...
      sbc.serial_close(h1)
      ...
      """
      ext = [struct.pack("I", handle)]
      return _u2i(_lg_command_ext(self.sl, _CMD_SERC, 4, ext, L=1))

   def serial_read_byte(self, handle):
      """
      Returns a single byte from the device.

      handle:= >= 0 (as returned by [*serial_open*]).

      If OK returns the read byte (0-255).

      On failure returns a negative error code.

      ...
      b = sbc.serial_read_byte(h1)
      ...
      """
      ext = [struct.pack("I", handle)]
      return _u2i(_lg_command_ext(self.sl, _CMD_SERRB, 4, ext, L=1))

   def serial_write_byte(self, handle, byte_val):
      """
      Writes a single byte to the device.

        handle:= >= 0 (as returned by [*serial_open*]).
      byte_val:= 0-255, the value to write.

      If OK returns 0.

      On failure returns a negative error code.

      ...
      sbc.serial_write_byte(h1, 23)

      sbc.serial_write_byte(h1, ord('Z'))
      ...
      """
      ext = [struct.pack("II", handle, byte_val)]
      return _u2i(
         _lg_command_ext(self.sl, _CMD_SERWB, 8, ext, L=2))

   def serial_read(self, handle, count=1000):
      """
      Reads up to count bytes from the device.

      handle:= >= 0 (as returned by [*serial_open*]).
       count:= >0, the number of bytes to read (defaults to 1000).

      If OK returns a list of the number of bytes read and
      a bytearray containing the bytes.

      On failure returns a list of negative error code and
      a null string.

      If no data is ready a bytes read of zero is returned.

      ...
      (b, d) = sbc.serial_read(h2, 100)
      if b > 0:
         # process read data
      ...
      """
      bytes = CMD_INTERRUPTED
      rdata = ""
      ext = [struct.pack("II", handle, count)]
      with self.sl.l:
         bytes = u2i(
            _lg_command_ext_nolock(self.sl, _CMD_SERR, 8, ext, L=2))
         if bytes > 0:
            rdata = self._rxbuf(bytes)
      return _u2i_list([bytes, rdata])

   def serial_write(self, handle, data):
      """
      Writes the data bytes to the device.

      handle:= >= 0 (as returned by [*serial_open*]).
        data:= the bytes to write.

      If OK returns 0.

      On failure returns a negative error code.

      ...
      sbc.serial_write(h1, b'\\x02\\x03\\x04')

      sbc.serial_write(h2, b'help')

      sbc.serial_write(h2, "hello")

      sbc.serial_write(h1, [2, 3, 4])
      ...
      """
      ext = [struct.pack("I", handle)] + [data]
      return _u2i(_lg_command_ext(self.sl, _CMD_SERW, 4+len(data), ext, L=1))

   def serial_data_available(self, handle):
      """
      Returns the number of bytes available to be read from the
      device.

      handle:= >= 0 (as returned by [*serial_open*]).

      If OK returns the count of bytes available (>= 0).

      On failure returns a negative error code.

      ...
      rdy = sbc.serial_data_available(h1)

      if rdy > 0:
         (b, d) = sbc.serial_read(h1, rdy)
      ...
      """
      ext = [struct.pack("I", handle)]
      return _u2i(_lg_command_ext(self.sl, _CMD_SERDA, 4, ext, L=1))


   # SHELL

   def shell(self, shellscr, pstring=""):
      """
      This function uses the system call to execute a shell script
      with the given string as its parameter.

      This is a privileged command. See [+Permits+].

      shellscr:= the name of the script, only alphanumerics,
                    '-' and '_' are allowed in the name
      pstring := the parameter string to pass to the script

      If OK returns the exit status of the system call.

      On failure returns a negative error code.

      shellscr must exist in a directory named cgi in the daemon's
      configuration directory and must be executable.

      The returned exit status is normally 256 times that set by
      the shell script exit function.  If the script can't be
      found 32512 will be returned.

      The following table gives some example returned statuses:

      Script exit status @ Returned system call status
      1                  @ 256
      5                  @ 1280
      10                 @ 2560
      200                @ 51200
      script not found   @ 32512

      ...
      // pass two parameters, hello and world
      status = sbc.shell("scr1", "hello world");

      // pass three parameters, hello, string with spaces, and world
      status = sbc.shell("scr1", "hello 'string with spaces' world");

      // pass one parameter, hello string with spaces world
      status = sbc.shell("scr1", "\\"hello string with spaces world\\"");
      ...
      """
      ls = len(shellscr)+1
      lp = len(pstring)+1
      ext = [struct.pack("I", ls)] + [shellscr+'\x00'+pstring+'\x00']
      return _u2i(_lg_command_ext(self.sl, _CMD_SHELL, 4+ls+lp, ext, L=1))

   # SPI

   def spi_open(self, spi_device, spi_channel, baud, spi_flags=0):
      """
      Returns a handle for the SPI device on the channel.  Data
      will be transferred at baud bits per second.  The flags
      may be used to modify the default behaviour.

      This is a privileged command. See [+Permits+].

       spi_device:= >= 0.
      spi_channel:= >= 0.
             baud:= speed to use.
        spi_flags:= see below.

      If OK returns a handle (>= 0).

      On failure returns a negative error code.

      spi_flags consists of the least significant 2 bits.

      . .
      1  0
      m  m
      . .

      mm defines the SPI mode.

      . .
      Mode POL PHA
       0    0   0
       1    0   1
       2    1   0
       3    1   1
      . .


      The other bits in flags should be set to zero.

      ...
      # open SPI device on channel 1 in mode 3 at 50000 bits per second

      h = sbc.spi_open(1, 50000, 3)
      ...
      """
      ext = [struct.pack("IIII", spi_device, spi_channel, baud, spi_flags)]
      return _u2i(_lg_command_ext(self.sl, _CMD_SPIO, 16, ext, L=4))

   def spi_close(self, handle):
      """
      Closes the SPI device.

      handle:= >= 0 (as returned by [*spi_open*]).

      If OK returns 0.

      On failure returns a negative error code.

      ...
      sbc.spi_close(h)
      ...
      """
      ext = [struct.pack("I", handle)]
      return _u2i(_lg_command_ext(self.sl, _CMD_SPIC, 4, ext, L=1))

   def spi_read(self, handle, count):
      """
      Reads count bytes from the SPI device.

      handle:= >= 0 (as returned by [*spi_open*]).
       count:= >0, the number of bytes to read.

      If OK returns a list of the number of bytes read and a
      bytearray containing the bytes.

      On failure returns a list of negative error code and
      a null string.

      ...
      (b, d) = sbc.spi_read(h, 60) # read 60 bytes from handle h
      if b == 60:
         # process read data
      else:
         # error path
      ...
      """
      bytes = CMD_INTERRUPTED
      rdata = ""
      ext = [struct.pack("II", handle, count)]
      with self.sl.l:
         bytes = u2i(_lg_command_ext_nolock(self.sl, _CMD_SPIR, 8, ext, L=2))
         if bytes > 0:
            rdata = self._rxbuf(bytes)
      return _u2i_list([bytes, rdata])

   def spi_write(self, handle, data):
      """
      Writes the data bytes to the SPI device.

      handle:= >= 0 (as returned by [*spi_open*]).
        data:= the bytes to write.

      If OK returns 0.

      On failure returns a negative error code.

      ...
      sbc.spi_write(0, b'\\x02\\xc0\\x80') # write 3 bytes to handle 0

      sbc.spi_write(0, b'defgh')        # write 5 bytes to handle 0

      sbc.spi_write(0, "def")           # write 3 bytes to handle 0

      sbc.spi_write(1, [2, 192, 128])   # write 3 bytes to handle 1
      ...
      """
      # I p1 handle
      # I p2 0
      # I p3 len
      ## extension ##
      # s len data bytes
      ext = [struct.pack("I", handle)] + [data]
      return _u2i(_lg_command_ext(self.sl, _CMD_SPIW, 4+len(data), ext, L=1))

   def spi_xfer(self, handle, data):
      """
      Writes the data bytes to the SPI device,
      returning the data bytes read from the device.

      handle:= >= 0 (as returned by [*spi_open*]).
        data:= the bytes to write.

      If OK returns a list of the number of bytes read and a
      bytearray containing the bytes.

      On failure returns a list of negative error code and
      a null string.

      ...
      (count, rx_data) = sbc.spi_xfer(h, b'\\x01\\x80\\x00')

      (count, rx_data) = sbc.spi_xfer(h, [1, 128, 0])

      (count, rx_data) = sbc.spi_xfer(h, b"hello")

      (count, rx_data) = sbc.spi_xfer(h, "hello")
      ...
      """
      # I p1 handle
      # I p2 0
      # I p3 len
      ## extension ##
      # s len data bytes

      bytes = CMD_INTERRUPTED
      rdata = ""
      ext = [struct.pack("I", handle)] + [data]
      with self.sl.l:
         bytes = u2i(_lg_command_ext_nolock(
            self.sl, _CMD_SPIX, 4+len(data), ext, L=1))
         if bytes > 0:
            rdata = self._rxbuf(bytes)
      return _u2i_list([bytes, rdata])


   # UTILITIES

   def get_sbc_name(self):
      """
      Returns the name of the sbc running the rgpiod daemon.

      If OK returns the SBC host name.

      On failure returns a null string.

      ...
      server = sbc.get_sbc_name()
      print(server)
      ...
      """
      bytes = CMD_INTERRUPTED
      rdata = ""
      with self.sl.l:
         bytes = u2i(
            _lg_command_nolock(self.sl, _CMD_SBC))
         if bytes > 0:
            rdata = self._rxbuf(bytes)
         else:
            rdata = ""
      return rdata

   def set_user(self, user="default",
      secretsFile=os.path.expanduser("~/.lg_secret")):
      """
      Sets the rgpiod daemon user.  The user then has the
      associated permissions.

             user:= the user to set (defaults to the default user).
      secretsFile:= the path to the shared secret file (defaults to
                    .lg_secret in the users home directory).

      If OK returns True if the user was set, False if not.

      On failure returns a negative error code.

      The returned value is True if permission is granted.

      ...
      if sbc.set_user("gpio"):
         print("using user gpio permissions")
      else:
         print("using default permissions")
      ...
      """

      user = user.strip()

      if user == "":
         user = "default"

      secret = bytearray()

      with open(secretsFile) as f:
         for line in f:
            l = line.split("=")
            if len(l) == 2:
               if l[0].strip() == user:
                  secret = bytearray(l[1].strip().encode('utf-8'))
                  break

      bytes = CMD_INTERRUPTED

      with self.sl.l:

         salt1 = "{:015x}".format((int(time.time()*1e7))&0xfffffffffffffff)
         ext = salt1 + '.' + user
         bytes = u2i(_lg_command_ext_nolock(
            self.sl, _CMD_USER, len(ext), [ext]))

         if bytes < 0:
            return bytes

         salt1 = bytearray(salt1.encode('utf-8'))
         salt2 = self._rxbuf(bytes)[:15]

         pwd=""

         h = hashlib.md5()
         h.update(salt1 + secret + salt2)
         pwd = h.hexdigest()

         res = u2i(_lg_command_ext_nolock(
            self.sl, _CMD_PASSW, len(pwd), [pwd]))

      return res

   def set_share_id(self, handle, share_id):
      """
      Starts or stops sharing of an object.

        handle:= >=0
      share_id:= >= 0, 0 stops sharing.

      If OK returns 0.

      On failure returns a negative error code.

      Normally objects associated with a handle are only accessible
      to the Python script which created them (and are automatically
      deleted when the script ends).

      If a non-zero share is set the object is accessible to any
      software which knows the share (and are not automatically
      deleted when the script ends).

      ...
      sbc.share_set(h, 23)
      ...
      """
      ext = [struct.pack("II", handle, share_id)]
      return _u2i(_lg_command_ext(self.sl, _CMD_SHRS, 8, ext, L=2))

   def use_share_id(self, share_id):
      """
      Starts or stops sharing of an object.

      share_id:= >= 0, 0 stops sharing.

      If OK returns 0.

      On failure returns a negative error code.

      Normally objects associated with a handle are only accessible
      to the Python script which created them (and are automatically
      deleted when the script ends).

      If a non-zero share is set the object is accessible to any
      software which knows the share and the object handle.

      ...
      sbc.share_use(23)
      ...
      """
      ext = [struct.pack("I", share_id)]
      return _u2i(_lg_command_ext(self.sl, _CMD_SHRU, 4, ext, L=1))

   def get_internal(self, config_id):
      """
      Returns the value of a configuration item.

      This is a privileged command. See [+Permits+].

      If OK returns a list of 0 (OK) and the item's value.

      On failure returns a list of negative error code and None.

      config_id:= the configuration item.

      ...
      cfg = sbc.get_internal(0)
      print(cfg)
      ...
      """
      ext = [struct.pack("I", config_id)]
      status = CMD_INTERRUPTED
      config_value = None
      with self.sl.l:
         bytes = u2i(
            _lg_command_ext_nolock(self.sl, _CMD_CGI, 4, ext, L=1))
         if bytes > 0:
            data = self._rxbuf(bytes)
            config_value = struct.unpack('Q', _str(data))[0]
            status = OKAY
         else:
            status = bytes
      return _u2i_list([status, config_value])

   def set_internal(self, config_id, config_value):
      """
      Sets the value of a sbc internal.

      This is a privileged command. See [+Permits+].

         config_id:= the configuration item.
      config_value:= the value to set.

      If OK returns 0.

      On failure returns a negative error code.

      ...
      sbc.set_internal(0, 255)
      cfg = sbc.get_internal()
      print(cfg)
      ...
      """
      ext = [struct.pack("QI", config_value, config_id)]
      return _u2i(_lg_command_ext(self.sl, _CMD_CSI, 12, ext, Q=1, L=1))


def xref():
   """
   baud:
   The speed of serial communication (I2C, SPI, serial link)
   in bits per second.

   bit: 0-1
   A value of 0 or 1.

   byte_val: 0-255
   A whole number.

   config_id:
   A number identifying a configuration item.

   . .
   CFG_ID_DEBUG_LEVEL 0
   CFG_ID_MIN_DELAY   1
   . .

   config_value:
   The value of a configurtion item.


   connected:
   True if a connection was established, False otherwise.

   count:
   The number of bytes of data to be transferred.

   data:
   Data to be transmitted, a series of bytes.

   debounce_micros:
   The debounce time in microseconds.

   edge:
   . .
   RISING_EDGE
   FALLING_EDGE
   BOTH_EDGES
   . .

   eFlags:
   Alert request flags for the GPIO.

   The following values may be or'd to form the value.

   . .
   RISING_EDGE
   FALLING_EDGE
   BOTH_EDGES
   . .

   errnum: <0
   Indicates an error.  Use [*rgpio.error_text*] for the error text.

   file_mode:
   The mode may have the following values

   . .
   FILE_READ   1
   FILE_WRITE  2
   FILE_RW     3
   . .

   The following values can be or'd into the file open mode

   . .
   FILE_APPEND 4
   FILE_CREATE 8
   FILE_TRUNC  16
   . .

   file_name:
   A full file path.  To be accessible the path must match
   an entry in the [files] section of the permits file.

   fpattern:
   A file path which may contain wildcards.  To be accessible the path
   must match an entry in the [files] section of the permits file.

   func:
   A user supplied callback function.

   gpio:
   The 0 based offset of a GPIO from the start of a gpiochip.

   gpiochip: >= 0
   The number of a gpiochip device.

   group_bits:
   A 64-bit value used to set the levels of a group.

   Set bit x to set GPIO x of the group high.

   Clear bit x to set GPIO x of the group low.

   group_mask:
   A 64-bit value used to determine which members of a group
   should be updated.

   Set bit x to update GPIO x of the group.

   Clear bit x to leave GPIO x of the group unaltered.

   handle: >= 0
   A number referencing an object opened by one of the following

   [*file_open*] 
   [*gpiochip_open*] 
   [*i2c_open*] 
   [*notify_open*] 
   [*serial_open*] 
   [*script_store*] 
   [*spi_open*]

   host:
   The name or IP address of the sbc running the rgpiod daemon.

   i2c_address: 0-0x7F
   The address of a device on the I2C bus.

   i2c_bus: >= 0
   An I2C bus number.

   i2c_flags: 0
   No I2C flags are currently defined.

   kind: TX_PWM or TX_WAVE
   A type of transmission.

   level: 0 or 1
   A GPIO level.

   levels:
   A list of GPIO levels.

   lFlags:
   Line modifiers for the GPIO.

   The following values may be or'd to form the value.

   . .
   SET_ACTIVE_LOW
   SET_OPEN_DRAIN
   SET_OPEN_SOURCE
   . .

   notify_handle:
   This associates a notification with a GPIO alert.

   params: 32 bit number
   When scripts are started they can receive up to 10 parameters
   to define their operation.

   port:
   The port used by the rgpiod daemon, defaults to 8889.

   pstring:
   The string to be passed to a [*shell*] script to be executed.

   pulse_cycles: >= 0
   The number of pulses to generate.  A value of 0 means infinite.

   pulse_delay:
   The delay in microseconds before the next wave pulse.

   pulse_off: >= 0
   The off period for a pulse in microseconds.

   pulse_offset: >= 0
   The offset in microseconds from the nominal pulse start.

   pulse_on: >= 0
   The on period for a pulse in microseconds.

   pulse_width: 0, 500-2500 microseconds
   Servo pulse width

   pulses:
   pulses is a list of pulse objects.  A pulse object is a container
   class with the following members.

   group_bits - the levels to set if the corresponding bit in
   group_mask is set. 
   group_mask - a mask indicating the group GPIO to be updated. 
   pulse_delay - the delay in microseconds before the next pulse.

   pwm_duty_cycle: 0-100 %
   PWM duty cycle %

   pwm_frequency: 0.1-10000 Hz
   PWM frequency

   reg: 0-255
   An I2C device register.  The usable registers depend on the
   actual device.

   script:
   The text of a script to store on the rgpiod daemon.

   secretsFile:
   The file containing the shared secret for a user.  If the shared
   secret for a user matches that known by the rgpiod daemon the user can
   "log in" to the daemon.

   seek_from: 0-2
   Direction to seek for [*file_seek*].

   . .
   FROM_START=0
   FROM_CURRENT=1
   FROM_END=2
   . .

   seek_offset:
   The number of bytes to move forward (positive) or backwards
   (negative) from the seek position (start, current, or end of file).

   ser_flags: 32 bit
   No serial flags are currently defined.

   servo_frequency:: 40-500 Hz
   Servo pulse frequency

   share_id:
   Objects created with a non-zero share_id are persistent and may be
   used by other software which knows the share_id.

   shellscr:
   The name of a shell script.  The script must exist
   in the cgi directory of the rgpiod daemon's configuration
   directory and must be executable.

   show_errors:
   Controls the display of rgpiod daemon connection failures.
   The default of True prints the probable failure reasons to
   standard output.

   spi_channel: >= 0
   A SPI channel.

   spi_device: >= 0
   A SPI device.

   spi_flags: 32 bit
   See [*spi_open*].

   tty:
   A serial device, e.g. ttyAMA0, ttyUSB0

   uint32:
   An unsigned 32 bit number.

   user:
   A name known by the rgpiod daemon and associated with a set of user
   permissions.

   watchdog_micros:
   The watchdog time in microseconds.

   word_val: 0-65535
   A whole number.
   """
   pass

