// SPDX-License-Identifier: GPL-2.0 //! Rust miscellaneous device sample. use kernel::prelude::*; use kernel::{ file::{self, File}, io_buffer::{IoBufferReader, IoBufferWriter}, miscdev, sync::{CondVar, Mutex, Ref, RefBorrow, UniqueRef}, }; module! { type: RustMiscdev, name: b"rust_miscdev", author: b"Rust for Linux Contributors", description: b"Rust miscellaneous device sample", license: b"GPL", } const MAX_TOKENS: usize = 3; struct SharedStateInner { token_count: usize, } struct SharedState { state_changed: CondVar, inner: Mutex, } impl SharedState { fn try_new() -> Result> { let mut state = Pin::from(UniqueRef::try_new(Self { // SAFETY: `condvar_init!` is called below. state_changed: unsafe { CondVar::new() }, // SAFETY: `mutex_init!` is called below. inner: unsafe { Mutex::new(SharedStateInner { token_count: 0 }) }, })?); // SAFETY: `state_changed` is pinned when `state` is. let pinned = unsafe { state.as_mut().map_unchecked_mut(|s| &mut s.state_changed) }; kernel::condvar_init!(pinned, "SharedState::state_changed"); // SAFETY: `inner` is pinned when `state` is. let pinned = unsafe { state.as_mut().map_unchecked_mut(|s| &mut s.inner) }; kernel::mutex_init!(pinned, "SharedState::inner"); Ok(state.into()) } } struct Token; impl file::Operations for Token { type Data = Ref; type OpenData = Ref; kernel::declare_file_operations!(read, write); fn open(shared: &Ref, _file: &File) -> Result { Ok(shared.clone()) } fn read( shared: RefBorrow<'_, SharedState>, _: &File, data: &mut impl IoBufferWriter, offset: u64, ) -> Result { // Succeed if the caller doesn't provide a buffer or if not at the start. if data.is_empty() || offset != 0 { return Ok(0); } { let mut inner = shared.inner.lock(); // Wait until we are allowed to decrement the token count or a signal arrives. while inner.token_count == 0 { if shared.state_changed.wait(&mut inner) { return Err(EINTR); } } // Consume a token. inner.token_count -= 1; } // Notify a possible writer waiting. shared.state_changed.notify_all(); // Write a one-byte 1 to the reader. data.write_slice(&[1u8; 1])?; Ok(1) } fn write( shared: RefBorrow<'_, SharedState>, _: &File, data: &mut impl IoBufferReader, _offset: u64, ) -> Result { { let mut inner = shared.inner.lock(); // Wait until we are allowed to increment the token count or a signal arrives. while inner.token_count == MAX_TOKENS { if shared.state_changed.wait(&mut inner) { return Err(EINTR); } } // Increment the number of token so that a reader can be released. inner.token_count += 1; } // Notify a possible reader waiting. shared.state_changed.notify_all(); Ok(data.len()) } } struct RustMiscdev { _dev: Pin>>, } impl kernel::Module for RustMiscdev { fn init(name: &'static CStr, _module: &'static ThisModule) -> Result { pr_info!("Rust miscellaneous device sample (init)\n"); let state = SharedState::try_new()?; Ok(RustMiscdev { _dev: miscdev::Registration::new_pinned(fmt!("{name}"), state)?, }) } } impl Drop for RustMiscdev { fn drop(&mut self) { pr_info!("Rust miscellaneous device sample (exit)\n"); } }