lx 0.4.0

A no_std crate to use Linux system calls
Documentation
use core::{
    marker::PhantomData,
    mem,
};

use crate::{
    ioctl,
    AsRawFd,
};

/// An ioctl's direction.
#[repr(u8)]
pub enum Direction {
    /// The argument is ignored.
    None = 0b00,

    /// This is a write-like operation: the kernel only reads from the argument.
    Write = 0b01,

    /// This is a read-like operation: the kernel only writes to the argument.
    Read = 0b10,

    /// The kernel reads and writes to the argument.
    ReadWrite = 0b11,
}

/// Computes a command number, or request number in a similar way to the `_IO`, `_IOR, `_IOW` and
/// `_IOWR` C macros.
pub const fn cmd(dir: Direction, ty: u8, nr: u8, size: usize) -> u32 {
    if size >> 14 != 0 {
        panic!("size must not exceed 14 bits");
    }
    (nr as u32) | ((ty as u32) << 8) | ((size as u32) << 16) | ((dir as u32) << 30)
}

pub struct None {
    cmd: u32,
}

impl None {
    pub const fn new(ty: u8, nr: u8) -> Self {
        Self {
            cmd: cmd(Direction::None, ty, nr, 0),
        }
    }

    pub const fn cmd(&self) -> u32 {
        self.cmd
    }

    /// Calls the ioctl on a file descriptor.
    ///
    /// # Safety
    ///
    /// This library does know know about the different ioctls. It is up to the caller to ensure
    /// that the ioctl it is making is safe.
    #[inline]
    pub unsafe fn call(&self, fd: &impl AsRawFd) -> crate::Result<i32> {
        ioctl(fd, self.cmd, 0)
    }
}

pub struct Read<T> {
    cmd: u32,
    _marker: PhantomData<*mut T>,
}

impl<T> Read<T> {
    pub const fn new(ty: u8, nr: u8) -> Self {
        Self {
            cmd: cmd(Direction::Read, ty, nr, mem::size_of::<T>()),
            _marker: PhantomData,
        }
    }

    pub const fn cmd(&self) -> u32 {
        self.cmd
    }

    /// Calls the ioctl.
    ///
    /// # Safety
    ///
    /// This library does know know about the different ioctls. It is up to the caller to ensure
    /// that the ioctl it is making is safe.
    #[inline]
    pub unsafe fn call(&self, fd: &impl AsRawFd, t: &mut T) -> crate::Result<i32> {
        ioctl(fd, self.cmd, t as *mut T as usize)
    }
}

pub struct Write<T> {
    cmd: u32,
    _marker: PhantomData<*const T>,
}

impl<T> Write<T> {
    pub const fn new(ty: u8, nr: u8) -> Self {
        Self {
            cmd: cmd(Direction::Write, ty, nr, mem::size_of::<T>()),
            _marker: PhantomData,
        }
    }

    pub const fn cmd(&self) -> u32 {
        self.cmd
    }

    /// Calls the ioctl.
    ///
    /// # Safety
    ///
    /// This library does know know about the different ioctls. It is up to the caller to ensure
    /// that the ioctl it is making is safe.
    #[inline]
    pub unsafe fn call(&self, fd: &impl AsRawFd, t: &T) -> crate::Result<i32> {
        ioctl(fd, self.cmd, t as *const T as usize)
    }
}

pub struct ReadWrite<T> {
    cmd: u32,
    _marker: PhantomData<*mut T>,
}

impl<T> ReadWrite<T> {
    pub const fn new(ty: u8, nr: u8) -> Self {
        Self {
            cmd: cmd(Direction::ReadWrite, ty, nr, mem::size_of::<T>()),
            _marker: PhantomData,
        }
    }

    pub const fn cmd(&self) -> u32 {
        self.cmd
    }

    /// Calls the ioctl.
    ///
    /// # Safety
    ///
    /// This library does know know about the different ioctls. It is up to the caller to ensure
    /// that the ioctl it is making is safe.
    #[inline]
    pub unsafe fn call(&self, fd: &impl AsRawFd, t: &mut T) -> crate::Result<i32> {
        ioctl(fd, self.cmd, t as *mut T as usize)
    }
}