ryuz / jelly

Original FPGA platform
http://ryuz.my.coocan.jp/jelly/index.html
MIT License
51 stars 14 forks source link

Proposal for polymorphism of "new" function in rust/mem_access/src/uio_accessor.rs #2

Closed ikwzm closed 2 years ago

ikwzm commented 2 years ago

uio_accessor の新しく生成する関数 new の引数を、 uio番号 または デバイス名 のどちらでも指定できるように new 関数をポリモーフィックにしてはどうでしょう?

#![allow(dead_code)]

use super::*;
use delegate::delegate;
use std::boxed::Box;
use std::error::Error;
use std::format;
use std::fs::File;
use std::io::Read;
use std::string::String;
use std::string::ToString;
use thiserror::Error;

#[derive(Debug, Error)]
enum UioAccessorError {
    #[error("UioError: {0}")]
    UioError(String),
}

fn read_file_to_string(path: String) -> Result<String, Box<dyn Error>> {
    let mut file = File::open(path)?;
    let mut buf = String::new();
    file.read_to_string(&mut buf)?;
    Ok(buf)
}

pub struct UioRegion {
    mmap_region: MmapRegion,
    phys_addr: usize,
}

pub trait NewUioRegion<T> {
    fn new(id:T) -> Result<UioRegion, Box<dyn Error>>;
}

impl NewUioRegion<usize> for UioRegion {
    fn new(id: usize) -> Result<UioRegion, Box<dyn Error>> {
        let phys_addr = Self::read_phys_addr(id)?;
        let size      = Self::read_size(id)?;
        let fname     = format!("/dev/uio{}", id);
        Ok(UioRegion {
            mmap_region: MmapRegion::new(fname, size)?,
            phys_addr: phys_addr,
        })
    }
}

impl NewUioRegion<String> for UioRegion {
    fn new(id: String) -> Result<UioRegion, Box<dyn Error>> {
        let device_name: &str = &id;
        return Self::new(device_name);
    }
}

impl NewUioRegion<&str> for UioRegion {
    fn new(id: &str) -> Result<UioRegion, Box<dyn Error>> {
        for path in std::fs::read_dir("/sys/class/uio/")? {
            let uio_num: usize = path
                .unwrap()
                .path()
                .display()
                .to_string()
                .replacen("/sys/class/uio/uio", "", 1)
                .parse()
                .unwrap();
            let dev_name = UioRegion::read_name(uio_num)?;
            if dev_name == id {
                return Self::new(uio_num);
            }
        }
        Err(Box::new(UioAccessorError::UioError(
            "device not found".to_string(),
        )))
    }
}

impl UioRegion {
    pub fn phys_addr(&self) -> usize {
        self.phys_addr
    }

    pub fn set_irq_enable(&mut self, enable: bool) -> Result<(), Box<dyn Error>> {
        let data: [u8; 4] = unsafe { std::mem::transmute(if enable { 1u32 } else { 0u32 }) };
        self.mmap_region.write(&data)?;
        Ok(())
    }

    pub fn wait_irq(&mut self) -> Result<(), Box<dyn Error>> {
        let mut buf: [u8; 4] = [0; 4];
        self.mmap_region.read(&mut buf)?;
        Ok(())
    }

    pub fn read_name(uio_num: usize) -> Result<String, Box<dyn Error>> {
        let fname = format!("/sys/class/uio/uio{}/name", uio_num);
        Ok(read_file_to_string(fname)?.trim().to_string())
    }

    pub fn read_size(uio_num: usize) -> Result<usize, Box<dyn Error>> {
        let fname = format!("/sys/class/uio/uio{}/maps/map0/size", uio_num);
        Ok(usize::from_str_radix(
            &read_file_to_string(fname)?.trim()[2..],
            16,
        )?)
    }

    pub fn read_phys_addr(uio_num: usize) -> Result<usize, Box<dyn Error>> {
        let fname = format!("/sys/class/uio/uio{}/maps/map0/addr", uio_num);
        Ok(usize::from_str_radix(
            &read_file_to_string(fname)?.trim()[2..],
            16,
        )?)
    }
}

impl MemRegion for UioRegion {
    fn subclone(&self, offset: usize, size: usize) -> Self {
        UioRegion {
            mmap_region: self.mmap_region.subclone(offset, size),
            phys_addr: self.phys_addr + offset,
        }
    }

    delegate! {
        to self.mmap_region {
            fn addr(&self) -> usize;
            fn size(&self) -> usize;
        }
    }
}

