summaryrefslogtreecommitdiff
path: root/src/sys/ioctl/mod.rs
blob: a2eb79b543cca3d317c9454b0f8cb958ae68a4b7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
//! 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
//! 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.
//! 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:
//!
//! ```
//! # #[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 generates the function:
//!
//! ```
//! # #[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<c_int> {
//!     let res = libc::ioctl(fd, ior!(SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE, mem::size_of::<u8>()), data);
//!     Errno::result(res)
//! }
//! # fn main() {}
//! ```
//!
//! 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).
//!
//! ```text
//! pub unsafe fn $NAME(fd: c_int, val: *mut u8, len: usize) -> Result<c_int>;
//! ```
//!
//! 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".
//!
//! For example the `TCGETS` `ioctl` reads a `termios` data structure for a given file descriptor.
//! It 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);
//! # 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<c_int>;
//! ```
//!
//! 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:
//!
//! ```
//! # #[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]
mod platform;

#[cfg(any(target_os = "macos",
          target_os = "ios",
          target_os = "netbsd",
          target_os = "openbsd",
          target_os = "freebsd",
          target_os = "dragonfly"))]
#[path = "platform/bsd.rs"]
#[macro_use]
mod platform;

pub use self::platform::*;

/// Convert raw ioctl return value to a Nix result
#[macro_export]
#[doc(hidden)]
macro_rules! convert_ioctl_res {
    ($w:expr) => (
        {
            $crate::Errno::result($w)
        }
    );
}

/// 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::libc::c_int,
                            data: *mut u8)
                            -> $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)
                            -> $crate::Result<$crate::libc::c_int> {
            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::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::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::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::sys::ioctl::ioctl_num_type, val))
        }
        );
    (read buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        pub unsafe fn $name(fd: $crate::libc::c_int,
                            val: *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))
        }
        );
    (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))
        }
        );
    (readwrite buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        pub unsafe fn $name(fd: $crate::libc::c_int,
                            val: *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))
        }
        );
}