configurable gpio devices

This commit is contained in:
Devaev Maxim 2020-12-25 11:08:56 +03:00
parent 4447e49abb
commit 0adfe17f70
11 changed files with 53 additions and 21 deletions

View File

@ -101,7 +101,6 @@ run: testenv $(TESTENV_GPIO)
--volume `pwd`/contrib/keymaps:/usr/share/kvmd/keymaps:ro \ --volume `pwd`/contrib/keymaps:/usr/share/kvmd/keymaps:ro \
--device $(TESTENV_VIDEO):$(TESTENV_VIDEO) \ --device $(TESTENV_VIDEO):$(TESTENV_VIDEO) \
--device $(TESTENV_GPIO):$(TESTENV_GPIO) \ --device $(TESTENV_GPIO):$(TESTENV_GPIO) \
--env KVMD_GPIO_DEVICE_PATH=$(TESTENV_GPIO) \
--env KVMD_SYSFS_PREFIX=/fake_sysfs \ --env KVMD_SYSFS_PREFIX=/fake_sysfs \
--env KVMD_PROCFS_PREFIX=/fake_procfs \ --env KVMD_PROCFS_PREFIX=/fake_procfs \
$(if $(TESTENV_RELAY),--device $(TESTENV_RELAY):$(TESTENV_RELAY),) \ $(if $(TESTENV_RELAY),--device $(TESTENV_RELAY):$(TESTENV_RELAY),) \
@ -121,6 +120,7 @@ run: testenv $(TESTENV_GPIO)
&& cp /testenv/$(if $(P),$(P),$(DEFAULT_PLATFORM)).override.yaml /etc/kvmd/override.yaml \ && cp /testenv/$(if $(P),$(P),$(DEFAULT_PLATFORM)).override.yaml /etc/kvmd/override.yaml \
&& nginx -c /etc/kvmd/nginx/nginx.conf -g 'user http; error_log stderr;' \ && nginx -c /etc/kvmd/nginx/nginx.conf -g 'user http; error_log stderr;' \
&& ln -s $(TESTENV_VIDEO) /dev/kvmd-video \ && ln -s $(TESTENV_VIDEO) /dev/kvmd-video \
&& ln -s $(TESTENV_VIDEO) /dev/kvmd-gpio \
&& $(if $(CMD),$(CMD),python -m kvmd.apps.kvmd --run) \ && $(if $(CMD),$(CMD),python -m kvmd.apps.kvmd --run) \
" "

View File

@ -27,7 +27,5 @@ import os
# XXX: Don't use these variables for any purpose other than testing. # XXX: Don't use these variables for any purpose other than testing.
# It can be removed at any time. # It can be removed at any time.
GPIO_DEVICE_PATH = str(os.getenv("KVMD_GPIO_DEVICE_PATH", "/dev/gpiochip0")).strip()
SYSFS_PREFIX = str(os.getenv("KVMD_SYSFS_PREFIX", "")).strip() SYSFS_PREFIX = str(os.getenv("KVMD_SYSFS_PREFIX", "")).strip()
PROCFS_PREFIX = str(os.getenv("KVMD_PROCFS_PREFIX", "")).strip() PROCFS_PREFIX = str(os.getenv("KVMD_PROCFS_PREFIX", "")).strip()

View File

