Skip to content

Commit 71d5614

Browse files
committed
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.
1 parent 932d6d2 commit 71d5614

File tree

1 file changed

+100
-62
lines changed

1 file changed

+100
-62
lines changed

src/sys/ioctl/mod.rs

Lines changed: 100 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
//! Provide helpers for making ioctl system calls
2-
//!
3-
//! Currently supports Linux on all architectures. Other platforms welcome!
1+
//! Provide helpers for making ioctl system calls.
42
//!
53
//! This library is pretty low-level and messy. `ioctl` is not fun.
64
//!
@@ -24,79 +22,119 @@
2422
//! to the CDROM device.
2523
//! * Do whatever else the device driver creator thought made most sense.
2624
//!
27-
//! `ioctl`s are synchronous system calls and are similar to read and
28-
//! write calls in that regard.
25+
//! `ioctl`s are synchronous system calls and are similar to read and write calls in that regard.
26+
//! They operate on file descriptors and have an identifier that specifies what the ioctl is.
27+
//! Additionally they may read or write data and therefore need to pass along a data pointer.
28+
//! Besides the semantics of the ioctls being confusing, the generation of this identifer can also
29+
//! be difficult.
2930
//!
30-
//! What does this module support?
31-
//! ===============================
31+
//! Historically `ioctl` numbers were arbitrary hard-coded values. This changed to a more-ordered
32+
//! system where the ioctl numbers had various subcomponents:
33+
//! * Number: The actual ioctl ID
34+
//! * Type: A grouping of ioctls for a common purpose or driver
35+
//! * Size: The size in bytes of the data that will be transferred
36+
//! * Direction: Whether there is any data and if it's read, write, or both
37+
//!
38+
//! Newer drivers should not generate complete integer identifiers for their `ioctl`s instead
39+
//! preferring to use the 4 components above to generate the final ioctl identifier. Because of
40+
//! how old `ioctl`s are, however, there are many hard-coded `ioctl` identifiers. These are
41+
//! commonly referred to as "bad" in `ioctl` documentation.
42+
//!
43+
//! Defining ioctls
44+
//! ===============
3245
//!
33-
//! This library provides the `ioctl!` macro, for binding `ioctl`s.
34-
//! Here's a few examples of how that can work for SPI under Linux
35-
//! from [rust-spidev](https://github.com/posborne/rust-spidev).
46+
//! This library provides the `ioctl!` macro, for binding `ioctl`s. This macro generates unsafe
47+
//! functions that can then be used for calling the ioctl. This macro has a few different ways it
48+
//! can be used depending on the specific ioctl you're working with.
49+
//!
50+
//! A simple `ioctl` is `SPI_IOC_RD_MODE`. This ioctl works with the SPI interface on Linux. This
51+
//! specific `ioctl` reads the mode of the SPI mode (one of 4 values, so 2 bits each) as a `u8`.
52+
//! It's declared in `/include/uapi/linux/spi/spidev.h` as `_IOR(SPI_IOC_MAGIC, 1, __u8)`. We can
53+
//! therefore generate a function to call this `ioctl` using the `ioctl!` macro in nix like:
3654
//!
3755
//! ```
38-
//! #[allow(non_camel_case_types)]
39-
//! pub struct spi_ioc_transfer {
40-
//! pub tx_buf: u64,
41-
//! pub rx_buf: u64,
42-
//! pub len: u32,
43-
//!
44-
//! // optional overrides
45-
//! pub speed_hz: u32,
46-
//! pub delay_usecs: u16,
47-
//! pub bits_per_word: u8,
48-
//! pub cs_change: u8,
49-
//! pub pad: u32,
50-
//! }
56+
//! # #[macro_use] extern crate nix;
57+
//! # const SPI_IOC_MAGIC: nix::libc::c_int = 'k' as nix::libc::c_int;
58+
//! ioctl!(read spi_read_mode with SPI_IOC_MAGIC, 1; u8);
59+
//! # fn main() {}
60+
//! ```
5161
//!
52-
//! #[cfg(linux)]
53-
//! mod ioctl {
54-
//! use super::*;
55-
//!
56-
//! const SPI_IOC_MAGIC: u8 = 'k' as u8;
57-
//! const SPI_IOC_NR_TRANSFER: u8 = 0;
58-
//! const SPI_IOC_NR_MODE: u8 = 1;
59-
//! const SPI_IOC_NR_LSB_FIRST: u8 = 2;
60-
//! const SPI_IOC_NR_BITS_PER_WORD: u8 = 3;
61-
//! const SPI_IOC_NR_MAX_SPEED_HZ: u8 = 4;
62-
//! const SPI_IOC_NR_MODE32: u8 = 5;
63-
//!
64-
//! ioctl!(read get_mode_u8 with SPI_IOC_MAGIC, SPI_IOC_NR_MODE; u8);
65-
//! ioctl!(read get_mode_u32 with SPI_IOC_MAGIC, SPI_IOC_NR_MODE; u32);
66-
//! ioctl!(write set_mode_u8 with SPI_IOC_MAGIC, SPI_IOC_NR_MODE; u8);
67-
//! ioctl!(write set_mode_u32 with SPI_IOC_MAGIC, SPI_IOC_NR_MODE32; u32);
68-
//! ioctl!(read get_lsb_first with SPI_IOC_MAGIC, SPI_IOC_NR_LSB_FIRST; u8);
69-
//! ioctl!(write set_lsb_first with SPI_IOC_MAGIC, SPI_IOC_NR_LSB_FIRST; u8);
70-
//! ioctl!(read get_bits_per_word with SPI_IOC_MAGIC, SPI_IOC_NR_BITS_PER_WORD; u8);
71-
//! ioctl!(write set_bits_per_word with SPI_IOC_MAGIC, SPI_IOC_NR_BITS_PER_WORD; u8);
72-
//! ioctl!(read get_max_speed_hz with SPI_IOC_MAGIC, SPI_IOC_NR_MAX_SPEED_HZ; u32);
73-
//! ioctl!(write set_max_speed_hz with SPI_IOC_MAGIC, SPI_IOC_NR_MAX_SPEED_HZ; u32);
74-
//! ioctl!(write spidev_transfer with SPI_IOC_MAGIC, SPI_IOC_NR_TRANSFER; spi_ioc_transfer);
75-
//! ioctl!(write buf spidev_transfer_buf with SPI_IOC_MAGIC, SPI_IOC_NR_TRANSFER; spi_ioc_transfer);
62+
//! This generates a function like:
63+
//!
64+
//! ```
65+
//! # #[macro_use] extern crate nix;
66+
//! # use nix::libc::c_int as c_int;
67+
//! # use nix::libc::c_ulong as c_ulong;
68+
//! # use std::mem;
69+
//! # use nix::{Errno, libc, Result};
70+
//! # const SPI_IOC_MAGIC: u8 = 'k' as u8;
71+
//! pub unsafe fn spi_read_mode(fd: c_int, val: *mut u8) -> Result<c_int> {
72+
//! let res = libc::ioctl(fd, ior!(SPI_IOC_MAGIC, 1, mem::size_of::<u8>()), val);
73+
//! Errno::result(res)
7674
//! }
75+
//! # fn main() {}
76+
//! ```
77+
//!
78+
//! The return value for `ioctl` functions generated by the `ioctl!` macro are assumed to return -1
79+
//! on error and everything else is a valid return value. If the return value needs to be changed,
80+
//! you can use `Result::map` on the return value in a helper function.
7781
//!
78-
//! // doctest workaround
79-
//! fn main() {}
82+
//! There are equivalent forms for `write`, `none` (no data in or out), and `readwrite`. The mode
83+
//! for a given `ioctl` should be clear from the documentation if it has good documentation.
84+
//! Otherwise it will be clear based on the macro used to generate the `ioctl` number where
85+
//! `_IOC`, _IOR`, `_IOW`, and `_IORW` map to "none", "read", "write", and "readwrite"
86+
//! respectively.
87+
//!
88+
//! Some `ioctl`s can operate on arrays of objects, so there are `read buf`, `write buf`, and
89+
//! `readwrite buf` forms as well. These generate functions that include a `len` argument to
90+
//! specify the length of the array:
91+
//!
92+
//! ```text
93+
//! pub unsafe fn $NAME(fd: c_int, val: *mut u8, len: usize) -> Result<c_int>;
8094
//! ```
8195
//!
82-
//! Spidev uses the `_IOC` macros that are encouraged (as far as
83-
//! `ioctl` can be encouraged at all) for newer drivers. Many
84-
//! drivers, however, just use magic numbers with no attached
85-
//! semantics. For those, the `ioctl!(bad ...)` variant should be
86-
//! used (the "bad" terminology is from the Linux kernel).
96+
//! As mentioned earlier, there are many old `ioctl`s that do not use the newer method of
97+
//! generating `ioctl` numbers and instead use hardcoded values. These can be used with the `bad`
98+
//! form of the `ioctl!` macro (there is no data transfer direction used with `bad`). The naming of
99+
//! this comes from the Linux kernel which refers to these `ioctl`s as "bad".
100+
//!
101+
//! For example the `TCGETS` `ioctl` reads a `termios` data structure for a given file descriptor.
102+
//! It can be implemented as:
103+
//!
104+
//! ```
105+
//! # #[macro_use] extern crate nix;
106+
//! # #[cfg(any(target_os = "android", target_os = "linux"))]
107+
//! # use nix::libc::TCGETS as TCGETS;
108+
//! # #[cfg(any(target_os = "android", target_os = "linux"))]
109+
//! ioctl!(bad tcgets with TCGETS);
110+
//! # fn main() {}
111+
//! ```
112+
//!
113+
//! The generated function has the same form as that generated by `read`:
114+
//!
115+
//! ```text
116+
//! pub unsafe fn tcgets(fd: c_int, val: *mut u8) -> Result<c_int>;
117+
//! ```
118+
//!
119+
//! There is also a `bad none` form for use with hard-coded `ioctl`s that do not transfer data.
120+
//! The `TIOCEXCL` `ioctl` that's part of the termios API can be implemented as:
121+
//!
122+
//! ```
123+
//! # #[macro_use] extern crate nix;
124+
//! # use nix::libc::TIOCEXCL as TIOCEXCL;
125+
//! ioctl!(bad none tiocexcl with TIOCEXCL);
126+
//! # fn main() {}
127+
//! ```
128+
//!
129+
//! More examples on using `ioctl!` can be found in the [rust-spidev crate](https://github.com/posborne/rust-spidev).
87130
//!
88131
//! How do I get the magic numbers?
89132
//! ===============================
90133
//!
91134
//! For Linux, look at your system's headers. For example, `/usr/include/linux/input.h` has a lot
92-
//! of lines defining macros which use `_IOR`, `_IOW`, `_IOC`, and `_IORW`. These macros
93-
//! correspond to the `ior!`, `iow!`, `ioc!`, and `iorw!` macros defined in this crate.
94-
//! Additionally, there is the `ioctl!` macro for creating a wrapper around `ioctl` that is
95-
//! somewhat more type-safe.
96-
//!
97-
//! Most `ioctl`s have no or little documentation. You'll need to scrounge through
98-
//! the source to figure out what they do and how they should be used.
99-
//!
135+
//! of lines defining macros which use `_IOR`, `_IOW`, `_IOC`, and `_IORW`. Finding documentation
136+
//! on some of those `ioctl`s can be trickier, however, though some are documented in the manpages
137+
//! and others are documented directly in the source.
100138
#[cfg(any(target_os = "linux", target_os = "android"))]
101139
#[path = "platform/linux.rs"]
102140
#[macro_use]

0 commit comments

Comments
 (0)