vsergeev / python-periphery

A pure Python 2/3 library for peripheral I/O (GPIO, LED, PWM, SPI, I2C, MMIO, Serial) in Linux.
MIT License
518 stars 137 forks source link

Add Userspace I/O class? #63

Open Livius90 opened 11 months ago

Livius90 commented 11 months ago

Can you consider to implement generic uio in your python periphery? As i see your MMIO implementation can serve the reg mapping for a /dev/uioX, only the IRQ waiting which is needed to implemented for this new class.

See this examples:

Livius90 commented 11 months ago

@vsergeev What do you think, is it fine?

import os
from periphery import MMIO

def get_uio_dev(uio_name):
    for dev in os.listdir("/sys/class/uio"):
        with open("/sys/class/uio/" + dev + "/name", "r") as f:
            name = f.readline().strip()
        if name == uio_name:
            return "/dev/" + dev
    return None

def get_uio_size(uio_dev):
    size = None
    with open("/sys/class/uio/" + uio_dev + "/maps/map0/size", "r") as f:
        size = int(f.readline().strip(), 16)
    return size

class UIO(MMIO):
    def __init__(self, name):
        uio_dev = get_uio_dev(name)
        uio_size = get_uio_size(uio_dev)
        super().__init__(0x00, uio_size, uio_dev)
        # _fdesc should be opened in MMIO's __init__() instead
        self._fdesc = os.open(uio_dev, os.O_RDWR | os.O_SYNC)

    def enable_irq(self):
        os.write(self._fdesc, bytes([1, 0, 0, 0]))

    def disable_irq(self):
        os.write(self._fdesc, bytes([0, 0, 0, 0]))

    def wait_irq(self):
        os.read(self._fdesc, 4)

    def close(self):
        os.close(self._fdesc)
        super().close()
vsergeev commented 10 months ago

It's a good idea -- could use a few more getters with the UIO info. One thing to decide is whether or not to abstract away the interrupt counts -- e.g. should wait_irq() return the raw interrupt counter or the number of interrupts that occurred since the last wait.

Livius90 commented 10 months ago

It's a good idea -- could use a few more getters with the UIO info. One thing to decide is whether or not to abstract away the interrupt counts -- e.g. should wait_irq() return the raw interrupt counter or the number of interrupts that occurred since the last wait.

What getter you think is need more?

Implementation for return missed interrupts: https://www.kernel.org/doc/html/v4.17/driver-api/uio-howto.html#how-uio-works

import os
from periphery import MMIO

def get_uio_dev(uio_name):
    for dev in os.listdir("/sys/class/uio"):
        with open("/sys/class/uio/" + dev + "/name", "r") as f:
            name = f.readline().strip()
        if name == uio_name:
            return "/dev/" + dev
    return None

def get_uio_size(uio_dev):
    size = None
    with open("/sys/class/uio/" + uio_dev + "/maps/map0/size", "r") as f:
        size = int(f.readline().strip(), 16)
    return size

class UIO(MMIO):
    def __init__(self, name):
        uio_dev = get_uio_dev(name)
        uio_size = get_uio_size(uio_dev)
        super().__init__(0x00, uio_size, uio_dev)
        # _fdesc should be opened in MMIO's __init__() instead
        self._fdesc = os.open(uio_dev, os.O_RDWR | os.O_SYNC)
        self.irq_total_count = 0
        self._irq_last_count = 0

    def enable_irq(self):
        os.write(self._fdesc, bytes([1, 0, 0, 0]))

    def disable_irq(self):
        os.write(self._fdesc, bytes([0, 0, 0, 0]))

    def wait_irq(self):
        self.irq_total_count = os.read(self._fdesc, 4)
        irq_count = self.irq_total_count - self._irq_last_count
        self._irq_last_count = self.irq_total_count

        # return 1 or number of missed interrupts
        return irq_count

    def close(self):
        os.close(self._fdesc)
        super().close()