@ -28,7 +28,6 @@ import gpiod
from ...logging import get_logger from ...logging import get_logger
from ... import env
from ... import aiotools from ... import aiotools
from ... import aiogp from ... import aiogp
@ -37,6 +36,7 @@ from ...yamlconf import Option
from ...validators.basic import valid_bool from ...validators.basic import valid_bool
from ...validators.basic import valid_float_f0 from ...validators.basic import valid_float_f0
from ...validators.basic import valid_float_f01 from ...validators.basic import valid_float_f01
from ...validators.os import valid_abs_path
from ...validators.hw import valid_gpio_pin from ...validators.hw import valid_gpio_pin
from . import AtxIsBusyError from . import AtxIsBusyError
@ -47,6 +47,8 @@ from . import BaseAtx
class Plugin(BaseAtx): # pylint: disable=too-many-instance-attributes class Plugin(BaseAtx): # pylint: disable=too-many-instance-attributes
def __init__( # pylint: disable=too-many-arguments,super-init-not-called def __init__( # pylint: disable=too-many-arguments,super-init-not-called
self, self,
device_path: str,
power_led_pin: int, power_led_pin: int,
power_led_inverted: bool, power_led_inverted: bool,
power_led_debounce: float, power_led_debounce: float,
@ -61,6 +63,8 @@ class Plugin(BaseAtx): # pylint: disable=too-many-instance-attributes
long_click_delay: float, long_click_delay: float,
) -> None: ) -> None:
self.__device_path = device_path
self.__power_led_pin = power_led_pin self.__power_led_pin = power_led_pin
self.__hdd_led_pin = hdd_led_pin self.__hdd_led_pin = hdd_led_pin
self.__power_switch_pin = power_switch_pin self.__power_switch_pin = power_switch_pin
@ -77,7 +81,7 @@ class Plugin(BaseAtx): # pylint: disable=too-many-instance-attributes
self.__reset_switch_line: Optional[gpiod.Line] = None self.__reset_switch_line: Optional[gpiod.Line] = None
self.__reader = aiogp.AioReader( self.__reader = aiogp.AioReader(
path=env.GPIO_DEVICE_PATH, path=self.__device_path,
consumer="kvmd::atx::leds", consumer="kvmd::atx::leds",
pins={ pins={
power_led_pin: aiogp.AioReaderPinParams(power_led_inverted, power_led_debounce), power_led_pin: aiogp.AioReaderPinParams(power_led_inverted, power_led_debounce),
@ -89,6 +93,8 @@ class Plugin(BaseAtx): # pylint: disable=too-many-instance-attributes
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> Dict:
return { return {
"device": Option("/dev/gpiochip0", type=valid_abs_path, unpack_as="device_path"),
"power_led_pin": Option(-1, type=valid_gpio_pin), "power_led_pin": Option(-1, type=valid_gpio_pin),
"power_led_inverted": Option(False, type=valid_bool), "power_led_inverted": Option(False, type=valid_bool),
"power_led_debounce": Option(0.1, type=valid_float_f0), "power_led_debounce": Option(0.1, type=valid_float_f0),
@ -108,7 +114,7 @@ class Plugin(BaseAtx): # pylint: disable=too-many-instance-attributes
assert self.__power_switch_line is None assert self.__power_switch_line is None
assert self.__reset_switch_line is None assert self.__reset_switch_line is None
self.__chip = gpiod.Chip(env.GPIO_DEVICE_PATH) self.__chip = gpiod.Chip(self.__device_path)
self.__power_switch_line = self.__chip.get_line(self.__power_switch_pin) self.__power_switch_line = self.__chip.get_line(self.__power_switch_pin)
self.__power_switch_line.request("kvmd::atx::power_switch", gpiod.LINE_REQ_DIR_OUT, default_vals=[0]) self.__power_switch_line.request("kvmd::atx::power_switch", gpiod.LINE_REQ_DIR_OUT, default_vals=[0])

View File

@ -46,6 +46,7 @@ from ....validators.basic import valid_bool
from ....validators.basic import valid_int_f0 from ....validators.basic import valid_int_f0
from ....validators.basic import valid_int_f1 from ....validators.basic import valid_int_f1
from ....validators.basic import valid_float_f01 from ....validators.basic import valid_float_f01
from ....validators.os import valid_abs_path
from ....validators.hw import valid_gpio_pin_optional from ....validators.hw import valid_gpio_pin_optional
from .. import BaseHid from .. import BaseHid
@ -113,6 +114,7 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
self, self,
phy: BasePhy, phy: BasePhy,
gpio_device_path: str,
reset_pin: int, reset_pin: int,
reset_inverted: bool, reset_inverted: bool,
reset_delay: float, reset_delay: float,
@ -133,7 +135,7 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
self.__noop = noop self.__noop = noop
self.__phy = phy self.__phy = phy
self.__gpio = Gpio(reset_pin, reset_inverted, reset_delay) self.__gpio = Gpio(gpio_device_path, reset_pin, reset_inverted, reset_delay)
self.__events_queue: "multiprocessing.Queue[BaseEvent]" = multiprocessing.Queue() self.__events_queue: "multiprocessing.Queue[BaseEvent]" = multiprocessing.Queue()
@ -149,6 +151,7 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> Dict:
return { return {
"gpio_device": Option("/dev/gpiochip0", type=valid_abs_path, unpack_as="gpio_device_path"),
"reset_pin": Option(-1, type=valid_gpio_pin_optional), "reset_pin": Option(-1, type=valid_gpio_pin_optional),
"reset_inverted": Option(False, type=valid_bool), "reset_inverted": Option(False, type=valid_bool),
"reset_delay": Option(0.1, type=valid_float_f01), "reset_delay": Option(0.1, type=valid_float_f01),

View File

@ -30,18 +30,18 @@ import gpiod
from ....logging import get_logger from ....logging import get_logger
from .... import env
# ===== # =====
class Gpio: class Gpio:
def __init__( def __init__(
self, self,
device_path: str,
reset_pin: int, reset_pin: int,
reset_inverted: bool, reset_inverted: bool,
reset_delay: float, reset_delay: float,
) -> None: ) -> None:
self.__device_path = device_path
self.__reset_pin = reset_pin self.__reset_pin = reset_pin
self.__reset_inverted = reset_inverted self.__reset_inverted = reset_inverted
self.__reset_delay = reset_delay self.__reset_delay = reset_delay
@ -53,7 +53,7 @@ class Gpio:
if self.__reset_pin >= 0: if self.__reset_pin >= 0:
assert self.__chip is None assert self.__chip is None
assert self.__reset_line is None assert self.__reset_line is None
self.__chip = gpiod.Chip(env.GPIO_DEVICE_PATH) self.__chip = gpiod.Chip(self.__device_path)
self.__reset_line = self.__chip.get_line(self.__reset_pin) self.__reset_line = self.__chip.get_line(self.__reset_pin)
self.__reset_line.request("kvmd::hid::reset", gpiod.LINE_REQ_DIR_OUT, default_vals=[int(self.__reset_inverted)]) self.__reset_line.request("kvmd::hid::reset", gpiod.LINE_REQ_DIR_OUT, default_vals=[int(self.__reset_inverted)])

View File

@ -44,8 +44,6 @@ from ...validators.basic import valid_int_f1
from ...validators.basic import valid_float_f01 from ...validators.basic import valid_float_f01
from ...validators.hw import valid_gpio_pin_optional from ...validators.hw import valid_gpio_pin_optional
from ... import env
from ._mcu import BasePhyConnection from ._mcu import BasePhyConnection
from ._mcu import BasePhy from ._mcu import BasePhy
from ._mcu import BaseMcuHid from ._mcu import BaseMcuHid
@ -100,6 +98,7 @@ class _SpiPhyConnection(BasePhyConnection):
class _SpiPhy(BasePhy): # pylint: disable=too-many-instance-attributes class _SpiPhy(BasePhy): # pylint: disable=too-many-instance-attributes
def __init__( def __init__(
self, self,
gpio_device_path: str,
bus: int, bus: int,
chip: int, chip: int,
hw_cs: bool, hw_cs: bool,
@ -109,6 +108,7 @@ class _SpiPhy(BasePhy): # pylint: disable=too-many-instance-attributes
read_timeout: float, read_timeout: float,
) -> None: ) -> None:
self.__gpio_device_path = gpio_device_path
self.__bus = bus self.__bus = bus
self.__chip = chip self.__chip = chip
self.__hw_cs = hw_cs self.__hw_cs = hw_cs
@ -145,7 +145,7 @@ class _SpiPhy(BasePhy): # pylint: disable=too-many-instance-attributes
@contextlib.contextmanager @contextlib.contextmanager
def __sw_cs_connected(self) -> Generator[Optional[gpiod.Line], None, None]: def __sw_cs_connected(self) -> Generator[Optional[gpiod.Line], None, None]:
if self.__sw_cs_pin > 0: if self.__sw_cs_pin > 0:
with contextlib.closing(gpiod.Chip(env.GPIO_DEVICE_PATH)) as chip: with contextlib.closing(gpiod.Chip(self.__gpio_device_path)) as chip:
line = chip.get_line(self.__sw_cs_pin) line = chip.get_line(self.__sw_cs_pin)
line.request("kvmd::hid::sw_cs", gpiod.LINE_REQ_DIR_OUT, default_vals=[1]) line.request("kvmd::hid::sw_cs", gpiod.LINE_REQ_DIR_OUT, default_vals=[1])
yield line yield line
@ -157,6 +157,7 @@ class _SpiPhy(BasePhy): # pylint: disable=too-many-instance-attributes
class Plugin(BaseMcuHid): class Plugin(BaseMcuHid):
def __init__(self, **kwargs: Any) -> None: def __init__(self, **kwargs: Any) -> None:
phy_kwargs: Dict = {key: kwargs.pop(key) for key in self.__get_phy_options()} phy_kwargs: Dict = {key: kwargs.pop(key) for key in self.__get_phy_options()}
phy_kwargs["gpio_device_path"] = kwargs["gpio_device_path"]
super().__init__(phy=_SpiPhy(**phy_kwargs), **kwargs) super().__init__(phy=_SpiPhy(**phy_kwargs), **kwargs)
@classmethod @classmethod

View File

@ -62,6 +62,7 @@ from .drive import DeviceInfo
class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
def __init__( # pylint: disable=super-init-not-called def __init__( # pylint: disable=super-init-not-called
self, self,
gpio_device_path: str,
target_pin: int, target_pin: int,
reset_pin: int, reset_pin: int,
@ -75,7 +76,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
self.__init_delay = init_delay self.__init_delay = init_delay
self.__init_retries = init_retries self.__init_retries = init_retries
self.__gpio = Gpio(target_pin, reset_pin, reset_delay) self.__gpio = Gpio(gpio_device_path, target_pin, reset_pin, reset_delay)
self.__device_info: Optional[DeviceInfo] = None self.__device_info: Optional[DeviceInfo] = None
self.__connected = False self.__connected = False
@ -97,8 +98,9 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> Dict:
return { return {
"target_pin": Option(-1, type=valid_gpio_pin), "gpio_device": Option("/dev/gpiochip0", type=valid_abs_path, unpack_as="gpio_device_path"),
"reset_pin": Option(-1, type=valid_gpio_pin), "target_pin": Option(-1, type=valid_gpio_pin),
"reset_pin": Option(-1, type=valid_gpio_pin),
"device": Option("", type=valid_abs_path, unpack_as="device_path"), "device": Option("", type=valid_abs_path, unpack_as="device_path"),
"init_delay": Option(1.0, type=valid_float_f01), "init_delay": Option(1.0, type=valid_float_f01),

View File

@ -24,7 +24,6 @@ from typing import Optional
import gpiod import gpiod
from .... import env
from .... import aiogp from .... import aiogp
@ -32,11 +31,13 @@ from .... import aiogp
class Gpio: class Gpio:
def __init__( def __init__(
self, self,
device_path: str,
target_pin: int, target_pin: int,
reset_pin: int, reset_pin: int,
reset_delay: float, reset_delay: float,
) -> None: ) -> None:
self.__device_path = device_path
self.__target_pin = target_pin self.__target_pin = target_pin
self.__reset_pin = reset_pin self.__reset_pin = reset_pin
self.__reset_delay = reset_delay self.__reset_delay = reset_delay
@ -50,7 +51,7 @@ class Gpio:
assert self.__target_line is None assert self.__target_line is None
assert self.__reset_line is None assert self.__reset_line is None
self.__chip = gpiod.Chip(env.GPIO_DEVICE_PATH) self.__chip = gpiod.Chip(self.__device_path)
self.__target_line = self.__chip.get_line(self.__target_pin) self.__target_line = self.__chip.get_line(self.__target_pin)
self.__target_line.request("kvmd::msd::target", gpiod.LINE_REQ_DIR_OUT, default_vals=[0]) self.__target_line.request("kvmd::msd::target", gpiod.LINE_REQ_DIR_OUT, default_vals=[0])

View File

@ -25,10 +25,13 @@ from typing import Optional
import gpiod import gpiod
from ... import env
from ... import aiotools from ... import aiotools
from ... import aiogp from ... import aiogp
from ...yamlconf import Option
from ...validators.os import valid_abs_path
from . import BaseUserGpioDriver from . import BaseUserGpioDriver
@ -38,10 +41,14 @@ class Plugin(BaseUserGpioDriver):
self, self,
instance_name: str, instance_name: str,
notifier: aiotools.AioNotifier, notifier: aiotools.AioNotifier,
device_path: str,
) -> None: ) -> None:
super().__init__(instance_name, notifier) super().__init__(instance_name, notifier)
self.__device_path = device_path
self.__input_pins: Dict[int, aiogp.AioReaderPinParams] = {} self.__input_pins: Dict[int, aiogp.AioReaderPinParams] = {}
self.__output_pins: Dict[int, Optional[bool]] = {} self.__output_pins: Dict[int, Optional[bool]] = {}
@ -50,6 +57,12 @@ class Plugin(BaseUserGpioDriver):
self.__chip: Optional[gpiod.Chip] = None self.__chip: Optional[gpiod.Chip] = None
self.__output_lines: Dict[int, gpiod.Line] = {} self.__output_lines: Dict[int, gpiod.Line] = {}
@classmethod
def get_plugin_options(cls) -> Dict:
return {
"device": Option("/dev/gpiochip0", type=valid_abs_path, unpack_as="device_path"),
}
def register_input(self, pin: int, debounce: float) -> None: def register_input(self, pin: int, debounce: float) -> None:
self.__input_pins[pin] = aiogp.AioReaderPinParams(False, debounce) self.__input_pins[pin] = aiogp.AioReaderPinParams(False, debounce)
@ -59,13 +72,13 @@ class Plugin(BaseUserGpioDriver):
def prepare(self) -> None: def prepare(self) -> None:
assert self.__reader is None assert self.__reader is None
self.__reader = aiogp.AioReader( self.__reader = aiogp.AioReader(
path=env.GPIO_DEVICE_PATH, path=self.__device_path,
consumer="kvmd::gpio::inputs", consumer="kvmd::gpio::inputs",
pins=self.__input_pins, pins=self.__input_pins,
notifier=self._notifier, notifier=self._notifier,
) )
self.__chip = gpiod.Chip(env.GPIO_DEVICE_PATH) self.__chip = gpiod.Chip(self.__device_path)
for (pin, initial) in self.__output_pins.items(): for (pin, initial) in self.__output_pins.items():
line = self.__chip.get_line(pin) line = self.__chip.get_line(pin)
line.request("kvmd::gpio::outputs", gpiod.LINE_REQ_DIR_OUT, default_vals=[int(initial or False)]) line.request("kvmd::gpio::outputs", gpiod.LINE_REQ_DIR_OUT, default_vals=[int(initial or False)])

View File

@ -2,6 +2,9 @@ kvmd:
server: server:
unix_mode: 0666 unix_mode: 0666
atx:
device: /dev/kvmd-gpio
hid: hid:
keyboard: keyboard:
device: /dev/null device: /dev/null
@ -33,6 +36,8 @@ kvmd:
gpio: gpio:
drivers: drivers:
__gpio__:
device: /dev/kvmd-gpio
relay: relay:
type: hidrelay type: hidrelay
device: /dev/hidraw0 device: /dev/hidraw0

View File

@ -2,6 +2,9 @@ kvmd:
server: server:
unix_mode: 0666 unix_mode: 0666
atx:
device: /dev/kvmd-gpio
hid: hid:
keyboard: keyboard:
device: /dev/null device: /dev/null