pub struct UioAccessor<U> {
    mem_accessor: MemAccessor<UioRegion, U>,
}

impl<U> From<UioAccessor<U>> for MemAccessor<UioRegion, U> {
    fn from(from: UioAccessor<U>) -> MemAccessor<UioRegion, U> {
        from.mem_accessor
    }
}

pub trait NewUioAccessor<T,U> {
    fn new(id: T) -> Result<UioAccessor<U>, Box<dyn Error>>;
}

impl<U> NewUioAccessor<usize,U> for UioAccessor<U> {
    fn new(id: usize) -> Result<UioAccessor<U>, Box<dyn Error>> {
        Ok(Self {
            mem_accessor: MemAccessor::<UioRegion, U>::new(UioRegion::new(id)?),
        })
    }
}

impl<U> NewUioAccessor<String,U> for UioAccessor<U> {
    fn new(id: String) -> Result<UioAccessor<U>, Box<dyn Error>> {
        Ok(Self {
            mem_accessor: MemAccessor::<UioRegion, U>::new(UioRegion::new(id)?),
        })
    }
}

impl<U> NewUioAccessor<&str,U> for UioAccessor<U> {
    fn new(id: &str) -> Result<UioAccessor<U>, Box<dyn Error>> {
        Ok(Self {
            mem_accessor: MemAccessor::<UioRegion, U>::new(UioRegion::new(id)?),
        })
    }
}

impl<U> UioAccessor<U> {
    pub fn subclone_<NewU>(&self, offset: usize, size: usize) -> UioAccessor<NewU> {
        UioAccessor::<NewU> {
            mem_accessor: MemAccessor::<UioRegion, NewU>::new(
                self.mem_accessor.region().subclone(offset, size),
            ),
        }
    }

    pub fn subclone(&self, offset: usize, size: usize) -> UioAccessor<U> {
        self.subclone_::<U>(offset, size)
    }

    pub fn subclone8(&self, offset: usize, size: usize) -> UioAccessor<u8> {
        self.subclone_::<u8>(offset, size)
    }

    pub fn subclone16(&self, offset: usize, size: usize) -> UioAccessor<u16> {
        self.subclone_::<u16>(offset, size)
    }

    pub fn subclone32(&self, offset: usize, size: usize) -> UioAccessor<u32> {
        self.subclone_::<u32>(offset, size)
    }

    pub fn subclone64(&self, offset: usize, size: usize) -> UioAccessor<u64> {
        self.subclone_::<u64>(offset, size)
    }

    delegate! {
        to self.mem_accessor.region() {
            pub fn addr(&self) -> usize;
            pub fn size(&self) -> usize;
            pub fn phys_addr(&self) -> usize;
        }
        to self.mem_accessor.region_mut() {
            pub fn set_irq_enable(&mut self, enable: bool) -> Result<(), Box<dyn Error>>;
            pub fn wait_irq(&mut self) -> Result<(), Box<dyn Error>>;
        }
    }
}

impl<U> MemAccess for UioAccessor<U> {
    fn reg_size() -> usize {
        core::mem::size_of::<U>()
    }

    delegate! {
        to self.mem_accessor {
            fn addr(&self) -> usize;
            fn size(&self) -> usize;

            unsafe fn copy_to<V>(&self, src_adr: usize, dst_ptr: *mut V, count: usize);
            unsafe fn copy_from<V>(&self, src_ptr: *const V, dst_adr: usize, count: usize);

            unsafe fn write_mem_<V>(&self, offset: usize, data: V);
            unsafe fn read_mem_<V>(&self, offset: usize) -> V;
            unsafe fn write_reg_<V>(&self, reg: usize, data: V);
            unsafe fn read_reg_<V>(&self, reg: usize) -> V;

            unsafe fn write_mem(&self, offset: usize, data: usize);
            unsafe fn write_mem8(&self, offset: usize, data: u8);
            unsafe fn write_mem16(&self, offset: usize, data: u16);
            unsafe fn write_mem32(&self, offset: usize, data: u32);
            unsafe fn write_mem64(&self, offset: usize, data: u64);
            unsafe fn read_mem(&self, offset: usize) -> usize;
            unsafe fn read_mem8(&self, offset: usize) -> u8;
            unsafe fn read_mem16(&self, offset: usize) -> u16;
            unsafe fn read_mem32(&self, offset: usize) -> u32;
            unsafe fn read_mem64(&self, offset: usize) -> u64;

            unsafe fn write_reg(&self, reg: usize, data: usize);
            unsafe fn write_reg8(&self, reg: usize, data: u8);
            unsafe fn write_reg16(&self, reg: usize, data: u16);
            unsafe fn write_reg32(&self, reg: usize, data: u32);
            unsafe fn write_reg64(&self, reg: usize, data: u64);
            unsafe fn read_reg(&self, reg: usize) -> usize;
            unsafe fn read_reg8(&self, reg: usize) -> u8;
            unsafe fn read_reg16(&self, reg: usize) -> u16;
            unsafe fn read_reg32(&self, reg: usize) -> u32;
            unsafe fn read_reg64(&self, reg: usize) -> u64;
        }
    }
}

