Skip to content

Commit da1858c

Browse files
committed
ioctl: crate for performing board ioctls
1 parent 3d7ea23 commit da1858c

File tree

8 files changed

+116
-37
lines changed

8 files changed

+116
-37
lines changed

.github/workflows/build.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ jobs:
2525
run: |
2626
cargo fmt --check --manifest-path openemc-bootloader/Cargo.toml
2727
cargo fmt --check --manifest-path openemc-firmware/Cargo.toml
28+
cargo fmt --check --manifest-path openemc-io/Cargo.toml
2829
cargo fmt --check --manifest-path openemc-log/Cargo.toml
2930
cargo fmt --check --manifest-path openemc-pack/Cargo.toml
3031
cargo fmt --check --manifest-path openemc-shared/Cargo.toml
@@ -33,6 +34,7 @@ jobs:
3334
run: |
3435
cd openemc-bootloader ; cargo clippy --quiet ; cd ..
3536
cd openemc-firmware ; cargo clippy --quiet ; cd ..
37+
cd openemc-io ; cargo clippy --quiet ; cd ..
3638
cd openemc-log ; cargo clippy --quiet ; cd ..
3739
cd openemc-pack ; cargo clippy --quiet ; cd ..
3840
cd openemc-shared ; cargo clippy --quiet ; cd ..
@@ -41,6 +43,8 @@ jobs:
4143
run: cd openemc-bootloader ; cargo build --release --quiet
4244
- name: Build firmware
4345
run: cd openemc-firmware ; cargo build --release --quiet
46+
- name: Build io
47+
run: cd openemc-io ; cargo build --release --quiet
4448
- name: Build logger
4549
run: cd openemc-log ; cargo build --release --quiet
4650
- name: Build packer

