From bbad1773b456ee8bd700e3201ac2be016c7389a2 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Tue, 11 Jul 2017 09:47:47 -0700 Subject: [PATCH 01/16] Remove old workaround --- src/sys/ioctl/mod.rs | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index 48b3827bb8..b5e1709da6 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -114,12 +114,6 @@ mod platform; pub use self::platform::*; -// liblibc has the wrong decl for linux :| hack until #26809 lands. -extern "C" { - #[doc(hidden)] - pub fn ioctl(fd: libc::c_int, req: libc::c_ulong, ...) -> libc::c_int; -} - /// A hack to get the macros to work nicely. #[doc(hidden)] pub use ::libc as libc; @@ -140,34 +134,34 @@ macro_rules! ioctl { pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, data: *mut u8) -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, $nr as $crate::sys::ioctl::libc::c_ulong, data)) + convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, $nr as $crate::sys::ioctl::libc::c_ulong, data)) } ); (none $name:ident with $ioty:expr, $nr:expr) => ( pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int) -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, io!($ioty, $nr) as $crate::sys::ioctl::libc::c_ulong)) + convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, io!($ioty, $nr) as $crate::sys::ioctl::libc::c_ulong)) } ); (read $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, val: *mut $ty) -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val)) + convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val)) } ); (write $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, val: $ty) -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val)) + convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val)) } ); (readwrite $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, val: *mut $ty) -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val)) + convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val)) } ); (read buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( @@ -175,14 +169,14 @@ macro_rules! ioctl { val: *mut $ty, len: usize) -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, ior!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val)) + convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, ior!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val)) } ); (write buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, val: *const $ty, len: usize) -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iow!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val)) + convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, iow!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val)) } ); (readwrite buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( @@ -190,7 +184,7 @@ macro_rules! ioctl { val: *mut $ty, len: usize) -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iorw!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val)) + convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, iorw!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val)) } ); } From 33e4aa792010f46d93fd4372ed1c9d7d34bc6c69 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Tue, 11 Jul 2017 11:36:15 -0700 Subject: [PATCH 02/16] Re-add bad variant of ioctl! --- src/sys/ioctl/mod.rs | 2 +- test/sys/test_ioctl.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index b5e1709da6..b3388c2804 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -130,7 +130,7 @@ macro_rules! convert_ioctl_res { #[macro_export] macro_rules! ioctl { - ($name:ident with $nr:expr) => ( + (bad $name:ident with $nr:expr) => ( pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, data: *mut u8) -> $crate::Result<$crate::sys::ioctl::libc::c_int> { diff --git a/test/sys/test_ioctl.rs b/test/sys/test_ioctl.rs index 55b61fd7b3..8a5b8a1233 100644 --- a/test/sys/test_ioctl.rs +++ b/test/sys/test_ioctl.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] // Simple tests to ensure macro generated fns compile -ioctl!(do_bad with 0x1234); +ioctl!(bad do_bad with 0x1234); ioctl!(none do_none with 0, 0); ioctl!(read read_test with 0, 0; u32); ioctl!(write write_test with 0, 0; u64); @@ -82,7 +82,7 @@ mod linux { #[test] fn test_op_read_write_64() { assert_eq!(iorw!(b'z', 10, (1 as u64) << 32), 0xC0007A0A); - } + } } #[cfg(any(target_os = "macos", @@ -132,5 +132,5 @@ mod bsd { #[test] fn test_op_read_write_64() { assert_eq!(iorw!(b'z', 10, (1 as u64) << 32), 0xC0007A0A); - } + } } From 7e2bbdc75ab335ee4c34f8ed4a8d507663080a10 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Tue, 11 Jul 2017 09:55:47 -0700 Subject: [PATCH 03/16] Add a "bad none" variant to the ioctl macro --- src/sys/ioctl/mod.rs | 6 ++++++ test/sys/test_ioctl.rs | 1 + 2 files changed, 7 insertions(+) diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index b3388c2804..ac8ff14910 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -137,6 +137,12 @@ macro_rules! ioctl { convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, $nr as $crate::sys::ioctl::libc::c_ulong, data)) } ); + (bad none $name:ident with $nr:expr) => ( + pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int) + -> $crate::Result<$crate::sys::ioctl::libc::c_int> { + convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, $nr as $crate::sys::ioctl::libc::c_ulong)) + } + ); (none $name:ident with $ioty:expr, $nr:expr) => ( pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int) -> $crate::Result<$crate::sys::ioctl::libc::c_int> { diff --git a/test/sys/test_ioctl.rs b/test/sys/test_ioctl.rs index 8a5b8a1233..c0ae078a44 100644 --- a/test/sys/test_ioctl.rs +++ b/test/sys/test_ioctl.rs @@ -2,6 +2,7 @@ // Simple tests to ensure macro generated fns compile ioctl!(bad do_bad with 0x1234); +ioctl!(bad none do_bad_none with 0x1234); ioctl!(none do_none with 0, 0); ioctl!(read read_test with 0, 0; u32); ioctl!(write write_test with 0, 0; u64); From 850b370fbea50614b056d31755cba0f8d706c7dc Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Tue, 11 Jul 2017 11:38:14 -0700 Subject: [PATCH 04/16] Hide internal macros/types within ioctl --- src/sys/ioctl/mod.rs | 1 + src/sys/ioctl/platform/bsd.rs | 10 ++++++++++ src/sys/ioctl/platform/linux.rs | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index ac8ff14910..6a79987669 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -120,6 +120,7 @@ pub use ::libc as libc; /// Convert raw ioctl return value to a Nix result #[macro_export] +#[doc(hidden)] macro_rules! convert_ioctl_res { ($w:expr) => ( { diff --git a/src/sys/ioctl/platform/bsd.rs b/src/sys/ioctl/platform/bsd.rs index 57b4d637ca..df3716b952 100644 --- a/src/sys/ioctl/platform/bsd.rs +++ b/src/sys/ioctl/platform/bsd.rs @@ -1,14 +1,20 @@ mod consts { + #[doc(hidden)] pub const VOID: u32 = 0x20000000; + #[doc(hidden)] pub const OUT: u32 = 0x40000000; + #[doc(hidden)] pub const IN: u32 = 0x80000000; + #[doc(hidden)] pub const INOUT: u32 = (IN|OUT); + #[doc(hidden)] pub const IOCPARM_MASK: u32 = 0x1fff; } pub use self::consts::*; #[macro_export] +#[doc(hidden)] macro_rules! ioc { ($inout:expr, $group:expr, $num:expr, $len:expr) => ( $inout | (($len as u32 & $crate::sys::ioctl::IOCPARM_MASK) << 16) | (($group as u32) << 8) | ($num as u32) @@ -16,21 +22,25 @@ macro_rules! ioc { } #[macro_export] +#[doc(hidden)] macro_rules! io { ($g:expr, $n:expr) => (ioc!($crate::sys::ioctl::VOID, $g, $n, 0)) } #[macro_export] +#[doc(hidden)] macro_rules! ior { ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::OUT, $g, $n, $len)) } #[macro_export] +#[doc(hidden)] macro_rules! iow { ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::IN, $g, $n, $len)) } #[macro_export] +#[doc(hidden)] macro_rules! iorw { ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::INOUT, $g, $n, $len)) } diff --git a/src/sys/ioctl/platform/linux.rs b/src/sys/ioctl/platform/linux.rs index cdf5b20ab8..00a25e8883 100644 --- a/src/sys/ioctl/platform/linux.rs +++ b/src/sys/ioctl/platform/linux.rs @@ -1,12 +1,19 @@ +#[doc(hidden)] pub const NRBITS: u32 = 8; +#[doc(hidden)] pub const TYPEBITS: u32 = 8; #[cfg(any(target_arch = "mips", target_arch = "mips64", target_arch = "powerpc", target_arch = "powerpc64"))] mod consts { + #[doc(hidden)] pub const NONE: u8 = 1; + #[doc(hidden)] pub const READ: u8 = 2; + #[doc(hidden)] pub const WRITE: u8 = 4; + #[doc(hidden)] pub const SIZEBITS: u8 = 13; + #[doc(hidden)] pub const DIRBITS: u8 = 3; } @@ -28,27 +35,41 @@ use this_arch_not_supported; target_arch = "x86_64", target_arch = "aarch64"))] mod consts { + #[doc(hidden)] pub const NONE: u8 = 0; + #[doc(hidden)] pub const READ: u8 = 2; + #[doc(hidden)] pub const WRITE: u8 = 1; + #[doc(hidden)] pub const SIZEBITS: u8 = 14; + #[doc(hidden)] pub const DIRBITS: u8 = 2; } pub use self::consts::*; +#[doc(hidden)] pub const NRSHIFT: u32 = 0; +#[doc(hidden)] pub const TYPESHIFT: u32 = NRSHIFT + NRBITS as u32; +#[doc(hidden)] pub const SIZESHIFT: u32 = TYPESHIFT + TYPEBITS as u32; +#[doc(hidden)] pub const DIRSHIFT: u32 = SIZESHIFT + SIZEBITS as u32; +#[doc(hidden)] pub const NRMASK: u32 = (1 << NRBITS) - 1; +#[doc(hidden)] pub const TYPEMASK: u32 = (1 << TYPEBITS) - 1; +#[doc(hidden)] pub const SIZEMASK: u32 = (1 << SIZEBITS) - 1; +#[doc(hidden)] pub const DIRMASK: u32 = (1 << DIRBITS) - 1; /// Encode an ioctl command. #[macro_export] +#[doc(hidden)] macro_rules! ioc { ($dir:expr, $ty:expr, $nr:expr, $sz:expr) => ( (($dir as u32) << $crate::sys::ioctl::DIRSHIFT) | @@ -59,53 +80,65 @@ macro_rules! ioc { /// Encode an ioctl command that has no associated data. #[macro_export] +#[doc(hidden)] macro_rules! io { ($ty:expr, $nr:expr) => (ioc!($crate::sys::ioctl::NONE, $ty, $nr, 0)) } /// Encode an ioctl command that reads. #[macro_export] +#[doc(hidden)] macro_rules! ior { ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ, $ty, $nr, $sz)) } /// Encode an ioctl command that writes. #[macro_export] +#[doc(hidden)] macro_rules! iow { ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::WRITE, $ty, $nr, $sz)) } /// Encode an ioctl command that both reads and writes. #[macro_export] +#[doc(hidden)] macro_rules! iorw { ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ | $crate::sys::ioctl::WRITE, $ty, $nr, $sz)) } /// Extracts the "direction" (read/write/none) from an encoded ioctl command. #[inline(always)] +#[doc(hidden)] pub fn ioc_dir(nr: u32) -> u8 { ((nr >> DIRSHIFT) & DIRMASK) as u8 } /// Extracts the type from an encoded ioctl command. #[inline(always)] +#[doc(hidden)] pub fn ioc_type(nr: u32) -> u32 { (nr >> TYPESHIFT) & TYPEMASK } /// Extracts the ioctl number from an encoded ioctl command. #[inline(always)] +#[doc(hidden)] pub fn ioc_nr(nr: u32) -> u32 { (nr >> NRSHIFT) & NRMASK } /// Extracts the size from an encoded ioctl command. #[inline(always)] +#[doc(hidden)] pub fn ioc_size(nr: u32) -> u32 { ((nr >> SIZESHIFT) as u32) & SIZEMASK } +#[doc(hidden)] pub const IN: u32 = (WRITE as u32) << DIRSHIFT; +#[doc(hidden)] pub const OUT: u32 = (READ as u32) << DIRSHIFT; +#[doc(hidden)] pub const INOUT: u32 = ((READ|WRITE) as u32) << DIRSHIFT; +#[doc(hidden)] pub const SIZE_MASK: u32 = SIZEMASK << SIZESHIFT; From 2d165ebfe7f7372745cabb1499c5c0a6fecaf3c9 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Tue, 11 Jul 2017 11:39:44 -0700 Subject: [PATCH 05/16] Remove unnecessary path aliasing --- src/sys/ioctl/mod.rs | 59 +++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index 6a79987669..b54995b774 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -114,10 +114,6 @@ mod platform; pub use self::platform::*; -/// A hack to get the macros to work nicely. -#[doc(hidden)] -pub use ::libc as libc; - /// Convert raw ioctl return value to a Nix result #[macro_export] #[doc(hidden)] @@ -129,69 +125,70 @@ macro_rules! convert_ioctl_res { ); } +/// Generates ioctl functions. See [::sys::ioctl](sys/ioctl/index.html). #[macro_export] macro_rules! ioctl { (bad $name:ident with $nr:expr) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, + pub unsafe fn $name(fd: $crate::libc::c_int, data: *mut u8) - -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, $nr as $crate::sys::ioctl::libc::c_ulong, data)) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::libc::c_ulong, data)) } ); (bad none $name:ident with $nr:expr) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int) - -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, $nr as $crate::sys::ioctl::libc::c_ulong)) + pub unsafe fn $name(fd: $crate::libc::c_int) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::libc::c_ulong)) } ); (none $name:ident with $ioty:expr, $nr:expr) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int) - -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, io!($ioty, $nr) as $crate::sys::ioctl::libc::c_ulong)) + pub unsafe fn $name(fd: $crate::libc::c_int) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, io!($ioty, $nr) as $crate::libc::c_ulong)) } ); (read $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, + pub unsafe fn $name(fd: $crate::libc::c_int, val: *mut $ty) - -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val)) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::libc::c_ulong, val)) } ); (write $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, + pub unsafe fn $name(fd: $crate::libc::c_int, val: $ty) - -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val)) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::libc::c_ulong, val)) } ); (readwrite $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, + pub unsafe fn $name(fd: $crate::libc::c_int, val: *mut $ty) - -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val)) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::libc::c_ulong, val)) } ); (read buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, + pub unsafe fn $name(fd: $crate::libc::c_int, val: *mut $ty, len: usize) - -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, ior!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val)) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, len) as $crate::libc::c_ulong, val)) } ); (write buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, + pub unsafe fn $name(fd: $crate::libc::c_int, val: *const $ty, - len: usize) -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, iow!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val)) + len: usize) -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, len) as $crate::libc::c_ulong, val)) } ); (readwrite buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, + pub unsafe fn $name(fd: $crate::libc::c_int, val: *mut $ty, len: usize) - -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::libc::ioctl(fd, iorw!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val)) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, len) as $crate::libc::c_ulong, val)) } ); } From 840a16a374e311af3f2137947a91e08311b0eb77 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Tue, 11 Jul 2017 11:39:56 -0700 Subject: [PATCH 06/16] Revise ioctl module documentation This refactors the examples to more directly address the exact use cases for the `ioctl!` macro that `nix` provides. Additionally other macros that should not be used by end users are no longer discussed. --- src/sys/ioctl/mod.rs | 196 ++++++++++++++++++++++++++----------------- 1 file changed, 119 insertions(+), 77 deletions(-) diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index b54995b774..330ff8cddc 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -1,102 +1,144 @@ -//! Provide helpers for making ioctl system calls -//! -//! Currently supports Linux on all architectures. Other platforms welcome! +//! Provide helpers for making ioctl system calls. //! //! This library is pretty low-level and messy. `ioctl` is not fun. //! //! What is an `ioctl`? //! =================== //! -//! The `ioctl` syscall is the grab-bag syscall on POSIX systems. Don't want -//! to add a new syscall? Make it an `ioctl`! `ioctl` refers to both the syscall, -//! and the commands that can be send with it. `ioctl` stands for "IO control", -//! and the commands are always sent to a file descriptor. +//! The `ioctl` syscall is the grab-bag syscall on POSIX systems. Don't want to add a new +//! syscall? Make it an `ioctl`! `ioctl` refers to both the syscall, and the commands that can be +//! sent with it. `ioctl` stands for "IO control", and the commands are always sent to a file +//! descriptor. //! //! It is common to see `ioctl`s used for the following purposes: //! -//! * Provide read/write access to out-of-band data related -//! to a device such as configuration (for instance, setting -//! serial port options) -//! * Provide a mechanism for performing full-duplex data -//! transfers (for instance, xfer on SPI devices). -//! * Provide access to control functions on a device (for example, -//! on Linux you can send commands like pause, resume, and eject -//! to the CDROM device. -//! * Do whatever else the device driver creator thought made most sense. -//! -//! `ioctl`s are synchronous system calls and are similar to read and -//! write calls in that regard. +//! * Provide read/write access to out-of-band data related to a device such as configuration +//! (for instance, setting serial port options) +//! * Provide a mechanism for performing full-duplex data transfers (for instance, xfer on SPI +//! devices). +//! * Provide access to control functions on a device (for example, on Linux you can send +//! commands like pause, resume, and eject to the CDROM device. +//! * Do whatever else the device driver creator thought made most sense. +//! +//! `ioctl`s are synchronous system calls and are similar to read and write calls in that regard. +//! They operate on file descriptors and have an identifier that specifies what the ioctl is. +//! Additionally they may read or write data and therefore need to pass along a data pointer. +//! Besides the semantics of the ioctls being confusing, the generation of this identifer can also +//! be difficult. +//! +//! Historically `ioctl` numbers were arbitrary hard-coded values. In Linux (before 2.6) and some +//! unices this has changed to a more-ordered system where the ioctl numbers are partitioned into +//! subcomponents (For linux this is documented in +//! [`Documentation/ioctl/ioctl-number.txt`](http://elixir.free-electrons.com/linux/latest/source/Documentation/ioctl/ioctl-number.txt)): +//! +//! * Number: The actual ioctl ID +//! * Type: A grouping of ioctls for a common purpose or driver +//! * Size: The size in bytes of the data that will be transferred +//! * Direction: Whether there is any data and if it's read, write, or both +//! +//! Newer drivers should not generate complete integer identifiers for their `ioctl`s instead +//! preferring to use the 4 components above to generate the final ioctl identifier. Because of +//! how old `ioctl`s are, however, there are many hard-coded `ioctl` identifiers. These are +//! commonly referred to as "bad" in `ioctl` documentation. +//! +//! Defining ioctls +//! =============== +//! +//! This library provides the `ioctl!` macro, for binding `ioctl`s. This macro generates public +//! unsafe functions that can then be used for calling the ioctl. This macro has a few different +//! ways it can be used depending on the specific ioctl you're working with. +//! +//! A simple `ioctl` is `SPI_IOC_RD_MODE`. This ioctl works with the SPI interface on Linux. This +//! specific `ioctl` reads the mode of the SPI device as a `u8`. It's declared in +//! `/include/uapi/linux/spi/spidev.h` as `_IOR(SPI_IOC_MAGIC, 1, __u8)`. Since it uses the `_IOR` +//! macro, we know it's a `read` ioctl and can use the `ioctl!` macro as follows: //! -//! What does this module support? -//! =============================== +//! ``` +//! # #[macro_use] extern crate nix; +//! const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h +//! const SPI_IOC_TYPE_MODE: u8 = 1; +//! ioctl!(read spi_read_mode with SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE; u8); +//! # fn main() {} +//! ``` //! -//! This library provides the `ioctl!` macro, for binding `ioctl`s. -//! Here's a few examples of how that can work for SPI under Linux -//! from [rust-spidev](https://github.com/posborne/rust-spidev). +//! This generates the function: //! //! ``` -//! #[allow(non_camel_case_types)] -//! pub struct spi_ioc_transfer { -//! pub tx_buf: u64, -//! pub rx_buf: u64, -//! pub len: u32, -//! -//! // optional overrides -//! pub speed_hz: u32, -//! pub delay_usecs: u16, -//! pub bits_per_word: u8, -//! pub cs_change: u8, -//! pub pad: u32, +//! # #[macro_use] extern crate nix; +//! # use std::mem; +//! # use nix::{Errno, libc, Result}; +//! # use nix::libc::c_int as c_int; +//! # const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h +//! # const SPI_IOC_TYPE_MODE: u8 = 1; +//! pub unsafe fn spi_read_mode(fd: c_int, data: *mut u8) -> Result { +//! let res = libc::ioctl(fd, ior!(SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE, mem::size_of::()), data); +//! Errno::result(res) //! } +//! # fn main() {} +//! ``` //! -//! #[cfg(linux)] -//! mod ioctl { -//! use super::*; -//! -//! const SPI_IOC_MAGIC: u8 = 'k' as u8; -//! const SPI_IOC_NR_TRANSFER: u8 = 0; -//! const SPI_IOC_NR_MODE: u8 = 1; -//! const SPI_IOC_NR_LSB_FIRST: u8 = 2; -//! const SPI_IOC_NR_BITS_PER_WORD: u8 = 3; -//! const SPI_IOC_NR_MAX_SPEED_HZ: u8 = 4; -//! const SPI_IOC_NR_MODE32: u8 = 5; -//! -//! ioctl!(read get_mode_u8 with SPI_IOC_MAGIC, SPI_IOC_NR_MODE; u8); -//! ioctl!(read get_mode_u32 with SPI_IOC_MAGIC, SPI_IOC_NR_MODE; u32); -//! ioctl!(write set_mode_u8 with SPI_IOC_MAGIC, SPI_IOC_NR_MODE; u8); -//! ioctl!(write set_mode_u32 with SPI_IOC_MAGIC, SPI_IOC_NR_MODE32; u32); -//! ioctl!(read get_lsb_first with SPI_IOC_MAGIC, SPI_IOC_NR_LSB_FIRST; u8); -//! ioctl!(write set_lsb_first with SPI_IOC_MAGIC, SPI_IOC_NR_LSB_FIRST; u8); -//! ioctl!(read get_bits_per_word with SPI_IOC_MAGIC, SPI_IOC_NR_BITS_PER_WORD; u8); -//! ioctl!(write set_bits_per_word with SPI_IOC_MAGIC, SPI_IOC_NR_BITS_PER_WORD; u8); -//! ioctl!(read get_max_speed_hz with SPI_IOC_MAGIC, SPI_IOC_NR_MAX_SPEED_HZ; u32); -//! ioctl!(write set_max_speed_hz with SPI_IOC_MAGIC, SPI_IOC_NR_MAX_SPEED_HZ; u32); -//! ioctl!(write spidev_transfer with SPI_IOC_MAGIC, SPI_IOC_NR_TRANSFER; spi_ioc_transfer); -//! ioctl!(write buf spidev_transfer_buf with SPI_IOC_MAGIC, SPI_IOC_NR_TRANSFER; spi_ioc_transfer); -//! } +//! The return value for `ioctl` functions generated by the `ioctl!` macro are `nix::Error`s. +//! These are generated by assuming the return value of the ioctl is `-1` on error and everything +//! else is a valid return value. If this is not the case, `Result::map` can be used to map some +//! of the range of "good" values (-2..-Inf, 0..Inf) into a smaller range in a helper function. +//! +//! The mode for a given `ioctl` should be clear from the documentation if it has good +//! documentation. Otherwise it will be clear based on the macro used to generate the `ioctl` +//! number where `_IO`, `_IOR`, `_IOW`, and `_IORW` map to "none", "read", "write_*", and "readwrite" +//! respectively. To determine the specific `write_` variant to use you'll need to find +//! what the argument type is supposed to be. If it's an `int`, then `write_int` should be used, +//! otherwise it should be a pointer and `write_ptr` should be used. On Linux the +//! [`ioctl_list` man page](http://man7.org/linux/man-pages/man2/ioctl_list.2.html) describes a +//! large number of `ioctl`s and describes their argument data type. +//! +//! More examples on using `ioctl!` can be found in the [rust-spidev crate](https://github.com/rust-embedded/rust-spidev). //! -//! // doctest workaround -//! fn main() {} +//! ```text +//! pub unsafe fn $NAME(fd: c_int, val: *mut u8, len: usize) -> Result; //! ``` //! -//! Spidev uses the `_IOC` macros that are encouraged (as far as -//! `ioctl` can be encouraged at all) for newer drivers. Many -//! drivers, however, just use magic numbers with no attached -//! semantics. For those, the `ioctl!(bad ...)` variant should be -//! used (the "bad" terminology is from the Linux kernel). +//! As mentioned earlier, there are many old `ioctl`s that do not use the newer method of +//! generating `ioctl` numbers and instead use hardcoded values. These can be used with the `bad` +//! form of the `ioctl!` macro (there is no data transfer direction used with `bad`). The naming of +//! this comes from the Linux kernel which refers to these `ioctl`s as "bad". //! -//! How do I get the magic numbers? -//! =============================== +//! For example the `TCGETS` `ioctl` reads a `termios` data structure for a given file descriptor. +//! It can be implemented as: //! -//! For Linux, look at your system's headers. For example, `/usr/include/linux/input.h` has a lot -//! of lines defining macros which use `_IOR`, `_IOW`, `_IOC`, and `_IORW`. These macros -//! correspond to the `ior!`, `iow!`, `ioc!`, and `iorw!` macros defined in this crate. -//! Additionally, there is the `ioctl!` macro for creating a wrapper around `ioctl` that is -//! somewhat more type-safe. +//! ``` +//! # #[macro_use] extern crate nix; +//! # #[cfg(any(target_os = "android", target_os = "linux"))] +//! # use nix::libc::TCGETS as TCGETS; +//! # #[cfg(any(target_os = "android", target_os = "linux"))] +//! ioctl!(bad tcgets with TCGETS); +//! # fn main() {} +//! ``` +//! +//! The generated function has the same form as that generated by `read`: +//! +//! ```text +//! pub unsafe fn tcgets(fd: c_int, val: *mut u8) -> Result; +//! ``` +//! +//! There is also a `bad none` form for use with hard-coded `ioctl`s that do not transfer data. +//! The `TIOCEXCL` `ioctl` that's part of the termios API can be implemented as: //! -//! Most `ioctl`s have no or little documentation. You'll need to scrounge through -//! the source to figure out what they do and how they should be used. +//! ``` +//! # #[macro_use] extern crate nix; +//! # use nix::libc::TIOCEXCL as TIOCEXCL; +//! ioctl!(bad none tiocexcl with TIOCEXCL); +//! # fn main() {} +//! ``` +//! +//! More examples on using `ioctl!` can be found in the [rust-spidev crate](https://github.com/rust-embedded/rust-spidev). //! +//! Finding ioctl documentation +//! --------------------------- +//! +//! For Linux, look at your system's headers. For example, `/usr/include/linux/input.h` has a lot +//! of lines defining macros which use `_IO`, `_IOR`, `_IOW`, `_IOC`, and `_IORW`. Some `ioctl`s are +//! documented directly in the headers defining their constants, but others have more extensive +//! documentation in man pages (like termios' `ioctl`s which are in `tty_ioctl(4)`). #[cfg(any(target_os = "linux", target_os = "android"))] #[path = "platform/linux.rs"] #[macro_use] From e3b18ceeb91190a41be320b20b931b80763304ac Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Tue, 11 Jul 2017 11:55:25 -0700 Subject: [PATCH 07/16] Remove ioc_* functions These are low-level functions that shouldn't be exposed --- src/sys/ioctl/platform/linux.rs | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/src/sys/ioctl/platform/linux.rs b/src/sys/ioctl/platform/linux.rs index 00a25e8883..e3a130a926 100644 --- a/src/sys/ioctl/platform/linux.rs +++ b/src/sys/ioctl/platform/linux.rs @@ -106,34 +106,6 @@ macro_rules! iorw { ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ | $crate::sys::ioctl::WRITE, $ty, $nr, $sz)) } -/// Extracts the "direction" (read/write/none) from an encoded ioctl command. -#[inline(always)] -#[doc(hidden)] -pub fn ioc_dir(nr: u32) -> u8 { - ((nr >> DIRSHIFT) & DIRMASK) as u8 -} - -/// Extracts the type from an encoded ioctl command. -#[inline(always)] -#[doc(hidden)] -pub fn ioc_type(nr: u32) -> u32 { - (nr >> TYPESHIFT) & TYPEMASK -} - -/// Extracts the ioctl number from an encoded ioctl command. -#[inline(always)] -#[doc(hidden)] -pub fn ioc_nr(nr: u32) -> u32 { - (nr >> NRSHIFT) & NRMASK -} - -/// Extracts the size from an encoded ioctl command. -#[inline(always)] -#[doc(hidden)] -pub fn ioc_size(nr: u32) -> u32 { - ((nr >> SIZESHIFT) as u32) & SIZEMASK -} - #[doc(hidden)] pub const IN: u32 = (WRITE as u32) << DIRSHIFT; #[doc(hidden)] From 4ac4b125537119b19cd9188a519bfa0cfa60dbf8 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Tue, 11 Jul 2017 16:46:17 -0700 Subject: [PATCH 08/16] Use the proper ioctl number type depending on target This also means that we need to properly mask off bits to the valid range of ioctl numbers. --- src/sys/ioctl/mod.rs | 18 ++++++++--------- src/sys/ioctl/platform/bsd.rs | 17 ++++++++++------ src/sys/ioctl/platform/linux.rs | 36 ++++++++++++++++++++------------- 3 files changed, 42 insertions(+), 29 deletions(-) diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index 330ff8cddc..a2eb79b543 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -174,40 +174,40 @@ macro_rules! ioctl { pub unsafe fn $name(fd: $crate::libc::c_int, data: *mut u8) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::libc::c_ulong, data)) + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) } ); (bad none $name:ident with $nr:expr) => ( pub unsafe fn $name(fd: $crate::libc::c_int) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::libc::c_ulong)) + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type)) } ); (none $name:ident with $ioty:expr, $nr:expr) => ( pub unsafe fn $name(fd: $crate::libc::c_int) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, io!($ioty, $nr) as $crate::libc::c_ulong)) + convert_ioctl_res!($crate::libc::ioctl(fd, io!($ioty, $nr) as $crate::sys::ioctl::ioctl_num_type)) } ); (read $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, val: *mut $ty) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::libc::c_ulong, val)) + convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, val)) } ); (write $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, val: $ty) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::libc::c_ulong, val)) + convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, val)) } ); (readwrite $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, val: *mut $ty) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::libc::c_ulong, val)) + convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, val)) } ); (read buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( @@ -215,14 +215,14 @@ macro_rules! ioctl { val: *mut $ty, len: usize) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, len) as $crate::libc::c_ulong, val)) + convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, val)) } ); (write buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, val: *const $ty, len: usize) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, len) as $crate::libc::c_ulong, val)) + convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, val)) } ); (readwrite buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( @@ -230,7 +230,7 @@ macro_rules! ioctl { val: *mut $ty, len: usize) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, len) as $crate::libc::c_ulong, val)) + convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, val)) } ); } diff --git a/src/sys/ioctl/platform/bsd.rs b/src/sys/ioctl/platform/bsd.rs index df3716b952..ea39da3de6 100644 --- a/src/sys/ioctl/platform/bsd.rs +++ b/src/sys/ioctl/platform/bsd.rs @@ -1,14 +1,19 @@ +/// The datatype used for the ioctl number +#[doc(hidden)] +pub type ioctl_num_type = ::libc::c_ulong; + mod consts { + use ::sys::ioctl::platform::ioctl_num_type; #[doc(hidden)] - pub const VOID: u32 = 0x20000000; + pub const VOID: ioctl_num_type = 0x20000000; #[doc(hidden)] - pub const OUT: u32 = 0x40000000; + pub const OUT: ioctl_num_type = 0x40000000; #[doc(hidden)] - pub const IN: u32 = 0x80000000; + pub const IN: ioctl_num_type = 0x80000000; #[doc(hidden)] - pub const INOUT: u32 = (IN|OUT); + pub const INOUT: ioctl_num_type = (IN|OUT); #[doc(hidden)] - pub const IOCPARM_MASK: u32 = 0x1fff; + pub const IOCPARM_MASK: ioctl_num_type = 0x1fff; } pub use self::consts::*; @@ -17,7 +22,7 @@ pub use self::consts::*; #[doc(hidden)] macro_rules! ioc { ($inout:expr, $group:expr, $num:expr, $len:expr) => ( - $inout | (($len as u32 & $crate::sys::ioctl::IOCPARM_MASK) << 16) | (($group as u32) << 8) | ($num as u32) + $inout | (($len as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::IOCPARM_MASK) << 16) | (($group as $crate::sys::ioctl::ioctl_num_type) << 8) | ($num as $crate::sys::ioctl::ioctl_num_type) ) } diff --git a/src/sys/ioctl/platform/linux.rs b/src/sys/ioctl/platform/linux.rs index e3a130a926..6daa925762 100644 --- a/src/sys/ioctl/platform/linux.rs +++ b/src/sys/ioctl/platform/linux.rs @@ -1,7 +1,15 @@ +/// The datatype used for the ioctl number +#[cfg(any(target_os = "android", target_env = "musl"))] #[doc(hidden)] -pub const NRBITS: u32 = 8; +pub type ioctl_num_type = ::libc::c_int; +#[cfg(not(any(target_os = "android", target_env = "musl")))] #[doc(hidden)] -pub const TYPEBITS: u32 = 8; +pub type ioctl_num_type = ::libc::c_ulong; + +#[doc(hidden)] +pub const NRBITS: ioctl_num_type = 8; +#[doc(hidden)] +pub const TYPEBITS: ioctl_num_type = 8; #[cfg(any(target_arch = "mips", target_arch = "mips64", target_arch = "powerpc", target_arch = "powerpc64"))] mod consts { @@ -50,32 +58,32 @@ mod consts { pub use self::consts::*; #[doc(hidden)] -pub const NRSHIFT: u32 = 0; +pub const NRSHIFT: ioctl_num_type = 0; #[doc(hidden)] -pub const TYPESHIFT: u32 = NRSHIFT + NRBITS as u32; +pub const TYPESHIFT: ioctl_num_type = NRSHIFT + NRBITS as ioctl_num_type; #[doc(hidden)] -pub const SIZESHIFT: u32 = TYPESHIFT + TYPEBITS as u32; +pub const SIZESHIFT: ioctl_num_type = TYPESHIFT + TYPEBITS as ioctl_num_type; #[doc(hidden)] -pub const DIRSHIFT: u32 = SIZESHIFT + SIZEBITS as u32; +pub const DIRSHIFT: ioctl_num_type = SIZESHIFT + SIZEBITS as ioctl_num_type; #[doc(hidden)] -pub const NRMASK: u32 = (1 << NRBITS) - 1; +pub const NRMASK: ioctl_num_type = (1 << NRBITS) - 1; #[doc(hidden)] -pub const TYPEMASK: u32 = (1 << TYPEBITS) - 1; +pub const TYPEMASK: ioctl_num_type = (1 << TYPEBITS) - 1; #[doc(hidden)] -pub const SIZEMASK: u32 = (1 << SIZEBITS) - 1; +pub const SIZEMASK: ioctl_num_type = (1 << SIZEBITS) - 1; #[doc(hidden)] -pub const DIRMASK: u32 = (1 << DIRBITS) - 1; +pub const DIRMASK: ioctl_num_type = (1 << DIRBITS) - 1; /// Encode an ioctl command. #[macro_export] #[doc(hidden)] macro_rules! ioc { ($dir:expr, $ty:expr, $nr:expr, $sz:expr) => ( - (($dir as u32) << $crate::sys::ioctl::DIRSHIFT) | - (($ty as u32) << $crate::sys::ioctl::TYPESHIFT) | - (($nr as u32) << $crate::sys::ioctl::NRSHIFT) | - (($sz as u32) << $crate::sys::ioctl::SIZESHIFT)) + (($dir as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::DIRMASK) << $crate::sys::ioctl::DIRSHIFT) | + (($ty as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::TYPEMASK) << $crate::sys::ioctl::TYPESHIFT) | + (($nr as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::NRMASK) << $crate::sys::ioctl::NRSHIFT) | + (($sz as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::SIZEMASK) << $crate::sys::ioctl::SIZESHIFT)) } /// Encode an ioctl command that has no associated data. From 9ec84b80d4bc3d888f544aa4c3c5f64ab2c05496 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Tue, 11 Jul 2017 22:09:26 -0700 Subject: [PATCH 09/16] Remove c_int and c_void from root These were exported for some weird reason and then left in for documentation. Also some parts of certain modules used them and others used the libc:: prefix. This was removed to improve the docs and also code consistency --- src/lib.rs | 3 +-- src/sys/aio.rs | 18 +++++++++--------- src/sys/event.rs | 4 ++-- src/sys/signal.rs | 10 +++++----- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f0cdb581ee..b53510ba6c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,7 +31,6 @@ pub mod libc { pub use self::libc::*; } -pub use libc::{c_int, c_void}; pub use errno::Errno; pub mod errno; @@ -96,7 +95,7 @@ pub enum Error { /// The operation involved a conversion to Rust's native String type, which failed because the /// string did not contain all valid UTF-8. InvalidUtf8, - /// The operation is not supported by Nix, in this instance either use the libc bindings or + /// The operation is not supported by Nix, in this instance either use the libc bindings or /// consult the module documentation to see if there is a more appropriate interface available. UnsupportedOperation, } diff --git a/src/sys/aio.rs b/src/sys/aio.rs index 64aee7ca36..abb742f3bd 100644 --- a/src/sys/aio.rs +++ b/src/sys/aio.rs @@ -94,7 +94,7 @@ impl<'a> AioCb<'a> { /// be prioritized at the process's priority level minus `prio` /// * `sigev_notify` Determines how you will be notified of event /// completion. - pub fn from_fd(fd: RawFd, prio: ::c_int, + pub fn from_fd(fd: RawFd, prio: libc::c_int, sigev_notify: SigevNotify) -> AioCb<'a> { let mut a = AioCb::common_init(fd, prio, sigev_notify); a.aio_offset = 0; @@ -118,13 +118,13 @@ impl<'a> AioCb<'a> { /// * `opcode` This field is only used for `lio_listio`. It determines /// which operation to use for this individual aiocb pub fn from_mut_slice(fd: RawFd, offs: off_t, buf: &'a mut [u8], - prio: ::c_int, sigev_notify: SigevNotify, + prio: libc::c_int, sigev_notify: SigevNotify, opcode: LioOpcode) -> AioCb<'a> { let mut a = AioCb::common_init(fd, prio, sigev_notify); a.aio_offset = offs; a.aio_nbytes = buf.len() as size_t; a.aio_buf = buf.as_ptr() as *mut c_void; - a.aio_lio_opcode = opcode as ::c_int; + a.aio_lio_opcode = opcode as libc::c_int; let aiocb = AioCb { aiocb: a, mutable: true, in_progress: false, keeper: Keeper::phantom(PhantomData)}; @@ -146,13 +146,13 @@ impl<'a> AioCb<'a> { /// * `opcode` This field is only used for `lio_listio`. It determines /// which operation to use for this individual aiocb pub fn from_boxed_slice(fd: RawFd, offs: off_t, buf: Rc>, - prio: ::c_int, sigev_notify: SigevNotify, + prio: libc::c_int, sigev_notify: SigevNotify, opcode: LioOpcode) -> AioCb<'a> { let mut a = AioCb::common_init(fd, prio, sigev_notify); a.aio_offset = offs; a.aio_nbytes = buf.len() as size_t; a.aio_buf = buf.as_ptr() as *mut c_void; - a.aio_lio_opcode = opcode as ::c_int; + a.aio_lio_opcode = opcode as libc::c_int; let aiocb = AioCb{ aiocb: a, mutable: true, in_progress: false, keeper: Keeper::boxed(buf)}; @@ -173,7 +173,7 @@ impl<'a> AioCb<'a> { // AioCb, and they must all be the same type. We're basically stuck with // using an unsafe function, since aio (as designed in C) is an unsafe API. pub fn from_slice(fd: RawFd, offs: off_t, buf: &'a [u8], - prio: ::c_int, sigev_notify: SigevNotify, + prio: libc::c_int, sigev_notify: SigevNotify, opcode: LioOpcode) -> AioCb { let mut a = AioCb::common_init(fd, prio, sigev_notify); a.aio_offset = offs; @@ -183,14 +183,14 @@ impl<'a> AioCb<'a> { // it. a.aio_buf = buf.as_ptr() as *mut c_void; assert!(opcode != LioOpcode::LIO_READ, "Can't read into an immutable buffer"); - a.aio_lio_opcode = opcode as ::c_int; + a.aio_lio_opcode = opcode as libc::c_int; let aiocb = AioCb { aiocb: a, mutable: false, in_progress: false, keeper: Keeper::none}; aiocb } - fn common_init(fd: RawFd, prio: ::c_int, + fn common_init(fd: RawFd, prio: libc::c_int, sigev_notify: SigevNotify) -> libc::aiocb { // Use mem::zeroed instead of explicitly zeroing each field, because the // number and name of reserved fields is OS-dependent. On some OSes, @@ -235,7 +235,7 @@ impl<'a> AioCb<'a> { pub fn fsync(&mut self, mode: AioFsyncMode) -> Result<()> { let p: *mut libc::aiocb = &mut self.aiocb; self.in_progress = true; - Errno::result(unsafe { libc::aio_fsync(mode as ::c_int, p) }).map(drop) + Errno::result(unsafe { libc::aio_fsync(mode as libc::c_int, p) }).map(drop) } /// Asynchronously reads from a file descriptor into a buffer diff --git a/src/sys/event.rs b/src/sys/event.rs index 494c12dd47..95b7619eca 100644 --- a/src/sys/event.rs +++ b/src/sys/event.rs @@ -21,7 +21,7 @@ pub struct KEvent { #[cfg(any(target_os = "openbsd", target_os = "freebsd", target_os = "dragonfly", target_os = "macos", target_os = "ios"))] -type type_of_udata = *mut ::c_void; +type type_of_udata = *mut libc::c_void; #[cfg(any(target_os = "openbsd", target_os = "freebsd", target_os = "dragonfly", target_os = "macos", target_os = "ios"))] @@ -127,7 +127,7 @@ libc_bitflags!( #[cfg(any(target_os = "macos", target_os = "ios"))] NOTE_EXITSTATUS, NOTE_EXTEND, - #[cfg(any(target_os = "macos", + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "dragonfly"))] diff --git a/src/sys/signal.rs b/src/sys/signal.rs index 309919b7eb..f885af9238 100644 --- a/src/sys/signal.rs +++ b/src/sys/signal.rs @@ -523,20 +523,20 @@ impl SigEvent { SigevNotify::SigevThreadId{..} => 4 // No SIGEV_THREAD_ID defined }; sev.sigev_signo = match sigev_notify { - SigevNotify::SigevSignal{ signal, .. } => signal as ::c_int, + SigevNotify::SigevSignal{ signal, .. } => signal as libc::c_int, #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] SigevNotify::SigevKevent{ kq, ..} => kq, #[cfg(any(target_os = "linux", target_os = "freebsd"))] - SigevNotify::SigevThreadId{ signal, .. } => signal as ::c_int, + SigevNotify::SigevThreadId{ signal, .. } => signal as libc::c_int, _ => 0 }; sev.sigev_value.sival_ptr = match sigev_notify { SigevNotify::SigevNone => ptr::null_mut::(), - SigevNotify::SigevSignal{ si_value, .. } => si_value as *mut ::c_void, + SigevNotify::SigevSignal{ si_value, .. } => si_value as *mut libc::c_void, #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] - SigevNotify::SigevKevent{ udata, .. } => udata as *mut ::c_void, + SigevNotify::SigevKevent{ udata, .. } => udata as *mut libc::c_void, #[cfg(any(target_os = "linux", target_os = "freebsd"))] - SigevNotify::SigevThreadId{ si_value, .. } => si_value as *mut ::c_void, + SigevNotify::SigevThreadId{ si_value, .. } => si_value as *mut libc::c_void, }; SigEvent::set_tid(&mut sev, &sigev_notify); SigEvent{sigevent: sev} From 415c563c36b1939fa39c5b0192a1b563a9f9bfa6 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Tue, 11 Jul 2017 22:35:52 -0700 Subject: [PATCH 10/16] Remove unnecessary constants --- src/sys/ioctl/platform/linux.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/sys/ioctl/platform/linux.rs b/src/sys/ioctl/platform/linux.rs index 6daa925762..117085d20b 100644 --- a/src/sys/ioctl/platform/linux.rs +++ b/src/sys/ioctl/platform/linux.rs @@ -113,12 +113,3 @@ macro_rules! iow { macro_rules! iorw { ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ | $crate::sys::ioctl::WRITE, $ty, $nr, $sz)) } - -#[doc(hidden)] -pub const IN: u32 = (WRITE as u32) << DIRSHIFT; -#[doc(hidden)] -pub const OUT: u32 = (READ as u32) << DIRSHIFT; -#[doc(hidden)] -pub const INOUT: u32 = ((READ|WRITE) as u32) << DIRSHIFT; -#[doc(hidden)] -pub const SIZE_MASK: u32 = SIZEMASK << SIZESHIFT; From b1c9e9d46fe0c6304374f533fa611a9741be3941 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Thu, 13 Jul 2017 11:30:52 -0700 Subject: [PATCH 11/16] Unify argument names to generated ioctl functions --- src/sys/ioctl/mod.rs | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index a2eb79b543..43114a5cce 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -94,7 +94,7 @@ //! More examples on using `ioctl!` can be found in the [rust-spidev crate](https://github.com/rust-embedded/rust-spidev). //! //! ```text -//! pub unsafe fn $NAME(fd: c_int, val: *mut u8, len: usize) -> Result; +//! pub unsafe fn $NAME(fd: c_int, data: *mut u8, len: usize) -> Result; //! ``` //! //! As mentioned earlier, there are many old `ioctl`s that do not use the newer method of @@ -117,7 +117,7 @@ //! The generated function has the same form as that generated by `read`: //! //! ```text -//! pub unsafe fn tcgets(fd: c_int, val: *mut u8) -> Result; +//! pub unsafe fn tcgets(fd: c_int, data: *mut u8) -> Result; //! ``` //! //! There is also a `bad none` form for use with hard-coded `ioctl`s that do not transfer data. @@ -191,46 +191,47 @@ macro_rules! ioctl { ); (read $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, - val: *mut $ty) + data: *mut $ty) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, val)) + convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); (write $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, - val: $ty) + data: $ty) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, val)) + convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); (readwrite $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, - val: *mut $ty) + data: *mut $ty) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, val)) + convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); (read buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, - val: *mut $ty, + data: *mut $ty, len: usize) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, val)) + convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, data)) } ); (write buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, - val: *const $ty, - len: usize) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, val)) + data: *const $ty, + len: usize) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, data)) } ); (readwrite buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, - val: *mut $ty, + data: *mut $ty, len: usize) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, val)) + convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, data)) } ); } From 85bb9d8101b3c59b4df2c08a07026782bcf710b2 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Tue, 18 Jul 2017 09:36:49 -0700 Subject: [PATCH 12/16] Split ioctl!(write ...) into write_ptr and write_int There two different write semantics used with ioctls: one involves passing a pointer the other involves passing an int. Previously the ioctl! macro did not distinguish between these cases and left it up to the user to set the proper datatype. This previous version was not type safe and prone to errors. The solution here is to split the "write" variant into a "write_ptr" and "write_int" variant that makes the semantics more explicit and improves type safety by specifying arguments better. --- src/sys/ioctl/mod.rs | 28 +++++++++++++++++++++++++--- test/sys/test_ioctl.rs | 9 +++++++-- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index 43114a5cce..058fc60a90 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -80,7 +80,22 @@ //! The return value for `ioctl` functions generated by the `ioctl!` macro are `nix::Error`s. //! These are generated by assuming the return value of the ioctl is `-1` on error and everything //! else is a valid return value. If this is not the case, `Result::map` can be used to map some -//! of the range of "good" values (-2..-Inf, 0..Inf) into a smaller range in a helper function. +//! of the range of "good" values (-Inf..-2, 0..Inf) into a smaller range in a helper function. +//! +//! Writing `ioctl`s generally use pointers as their data source and these should use the +//! `write_ptr` variant. But in some cases an `int` is passed directly. For these `ioctl`s use the +//! `write_int` variant of the `ioctl!` macro. This variant does not take a type as the last argument: +//! +//! ``` +//! # #[macro_use] extern crate nix; +//! const HCI_IOC_MAGIC: u8 = b'k'; +//! const HCI_IOC_HCIDEVUP: u8 = 1; +//! ioctl!(write_int hci_dev_up with HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP); +//! # fn main() {} +//! ``` +//! +//! Some `ioctl`s don't transfer any data, and those should use the `none` variant. This variant +//! doesn't take a type and so it is declared similar to the `write_int` variant shown above. //! //! The mode for a given `ioctl` should be clear from the documentation if it has good //! documentation. Otherwise it will be clear based on the macro used to generate the `ioctl` @@ -196,13 +211,20 @@ macro_rules! ioctl { convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (write $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + (write_ptr $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, - data: $ty) + data: *const $ty) -> $crate::Result<$crate::libc::c_int> { convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); + (write_int $name:ident with $ioty:expr, $nr:expr) => ( + pub unsafe fn $name(fd: $crate::libc::c_int, + data: $crate::libc::c_int) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$crate::libc::c_int>()) as $crate::sys::ioctl::ioctl_num_type, data)) + } + ); (readwrite $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, data: *mut $ty) diff --git a/test/sys/test_ioctl.rs b/test/sys/test_ioctl.rs index c0ae078a44..7f0018d332 100644 --- a/test/sys/test_ioctl.rs +++ b/test/sys/test_ioctl.rs @@ -5,10 +5,15 @@ ioctl!(bad do_bad with 0x1234); ioctl!(bad none do_bad_none with 0x1234); ioctl!(none do_none with 0, 0); ioctl!(read read_test with 0, 0; u32); -ioctl!(write write_test with 0, 0; u64); +ioctl!(write_int write_ptr_int with 0, 0); +ioctl!(write_ptr write_ptr_u8 with 0, 0; u8); +ioctl!(write_ptr write_ptr_u32 with 0, 0; u32); +ioctl!(write_ptr write_ptr_u64 with 0, 0; u64); ioctl!(readwrite readwrite_test with 0, 0; u64); ioctl!(read buf readbuf_test with 0, 0; u32); -ioctl!(write buf writebuf_test with 0, 0; u32); +ioctl!(write buf writebuf_test_u8 with 0, 0; u8); +ioctl!(write buf writebuf_test_u32 with 0, 0; u32); +ioctl!(write buf writebuf_test_u64 with 0, 0; u64); ioctl!(readwrite buf readwritebuf_test with 0, 0; u32); // See C code for source of values for op calculations (does NOT work for mips/powerpc): From c8cbbab3eb02794a3a23c7efdcab862810d8590b Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Thu, 13 Jul 2017 11:53:31 -0700 Subject: [PATCH 13/16] Add 'bad' prefixes for read, write_*, and readwrite ioctls --- src/sys/ioctl/mod.rs | 55 ++++++++++++++++++++++++++++++------------ test/sys/test_ioctl.rs | 7 ++++-- 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index 058fc60a90..f54384e61f 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -108,35 +108,37 @@ //! //! More examples on using `ioctl!` can be found in the [rust-spidev crate](https://github.com/rust-embedded/rust-spidev). //! -//! ```text -//! pub unsafe fn $NAME(fd: c_int, data: *mut u8, len: usize) -> Result; -//! ``` +//! Using hard-coded ioctl numbers +//! ------------------------------ //! //! As mentioned earlier, there are many old `ioctl`s that do not use the newer method of -//! generating `ioctl` numbers and instead use hardcoded values. These can be used with the `bad` -//! form of the `ioctl!` macro (there is no data transfer direction used with `bad`). The naming of -//! this comes from the Linux kernel which refers to these `ioctl`s as "bad". +//! generating `ioctl` numbers and instead use hardcoded values. These can be used with the `bad *` +//! variants of the `ioctl!` macro. This naming comes from the Linux kernel which refers to these +//! `ioctl`s as "bad". These are a different variant as they bypass calling the macro that generates +//! the ioctl number and instead use the defined value directly. //! //! For example the `TCGETS` `ioctl` reads a `termios` data structure for a given file descriptor. -//! It can be implemented as: +//! It's defined as `0x5401` in `ioctls.h` on Linux and can be implemented as: //! //! ``` //! # #[macro_use] extern crate nix; //! # #[cfg(any(target_os = "android", target_os = "linux"))] //! # use nix::libc::TCGETS as TCGETS; //! # #[cfg(any(target_os = "android", target_os = "linux"))] -//! ioctl!(bad tcgets with TCGETS); +//! # use nix::libc::termios as termios; +//! # #[cfg(any(target_os = "android", target_os = "linux"))] +//! ioctl!(bad read tcgets with TCGETS; termios); //! # fn main() {} //! ``` //! //! The generated function has the same form as that generated by `read`: //! //! ```text -//! pub unsafe fn tcgets(fd: c_int, data: *mut u8) -> Result; +//! pub unsafe fn tcgets(fd: c_int, data: *mut termios) -> Result; //! ``` //! -//! There is also a `bad none` form for use with hard-coded `ioctl`s that do not transfer data. -//! The `TIOCEXCL` `ioctl` that's part of the termios API can be implemented as: +//! There is also a `bad none`, `bad write_int`/`bad write_ptr`, and `bad readwrite` variant that work +//! similar to the standard `none`, `write_int`/`write_ptr`, and `readwrite` variants. //! //! ``` //! # #[macro_use] extern crate nix; @@ -185,17 +187,38 @@ macro_rules! convert_ioctl_res { /// Generates ioctl functions. See [::sys::ioctl](sys/ioctl/index.html). #[macro_export] macro_rules! ioctl { - (bad $name:ident with $nr:expr) => ( + (bad none $name:ident with $nr:expr) => ( + pub unsafe fn $name(fd: $crate::libc::c_int) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type)) + } + ); + (bad read $name:ident with $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, - data: *mut u8) + data: *mut $ty) -> $crate::Result<$crate::libc::c_int> { convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (bad none $name:ident with $nr:expr) => ( - pub unsafe fn $name(fd: $crate::libc::c_int) + (bad write_ptr $name:ident with $nr:expr; $ty:ty) => ( + pub unsafe fn $name(fd: $crate::libc::c_int, + data: *const $ty) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type)) + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) + } + ); + (bad write_int $name:ident with $nr:expr) => ( + pub unsafe fn $name(fd: $crate::libc::c_int, + data: $crate::libc::c_int) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) + } + ); + (bad readwrite $name:ident with $nr:expr; $ty:ty) => ( + pub unsafe fn $name(fd: $crate::libc::c_int, + data: *mut $ty) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) } ); (none $name:ident with $ioty:expr, $nr:expr) => ( diff --git a/test/sys/test_ioctl.rs b/test/sys/test_ioctl.rs index 7f0018d332..184322667a 100644 --- a/test/sys/test_ioctl.rs +++ b/test/sys/test_ioctl.rs @@ -1,8 +1,11 @@ #![allow(dead_code)] // Simple tests to ensure macro generated fns compile -ioctl!(bad do_bad with 0x1234); -ioctl!(bad none do_bad_none with 0x1234); +ioctl!(bad none do_bad with 0x1234); +ioctl!(bad read do_bad_read with 0x1234; u16); +ioctl!(bad write_int do_bad_write_int with 0x1234); +ioctl!(bad write_ptr do_bad_write_ptr with 0x1234; u8); +ioctl!(bad readwrite do_bad_readwrite with 0x1234; u32); ioctl!(none do_none with 0, 0); ioctl!(read read_test with 0, 0; u32); ioctl!(write_int write_ptr_int with 0, 0); From 3b87bc80b4db8ea5318c39738c0a1d8658f57e77 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Thu, 13 Jul 2017 11:41:27 -0700 Subject: [PATCH 14/16] Refactor ioctl! for buffers Instead of relying on the macro user to calculate the length in bytes do that within the macro itself --- src/sys/ioctl/mod.rs | 68 ++++++++++++++++++++++++++++++++---------- test/sys/test_ioctl.rs | 13 ++++---- 2 files changed, 61 insertions(+), 20 deletions(-) diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index f54384e61f..4db3fe760b 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -140,14 +140,55 @@ //! There is also a `bad none`, `bad write_int`/`bad write_ptr`, and `bad readwrite` variant that work //! similar to the standard `none`, `write_int`/`write_ptr`, and `readwrite` variants. //! +//! Working with arrays +//! -------------------- +//! +//! Some `ioctl`s work with entire arrays of elements. These are supported by the `*_buf` variants in +//! the `ioctl!` macro which can be used by specifying `read_buf`, `write_buf`, and +//! `readwrite_buf`. Note that there are no "bad" versions for working with buffers. The generated +//! functions include a `len` argument to specify the number of elements (where the type of each +//! element is specified in the macro). +//! +//! Again looking to the SPI `ioctl`s on Linux for an example, there is a `SPI_IOC_MESSAGE` `ioctl` +//! that queues up multiple SPI messages by writing an entire array of `spi_ioc_transfer` structs. +//! `linux/spi/spidev.h` defines a macro to calculate the `ioctl` number like: +//! +//! ```C +//! #define SPI_IOC_MAGIC 'k' +//! #define SPI_MSGSIZE(N) ... +//! #define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)]) +//! ``` +//! +//! The `SPI_MSGSIZE(N)` calculation is already handled by the `ioctl!` macro, so all that's +//! needed to define this `ioctl` is: +//! //! ``` //! # #[macro_use] extern crate nix; -//! # use nix::libc::TIOCEXCL as TIOCEXCL; -//! ioctl!(bad none tiocexcl with TIOCEXCL); +//! const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h +//! const SPI_IOC_TYPE_MESSAGE: u8 = 0; +//! # pub struct spi_ioc_transfer(u64); +//! ioctl!(write_buf spi_transfer with SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE; spi_ioc_transfer); //! # fn main() {} //! ``` //! -//! More examples on using `ioctl!` can be found in the [rust-spidev crate](https://github.com/rust-embedded/rust-spidev). +//! This generates a function like: +//! +//! ``` +//! # #[macro_use] extern crate nix; +//! # use std::mem; +//! # use nix::{Errno, libc, Result}; +//! # use nix::libc::c_int as c_int; +//! # const SPI_IOC_MAGIC: u8 = b'k'; +//! # const SPI_IOC_TYPE_MESSAGE: u8 = 0; +//! # pub struct spi_ioc_transfer(u64); +//! pub unsafe fn spi_message(fd: c_int, data: &mut [spi_ioc_transfer]) -> Result { +//! let res = libc::ioctl(fd, +//! iow!(SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE, data.len() * mem::size_of::()), +//! data); +//! Errno::result(res) +//! } +//! # fn main() {} +//! ``` //! //! Finding ioctl documentation //! --------------------------- @@ -255,28 +296,25 @@ macro_rules! ioctl { convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (read buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + (read_buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, - data: *mut $ty, - len: usize) + data: &mut [$ty]) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, data)) + convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (write buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + (write_buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, - data: *const $ty, - len: usize) + data: &[$ty]) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, data)) + convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (readwrite buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + (readwrite_buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( pub unsafe fn $name(fd: $crate::libc::c_int, - data: *mut $ty, - len: usize) + data: &mut [$ty]) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, len) as $crate::sys::ioctl::ioctl_num_type, data)) + convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); } diff --git a/test/sys/test_ioctl.rs b/test/sys/test_ioctl.rs index 184322667a..742103a41b 100644 --- a/test/sys/test_ioctl.rs +++ b/test/sys/test_ioctl.rs @@ -13,11 +13,14 @@ ioctl!(write_ptr write_ptr_u8 with 0, 0; u8); ioctl!(write_ptr write_ptr_u32 with 0, 0; u32); ioctl!(write_ptr write_ptr_u64 with 0, 0; u64); ioctl!(readwrite readwrite_test with 0, 0; u64); -ioctl!(read buf readbuf_test with 0, 0; u32); -ioctl!(write buf writebuf_test_u8 with 0, 0; u8); -ioctl!(write buf writebuf_test_u32 with 0, 0; u32); -ioctl!(write buf writebuf_test_u64 with 0, 0; u64); -ioctl!(readwrite buf readwritebuf_test with 0, 0; u32); +ioctl!(read_buf readbuf_test with 0, 0; u32); +const SPI_IOC_MAGIC: u8 = b'k'; +const SPI_IOC_MESSAGE: u8 = 0; +ioctl!(write_buf writebuf_test_consts with SPI_IOC_MAGIC, SPI_IOC_MESSAGE; u8); +ioctl!(write_buf writebuf_test_u8 with 0, 0; u8); +ioctl!(write_buf writebuf_test_u32 with 0, 0; u32); +ioctl!(write_buf writebuf_test_u64 with 0, 0; u64); +ioctl!(readwrite_buf readwritebuf_test with 0, 0; u32); // See C code for source of values for op calculations (does NOT work for mips/powerpc): // https://gist.github.com/posborne/83ea6880770a1aef332e From aeec79784f598b317d8320a17bc59e38880ea9cf Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Mon, 17 Jul 2017 11:15:42 -0700 Subject: [PATCH 15/16] Update changelog --- CHANGELOG.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 350fc117c2..817a89f45e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,11 +24,12 @@ This project adheres to [Semantic Versioning](http://semver.org/). and nix::Error::UnsupportedOperation}` ([#614](https://github.com/nix-rust/nix/pull/614)) - Added `cfmakeraw`, `cfsetspeed`, and `tcgetsid`. ([#527](https://github.com/nix-rust/nix/pull/527)) +- Added "bad none", "bad write_ptr", "bad write_int", and "bad readwrite" variants to the `ioctl!` + macro. ([#670](https://github.com/nix-rust/nix/pull/670)) ### Changed -- Changed `ioctl!(write ...)` to take argument by value instead as pointer. - If you need a pointer as argument, use `ioctl!(write buf ...)`. - ([#626](https://github.com/nix-rust/nix/pull/626)) +- Changed `ioctl!(write ...)` into `ioctl!(write_ptr ...)` and `ioctl!(write_int ..)` variants + to more clearly separate those use cases. ([#670](https://github.com/nix-rust/nix/pull/670)) - Marked `sys::mman::{ mmap, munmap, madvise, munlock, msync }` as unsafe. ([#559](https://github.com/nix-rust/nix/pull/559)) - Minimum supported Rust version is now 1.13. @@ -48,6 +49,11 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Revised the termios API including additional tests and documentation and exposed it on iOS. ([#527](https://github.com/nix-rust/nix/pull/527)) - `eventfd`, `signalfd`, and `pwritev`/`preadv` functionality is now included by default for all supported platforms. ([#681](https://github.com/nix-rust/nix/pull/561)) +- The `ioctl!` macro's plain variants has been replaced with "bad read" to be consistent with + other variants. The generated functions also have more strict types for their arguments. The + "*_buf" variants also now calculate total array size and take slice references for improved type + safety. The documentation has also been dramatically improved. + ([#670](https://github.com/nix-rust/nix/pull/670)) ### Removed - Removed `io::Error` from `nix::Error` and the conversion from `nix::Error` to `Errno` @@ -55,6 +61,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). - All feature flags have been removed in favor of conditional compilation on supported platforms. `execvpe` is no longer supported, but this was already broken and will be added back in the next release. ([#681](https://github.com/nix-rust/nix/pull/561)) +- Removed `ioc_*` functions and many helper constants and macros within the `ioctl` module. These + should always have been private and only the `ioctl!` should be used in public code. + ([#670](https://github.com/nix-rust/nix/pull/670)) ### Fixed - Fixed multiple issues compiling under different archetectures and OSes. @@ -73,6 +82,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#623](https://github.com/nix-rust/nix/pull/623)) - Multiple constants related to the termios API have now been properly defined for all supported platforms. ([#527](https://github.com/nix-rust/nix/pull/527)) +- `ioctl!` macro now supports working with non-int datatypes and properly supports all platforms. + ([#670](https://github.com/nix-rust/nix/pull/670)) ## [0.8.1] 2017-04-16 From 4525567da96a3a1e6af0b12c4903cdf155d48871 Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Thu, 20 Jul 2017 14:54:26 -0700 Subject: [PATCH 16/16] Add tests of actual ioctl usage --- test/sys/test_ioctl.rs | 179 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) diff --git a/test/sys/test_ioctl.rs b/test/sys/test_ioctl.rs index 742103a41b..38ecdc99af 100644 --- a/test/sys/test_ioctl.rs +++ b/test/sys/test_ioctl.rs @@ -146,3 +146,182 @@ mod bsd { assert_eq!(iorw!(b'z', 10, (1 as u64) << 32), 0xC0007A0A); } } + +#[cfg(any(target_os = "android", target_os = "linux"))] +mod linux_ioctls { + use std::mem; + use std::os::unix::io::AsRawFd; + + use tempfile::tempfile; + use libc::{TCGETS, TCSBRK, TCSETS, TIOCNXCL, termios}; + + use nix::Error::Sys; + use nix::errno::{ENOTTY, ENOSYS}; + + ioctl!(bad none tiocnxcl with TIOCNXCL); + #[test] + fn test_ioctl_bad_none() { + let file = tempfile().unwrap(); + let res = unsafe { tiocnxcl(file.as_raw_fd()) }; + assert_eq!(res, Err(Sys(ENOTTY))); + } + + ioctl!(bad read tcgets with TCGETS; termios); + #[test] + fn test_ioctl_bad_read() { + let file = tempfile().unwrap(); + let mut termios = unsafe { mem::uninitialized() }; + let res = unsafe { tcgets(file.as_raw_fd(), &mut termios) }; + assert_eq!(res, Err(Sys(ENOTTY))); + } + + ioctl!(bad write_int tcsbrk with TCSBRK); + #[test] + fn test_ioctl_bad_write_int() { + let file = tempfile().unwrap(); + let res = unsafe { tcsbrk(file.as_raw_fd(), 0) }; + assert_eq!(res, Err(Sys(ENOTTY))); + } + + ioctl!(bad write_ptr tcsets with TCSETS; termios); + #[test] + fn test_ioctl_bad_write_ptr() { + let file = tempfile().unwrap(); + let termios: termios = unsafe { mem::uninitialized() }; + let res = unsafe { tcsets(file.as_raw_fd(), &termios) }; + assert_eq!(res, Err(Sys(ENOTTY))); + } + + // FIXME: Find a suitable example for "bad readwrite". + + // From linux/videodev2.h + ioctl!(none log_status with b'V', 70); + #[test] + fn test_ioctl_none() { + let file = tempfile().unwrap(); + let res = unsafe { log_status(file.as_raw_fd()) }; + assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS))); + } + + #[repr(C)] + pub struct v4l2_audio { + index: u32, + name: [u8; 32], + capability: u32, + mode: u32, + reserved: [u32; 2], + } + + // From linux/videodev2.h + ioctl!(write_ptr s_audio with b'V', 34; v4l2_audio); + #[test] + fn test_ioctl_read() { + let file = tempfile().unwrap(); + let data: v4l2_audio = unsafe { mem::uninitialized() }; + let res = unsafe { g_audio(file.as_raw_fd(), &data) }; + assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS))); + } + + // From linux/net/bluetooth/hci_sock.h + const HCI_IOC_MAGIC: u8 = b'H'; + const HCI_IOC_HCIDEVUP: u8 = 201; + ioctl!(write_int hcidevup with HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP); + #[test] + fn test_ioctl_write_int() { + let file = tempfile().unwrap(); + let res = unsafe { hcidevup(file.as_raw_fd(), 0) }; + assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS))); + } + + // From linux/videodev2.h + ioctl!(write_ptr g_audio with b'V', 33; v4l2_audio); + #[test] + fn test_ioctl_write_ptr() { + let file = tempfile().unwrap(); + let mut data: v4l2_audio = unsafe { mem::uninitialized() }; + let res = unsafe { g_audio(file.as_raw_fd(), &mut data) }; + assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS))); + } + + // From linux/videodev2.h + ioctl!(readwrite enum_audio with b'V', 65; v4l2_audio); + #[test] + fn test_ioctl_readwrite() { + let file = tempfile().unwrap(); + let mut data: v4l2_audio = unsafe { mem::uninitialized() }; + let res = unsafe { enum_audio(file.as_raw_fd(), &mut data) }; + assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS))); + } + + // FIXME: Find a suitable example for read_buf. + + #[repr(C)] + pub struct spi_ioc_transfer { + tx_buf: u64, + rx_buf: u64, + len: u32, + speed_hz: u32, + delay_usecs: u16, + bits_per_word: u8, + cs_change: u8, + tx_nbits: u8, + rx_nbits: u8, + pad: u16, + } + + // From linux/spi/spidev.h + ioctl!(write_buf spi_ioc_message with super::SPI_IOC_MAGIC, super::SPI_IOC_MESSAGE; spi_ioc_transfer); + #[test] + fn test_ioctl_write_buf() { + let file = tempfile().unwrap(); + let mut data: [spi_ioc_transfer; 4] = unsafe { mem::uninitialized() }; + let res = unsafe { spi_ioc_message(file.as_raw_fd(), &mut data[..]) }; + assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS))); + } + + // FIXME: Find a suitable example for readwrite_buf. +} + +#[cfg(target_os = "freebsd")] +mod freebsd_ioctls { + use std::mem; + use std::os::unix::io::AsRawFd; + + use tempfile::tempfile; + use libc::termios; + + use nix::Error::Sys; + use nix::errno::ENOTTY; + + // From sys/sys/ttycom.h + const TTY_IOC_MAGIC: u8 = b't'; + const TTY_IOC_TYPE_NXCL: u8 = 14; + const TTY_IOC_TYPE_GETA: u8 = 19; + const TTY_IOC_TYPE_SETA: u8 = 20; + + ioctl!(none tiocnxcl with TTY_IOC_MAGIC, TTY_IOC_TYPE_NXCL); + #[test] + fn test_ioctl_none() { + let file = tempfile().unwrap(); + let res = unsafe { tiocnxcl(file.as_raw_fd()) }; + assert_eq!(res, Err(Sys(ENOTTY))); + } + + ioctl!(read tiocgeta with TTY_IOC_MAGIC, TTY_IOC_TYPE_GETA; termios); + #[test] + fn test_ioctl_read() { + let file = tempfile().unwrap(); + let mut termios = unsafe { mem::uninitialized() }; + let res = unsafe { tiocgeta(file.as_raw_fd(), &mut termios) }; + assert_eq!(res, Err(Sys(ENOTTY))); + } + + ioctl!(write_ptr tiocseta with TTY_IOC_MAGIC, TTY_IOC_TYPE_SETA; termios); + #[test] + fn test_ioctl_write_ptr() { + let file = tempfile().unwrap(); + let termios: termios = unsafe { mem::uninitialized() }; + let res = unsafe { tiocseta(file.as_raw_fd(), &termios) }; + assert_eq!(res, Err(Sys(ENOTTY))); + } +}