同様に udmabuf_accessor の new にもデバイス名を指定できるようにしてほしいかな。デバイスツリーだと、u-dma-buf のデバイス名は自由に設定できるので、できれば文字列で指定できた方がいいと思います。

#![allow(dead_code)]

use super::*;
use delegate::delegate;
use std::boxed::Box;
use std::error::Error;
use std::format;
use std::fs::File;
use std::io::Read;
use std::string::String;

const O_SYNC: i32 = 0x101000;

fn read_file_to_string(path: String) -> Result<String, Box<dyn Error>> {
    let mut file = File::open(path)?;
    let mut buf = String::new();
    file.read_to_string(&mut buf)?;
    Ok(buf)
}

pub struct UdmabufRegion {
    device_name: String,
    mmap_region: MmapRegion,
    phys_addr: usize,
}

pub trait NewUdmabufRegion<T> {
    fn new(id:T, cache_enable: bool) -> Result<UdmabufRegion, Box<dyn Error>>;
}

impl NewUdmabufRegion<usize> for UdmabufRegion {
    fn new(id: usize, cache_enable: bool) -> Result<UdmabufRegion, Box<dyn Error>> {
        let device_name: String = format!("udmabuf{}", id);
        return Self::new(device_name, cache_enable);
    }
}

impl NewUdmabufRegion<String> for UdmabufRegion {
    fn new(id: String, cache_enable: bool) -> Result<UdmabufRegion, Box<dyn Error>> {
        let device_name: &str = &id;
        return Self::new(device_name, cache_enable);
    }
}

impl NewUdmabufRegion<&str> for UdmabufRegion {
    fn new(id: &str, cache_enable: bool) -> Result<UdmabufRegion, Box<dyn Error>> {
        let phys_addr   = Self::read_phys_addr(id)?;
        let size        = Self::read_size(id)?;
        let fname       = format!("/dev/{}", id);
        let mmap_region =
            MmapRegion::new_with_flag(fname, size, if cache_enable { 0 } else { O_SYNC })?;

        Ok(Self {
            device_name: String::from(id),
            mmap_region: mmap_region,
            phys_addr:   phys_addr,
        })
    }
}

impl UdmabufRegion {
    pub fn phys_addr(&self) -> usize {
        self.phys_addr
    }

    pub fn read_phys_addr(device_name: &str) -> Result<usize, Box<dyn Error>> {
        let fname = format!("/sys/class/u-dma-buf/{}/phys_addr", device_name);
        Ok(usize::from_str_radix(
            &read_file_to_string(fname)?.trim()[2..],
            16,
        )?)
    }

    pub fn read_size(device_name: &str) -> Result<usize, Box<dyn Error>> {
        let fname = format!("/sys/class/u-dma-buf/{}/size", device_name);
        Ok(read_file_to_string(fname)?.trim().parse()?)
    }
}

impl MemRegion for UdmabufRegion {
    fn subclone(&self, offset: usize, size: usize) -> Self {
        let device_name: &str = &self.device_name;
        UdmabufRegion {
            device_name: String::from(device_name),
            mmap_region: self.mmap_region.subclone(offset, size),
            phys_addr: self.phys_addr + offset,
        }
    }

    delegate! {
        to self.mmap_region {
            fn addr(&self) -> usize;
            fn size(&self) -> usize;
        }
    }
}

impl Clone for UdmabufRegion {
    fn clone(&self) -> Self {
        self.subclone(0, 0)
    }
}

pub struct UdmabufAccessor<U> {
    mem_accessor: MemAccessor<UdmabufRegion, U>,
}

impl<U> From<UdmabufAccessor<U>> for MemAccessor<UdmabufRegion, U> {
    fn from(from: UdmabufAccessor<U>) -> MemAccessor<UdmabufRegion, U> {
        from.mem_accessor
    }
}