Cargo.lock

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
members = [
33
"board-io-test",
44
"openemc-build",
5+
"openemc-io",
56
"openemc-log",
67
"openemc-pack",
78
"xtask",

board-io-test/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ license = "GPL-3.0"
55
edition = "2021"
66

77
[dependencies]
8+
openemc-io = { path = "../openemc-io" }
89
rand = "0.10"
9-
nix = { version = "0.31", features = ["ioctl"] }

board-io-test/src/main.rs

Lines changed: 13 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,11 @@
33
//! For use with STM Nucleo-F103RB example board (`stm_nucleo_f103rb.rs`).
44
//!
55
6-
use nix::ioctl_readwrite;
6+
use openemc_io::{OpenEmc, OpenEmcIoctlError};
77
use rand::prelude::*;
8-
use std::{
9-
fs::File,
10-
io::{Read, Write},
11-
os::fd::AsRawFd,
12-
};
8+
use std::io::{Read, Write};
139

14-
const OPENEMC_MAX_DATA_SIZE: usize = 32;
15-
const OPENEMC_IOC_MAGIC: u8 = 0xEC;
16-
17-
#[repr(C)]
18-
struct OpenemcIoctlData {
19-
len: u8,
20-
data: [u8; OPENEMC_MAX_DATA_SIZE],
21-
}
22-
23-
ioctl_readwrite!(openemc_board_ioctl, OPENEMC_IOC_MAGIC, 0, OpenemcIoctlData);
24-
25-
fn io_test(openemc: &mut File) {
10+
fn io_test(openemc: &mut OpenEmc) {
2611
let mut test_data = vec![0u8; 32];
2712
rand::rng().fill(test_data.as_mut_slice());
2813

@@ -43,37 +28,30 @@ fn io_test(openemc: &mut File) {
4328
println!("IO test done");
4429
}
4530

46-
fn ioctl_test(openemc: &mut File) {
31+
fn ioctl_test(openemc: &mut OpenEmc) {
4732
println!("ioctl test");
4833

4934
let mut test_data = vec![0u8; 32];
5035
rand::rng().fill(test_data.as_mut_slice());
5136

52-
let mut ioctl_data = OpenemcIoctlData { len: 30, data: [0u8; OPENEMC_MAX_DATA_SIZE] };
53-
ioctl_data.data[..30].copy_from_slice(&test_data[..30]);
54-
55-
let ret = unsafe { openemc_board_ioctl(openemc.as_raw_fd(), &mut ioctl_data) };
56-
assert!(ret.is_ok(), "ioctl failed");
57-
assert_eq!(ret.unwrap(), 25, "ioctl return value mismatch");
58-
assert_eq!(ioctl_data.len, 25, "ioctl response length mismatch");
37+
let result = openemc.ioctl(&test_data[..30]).unwrap();
38+
assert_eq!(result.len(), 25, "ioctl response length mismatch");
5939

6040
let expected: Vec<_> = test_data.iter().map(|v| v.wrapping_add(1)).collect();
61-
assert_eq!(&ioctl_data.data[..25], &expected[..25], "ioctl data mismatch");
41+
assert_eq!(&result[..25], &expected[..25], "ioctl data mismatch");
6242

63-
ioctl_data.len = 32;
64-
ioctl_data.data.copy_from_slice(&test_data);
65-
let ret = unsafe { openemc_board_ioctl(openemc.as_raw_fd(), &mut ioctl_data) };
66-
assert!(ret.is_err(), "ioctl should have failed");
67-
let errno = ret.unwrap_err() as i32;
68-
assert_eq!(errno, 10, "wrong errno");
43+
let result = openemc.ioctl(&test_data);
44+
assert!(result.is_err(), "ioctl should have failed");
45+
let err = result.unwrap_err();
46+
let ioctl_err = err.get_ref().unwrap().downcast_ref::<OpenEmcIoctlError>().unwrap();
47+
assert_eq!(ioctl_err.0, 10, "wrong errno");
6948

7049
println!("ioctl test done");
7150
}
7251

7352
fn main() {
7453
println!("Opening /dev/openemc");
75-
let mut openemc =
76-
File::options().read(true).write(true).open("/dev/openemc").expect("cannot open /dev/openemc");
54+
let mut openemc = OpenEmc::new().expect("cannot open /dev/openemc");
7755

7856
io_test(&mut openemc);
7957
ioctl_test(&mut openemc);

openemc-io/Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[package]
2+
name = "openemc-io"
3+
description = "OpenEMC board IO"
4+
license = "LGPL-3.0"
5+
edition = "2021"
6+
7+
[dependencies]
8+
rustix = "1"

openemc-io/src/lib.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//! OpenEMC board IO.
2+
3+
use rustix::ioctl::{self, opcode, Opcode};
4+
use std::{fmt, fs::File, io};
5+
6+
/// OpenEMC ioctl error.
7+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8+
pub struct OpenEmcIoctlError(pub u8);
9+
10+
impl fmt::Display for OpenEmcIoctlError {
11+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
12+
write!(f, "OpenEMC board ioctl error {}", self.0)
13+
}
14+
}
15+
16+
impl std::error::Error for OpenEmcIoctlError {}
17+
18+
impl From<OpenEmcIoctlError> for io::Error {
19+
fn from(e: OpenEmcIoctlError) -> Self {
20+
io::Error::other(e)
21+
}
22+
}
23+
24+
const OPENEMC_IOC_MAGIC: u8 = 0xEC;
25+
26+
#[repr(C)]
27+
struct OpenEmcIoctlData {
28+
len: u8,
29+
data: [u8; Self::MAX_DATA_SIZE],
30+
}
31+
32+
impl OpenEmcIoctlData {
33+
const MAX_DATA_SIZE: usize = 32;
34+
const OPCODE: Opcode = opcode::read_write::<Self>(OPENEMC_IOC_MAGIC, 0);
35+
}
36+
37+
/// OpenEMC access.
38+
#[derive(Debug)]
39+
pub struct OpenEmc(File);
40+
41+
impl OpenEmc {
42+
/// Maximum request size.
43+
pub const MAX_REQ_SIZE: usize = OpenEmcIoctlData::MAX_DATA_SIZE;
44+
45+
/// Opens the OpenEMC board IO device.
46+
pub fn new() -> io::Result<Self> {
47+
Ok(Self(File::options().read(true).write(true).open("/dev/openemc")?))
48+
}
49+
50+
/// Performs an OpenEMC board ioctl.
51+
///
52+
/// `req` length must not exceed [`Self::MAX_REQ_SIZE`].
53+
pub fn ioctl(&self, req: &[u8]) -> io::Result<Vec<u8>> {
54+
assert!(req.len() <= OpenEmcIoctlData::MAX_DATA_SIZE, "OpenEMC ioctl request too big");
55+
56+
let mut ioc = OpenEmcIoctlData { len: req.len() as u8, data: [0; OpenEmcIoctlData::MAX_DATA_SIZE] };
57+
ioc.data[..req.len()].copy_from_slice(req);
58+
59+
unsafe { ioctl::ioctl(&self.0, ioctl::Updater::<{ OpenEmcIoctlData::OPCODE }, _>::new(&mut ioc)) }
60+
.map_err(|e| OpenEmcIoctlError(e.raw_os_error() as u8))?;
61+
62+
Ok(ioc.data[..ioc.len as usize].to_vec())
63+
}
64+
}
65+
66+
impl io::Read for OpenEmc {
67+
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
68+
self.0.read(buf)
69+
}
70+
}
71+
72+
impl io::Write for OpenEmc {
73+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
74+
self.0.write(buf)
75+
}
76+
77+
fn flush(&mut self) -> io::Result<()> {
78+
self.0.flush()
79+
}
80+
}

scripts/crates-fmt.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ cd ..
1111
cargo +nightly fmt
1212
cargo +nightly fmt --manifest-path openemc-bootloader/Cargo.toml
1313
cargo +nightly fmt --manifest-path openemc-firmware/Cargo.toml
14+
cargo +nightly fmt --manifest-path openemc-io/Cargo.toml
1415
cargo +nightly fmt --manifest-path openemc-shared/Cargo.toml
1516
cargo +nightly fmt --manifest-path openemc-log/Cargo.toml
1617
cargo +nightly fmt --manifest-path openemc-pack/Cargo.toml

0 commit comments

Comments
 (0)