pub trait NewUdmabufAccessor<T,U> {
    fn new(device_name: T, cache_enable: bool) -> Result<UdmabufAccessor<U>, Box<dyn Error>>;
}

impl<U> NewUdmabufAccessor<usize,U> for UdmabufAccessor<U> {
    fn new(device_name: usize, cache_enable: bool) -> Result<UdmabufAccessor<U>, Box<dyn Error>> {
        Ok(UdmabufAccessor {
            mem_accessor: MemAccessor::<UdmabufRegion, U>::new(UdmabufRegion::new(
                device_name ,
                cache_enable,
            )?),
        })
    }
}

impl<U> NewUdmabufAccessor<String,U> for UdmabufAccessor<U> {
    fn new(device_name: String, cache_enable: bool) -> Result<Self, Box<dyn Error>> {
        Ok(Self {
            mem_accessor: MemAccessor::<UdmabufRegion, U>::new(UdmabufRegion::new(
                device_name ,
                cache_enable,
            )?),
        })
    }
}

impl<U> NewUdmabufAccessor<&str,U> for UdmabufAccessor<U> {
    fn new(device_name: &str, cache_enable: bool) -> Result<Self, Box<dyn Error>> {
        Ok(Self {
            mem_accessor: MemAccessor::<UdmabufRegion, U>::new(UdmabufRegion::new(
                device_name ,
                cache_enable,
            )?),
        })
    }
}

impl<U> UdmabufAccessor<U> {
    pub fn subclone_<NewU>(&self, offset: usize, size: usize) -> UdmabufAccessor<NewU> {
        UdmabufAccessor::<NewU> {
            mem_accessor: MemAccessor::<UdmabufRegion, NewU>::new(
                self.mem_accessor.region().subclone(offset, size),
            ),
        }
    }

    pub fn subclone(&self, offset: usize, size: usize) -> UdmabufAccessor<U> {
        self.subclone_::<U>(offset, size)
    }

    pub fn subclone8(&self, offset: usize, size: usize) -> UdmabufAccessor<u8> {
        self.subclone_::<u8>(offset, size)
    }

    pub fn subclone16(&self, offset: usize, size: usize) -> UdmabufAccessor<u16> {
        self.subclone_::<u16>(offset, size)
    }

    pub fn subclone32(&self, offset: usize, size: usize) -> UdmabufAccessor<u32> {
        self.subclone_::<u32>(offset, size)
    }

    pub fn subclone64(&self, offset: usize, size: usize) -> UdmabufAccessor<u64> {
        self.subclone_::<u64>(offset, size)
    }

    delegate! {
        to self.mem_accessor.region() {
            pub fn addr(&self) -> usize;
            pub fn size(&self) -> usize;
            pub fn phys_addr(&self) -> usize;
        }
    }
}

impl<U> Clone for UdmabufAccessor<U> {
    fn clone(&self) -> Self {
        self.subclone(0, 0)
    }
}

impl<U> MemAccess for UdmabufAccessor<U> {
    fn reg_size() -> usize {
        core::mem::size_of::<U>()
    }

    delegate! {
        to self.mem_accessor {
            fn addr(&self) -> usize;
            fn size(&self) -> usize;

            unsafe fn copy_to<V>(&self, src_adr: usize, dst_ptr: *mut V, count: usize);
            unsafe fn copy_from<V>(&self, src_ptr: *const V, dst_adr: usize, count: usize);

            unsafe fn write_mem_<V>(&self, offset: usize, data: V);
            unsafe fn read_mem_<V>(&self, offset: usize) -> V;
            unsafe fn write_reg_<V>(&self, reg: usize, data: V);
            unsafe fn read_reg_<V>(&self, reg: usize) -> V;

            unsafe fn write_mem(&self, offset: usize, data: usize);
            unsafe fn write_mem8(&self, offset: usize, data: u8);
            unsafe fn write_mem16(&self, offset: usize, data: u16);
            unsafe fn write_mem32(&self, offset: usize, data: u32);
            unsafe fn write_mem64(&self, offset: usize, data: u64);
            unsafe fn read_mem(&self, offset: usize) -> usize;
            unsafe fn read_mem8(&self, offset: usize) -> u8;
            unsafe fn read_mem16(&self, offset: usize) -> u16;
            unsafe fn read_mem32(&self, offset: usize) -> u32;
            unsafe fn read_mem64(&self, offset: usize) -> u64;

            unsafe fn write_reg(&self, reg: usize, data: usize);
            unsafe fn write_reg8(&self, reg: usize, data: u8);
            unsafe fn write_reg16(&self, reg: usize, data: u16);
            unsafe fn write_reg32(&self, reg: usize, data: u32);
            unsafe fn write_reg64(&self, reg: usize, data: u64);
            unsafe fn read_reg(&self, reg: usize) -> usize;
            unsafe fn read_reg8(&self, reg: usize) -> u8;
            unsafe fn read_reg16(&self, reg: usize) -> u16;
            unsafe fn read_reg32(&self, reg: usize) -> u32;
            unsafe fn read_reg64(&self, reg: usize) -> u64;
        }
    }
}
ryuz commented 2 years ago

有難うございます。本家からコメント頂き大変光栄です。 Rust初心者なのでこのあたりの流儀がよくわかっておらず恐縮です。 uio の方はネーミングまずかった気はしておりますが、一応 new_from_name という名前でデバイス名からの生成は作っております(命名は from_name とかの方が良かったのかもしれませんが)。 https://github.com/ryuz/jelly/blob/master/rust/mem_access/src/uio_accessor.rs C++であればコンストラクタの多重定義(オーバーロード)を使うところですが、Rust 的には多重定義は使わずに別名関数とするのが流儀かと思ったのですが、ポリモーフィックにするのとどちらがトレンドなのかとかわかりますでしょうか?

udmabuf の方のデバイス名対応はできていませんでしたので是非検討させて頂きたいと思います。

ikwzm commented 2 years ago

すみません。実は私も今回初めて Rust を触ったので、ポリモーフィックがトレンドなのかわからないです(汗。

ryuz commented 2 years ago

有難うございます。初心者があまり難しいことをすると嵌りそうなので、一旦難しいことはせずにデバイス名での生成だけ用意させていただいて、もう少しスキルアップしたら見直す方向で考えてみたいと思います。

u-dma-buf のデバイス名は自由に設定できるので

ということを知らないまま使っていたので大変参考になりました。

ryuz commented 2 years ago

コード読ませていただいて、やろうとされていることは trait を使った new のオーバーロードかと理解したのですがあっておりますでしょうか?

今少し調べているのですが、 https://qiita.com/muumu/items/11e736612939a53699e9 にて 「Rustにはコンストラクタが無いのでコンストラクタのオーバーロードも無く、関数名を書き分けることが推奨されている」との記述を見つけ、元ネタを探し中です。 なるべく Rust の流儀に合わせたいなとは思っているのですが、流儀自体も移り変わりがあろうかと思いますので、最新動向としてポリモーフィックがトレンドになってきているようなら trait 追加していくことも考えたいと思います。

ryuz commented 2 years ago

結局なのですが、番号指定ではなくデバイス名での指定が自然と思いましたので、そのように変更しました(番号指定は別名にして残しましたが不要な気がしています)。 https://github.com/ryuz/jelly/blob/develop/rust/mem_access/src/udmabuf_accessor.rs

最新のu-dma-buf仕様についていけてなかったのですが、かなり高機能になっているのですね。 https://github.com/ikwzm/udmabuf/blob/master/Readme.ja.md minor-number の衝突は気になっておりました。minor-number は指定せずに自動割り当てとして、device-name で紐づけるのがスマートな気がしておりますが、推奨の使い方とかありますでしょうか? uio は あくまで、/dev の下には番号由来の名前が現れて /class/uio/ の下から name 取得するのに対して、u-dam-buf は device-name が直接的に指定されるので、取り扱いポリシーが異なる認識でおります(なので uio 用と u-dma-buf とでは new まわりのポリシーは異なるAPI設計が妥当と思っています)。

ikwzm commented 2 years ago

私は device-name プロパティで u-dma-buf のデバイス名を指定する方法を使っています。どうも番号で指定するのは慣れませんね~。

ryuz commented 2 years ago

有難うございます! サンプルも device-name 指定に変えて、デフォルトは device-name 指定のAPIにしていこうと思います。

ryuz commented 2 years ago

遅くなりましたが、ひとまず device-name で open できるバージョンを v0.1.5 として publish いたしました。 https://crates.io/crates/jelly-mem_access/0.1.5 https://github.com/ryuz/jelly-mem_access/tree/v0.1.5

ポリモーフィックをご提案頂いておりますが、私のRustスキルが追い付いていない部分もありますので、特に大きな課題が残っていなければ一旦本件は close して、また別途検討させて頂きたいと思います。