1d969d504SAndreas Hindborg // SPDX-License-Identifier: GPL-2.0 2d969d504SAndreas Hindborg 3d969d504SAndreas Hindborg use super::{NullBlkDevice, THIS_MODULE}; 4*34585dc6SAndreas Hindborg use core::fmt::{Display, Write}; 5d969d504SAndreas Hindborg use kernel::{ 6d969d504SAndreas Hindborg block::mq::gen_disk::{GenDisk, GenDiskBuilder}, 7d969d504SAndreas Hindborg c_str, 8d969d504SAndreas Hindborg configfs::{self, AttributeOperations}, 9d969d504SAndreas Hindborg configfs_attrs, new_mutex, 10d969d504SAndreas Hindborg page::PAGE_SIZE, 11d969d504SAndreas Hindborg prelude::*, 12d969d504SAndreas Hindborg str::{kstrtobool_bytes, CString}, 13d969d504SAndreas Hindborg sync::Mutex, 14d969d504SAndreas Hindborg }; 15d969d504SAndreas Hindborg use pin_init::PinInit; 16d969d504SAndreas Hindborg 17d969d504SAndreas Hindborg pub(crate) fn subsystem() -> impl PinInit<kernel::configfs::Subsystem<Config>, Error> { 18d969d504SAndreas Hindborg let item_type = configfs_attrs! { 19d969d504SAndreas Hindborg container: configfs::Subsystem<Config>, 20d969d504SAndreas Hindborg data: Config, 21d969d504SAndreas Hindborg child: DeviceConfig, 22d969d504SAndreas Hindborg attributes: [ 23d969d504SAndreas Hindborg features: 0, 24d969d504SAndreas Hindborg ], 25d969d504SAndreas Hindborg }; 26d969d504SAndreas Hindborg 27d969d504SAndreas Hindborg kernel::configfs::Subsystem::new(c_str!("rnull"), item_type, try_pin_init!(Config {})) 28d969d504SAndreas Hindborg } 29d969d504SAndreas Hindborg 30d969d504SAndreas Hindborg #[pin_data] 31d969d504SAndreas Hindborg pub(crate) struct Config {} 32d969d504SAndreas Hindborg 33d969d504SAndreas Hindborg #[vtable] 34d969d504SAndreas Hindborg impl AttributeOperations<0> for Config { 35d969d504SAndreas Hindborg type Data = Config; 36d969d504SAndreas Hindborg 37d969d504SAndreas Hindborg fn show(_this: &Config, page: &mut [u8; PAGE_SIZE]) -> Result<usize> { 38d969d504SAndreas Hindborg let mut writer = kernel::str::Formatter::new(page); 39*34585dc6SAndreas Hindborg writer.write_str("blocksize,size,rotational,irqmode\n")?; 40d969d504SAndreas Hindborg Ok(writer.bytes_written()) 41d969d504SAndreas Hindborg } 42d969d504SAndreas Hindborg } 43d969d504SAndreas Hindborg 44d969d504SAndreas Hindborg #[vtable] 45d969d504SAndreas Hindborg impl configfs::GroupOperations for Config { 46d969d504SAndreas Hindborg type Child = DeviceConfig; 47d969d504SAndreas Hindborg 48d969d504SAndreas Hindborg fn make_group( 49d969d504SAndreas Hindborg &self, 50d969d504SAndreas Hindborg name: &CStr, 51d969d504SAndreas Hindborg ) -> Result<impl PinInit<configfs::Group<DeviceConfig>, Error>> { 52d969d504SAndreas Hindborg let item_type = configfs_attrs! { 53d969d504SAndreas Hindborg container: configfs::Group<DeviceConfig>, 54d969d504SAndreas Hindborg data: DeviceConfig, 55d969d504SAndreas Hindborg attributes: [ 56d969d504SAndreas Hindborg // Named for compatibility with C null_blk 57d969d504SAndreas Hindborg power: 0, 58d969d504SAndreas Hindborg blocksize: 1, 59d969d504SAndreas Hindborg rotational: 2, 60d969d504SAndreas Hindborg size: 3, 61*34585dc6SAndreas Hindborg irqmode: 4, 62d969d504SAndreas Hindborg ], 63d969d504SAndreas Hindborg }; 64d969d504SAndreas Hindborg 65d969d504SAndreas Hindborg Ok(configfs::Group::new( 66d969d504SAndreas Hindborg name.try_into()?, 67d969d504SAndreas Hindborg item_type, 68d969d504SAndreas Hindborg // TODO: cannot coerce new_mutex!() to impl PinInit<_, Error>, so put mutex inside 69d969d504SAndreas Hindborg try_pin_init!( DeviceConfig { 70d969d504SAndreas Hindborg data <- new_mutex!(DeviceConfigInner { 71d969d504SAndreas Hindborg powered: false, 72d969d504SAndreas Hindborg block_size: 4096, 73d969d504SAndreas Hindborg rotational: false, 74d969d504SAndreas Hindborg disk: None, 75d969d504SAndreas Hindborg capacity_mib: 4096, 76*34585dc6SAndreas Hindborg irq_mode: IRQMode::None, 77d969d504SAndreas Hindborg name: name.try_into()?, 78d969d504SAndreas Hindborg }), 79d969d504SAndreas Hindborg }), 80d969d504SAndreas Hindborg )) 81d969d504SAndreas Hindborg } 82d969d504SAndreas Hindborg } 83d969d504SAndreas Hindborg 84*34585dc6SAndreas Hindborg #[derive(Debug, Clone, Copy)] 85*34585dc6SAndreas Hindborg pub(crate) enum IRQMode { 86*34585dc6SAndreas Hindborg None, 87*34585dc6SAndreas Hindborg Soft, 88*34585dc6SAndreas Hindborg } 89*34585dc6SAndreas Hindborg 90*34585dc6SAndreas Hindborg impl TryFrom<u8> for IRQMode { 91*34585dc6SAndreas Hindborg type Error = kernel::error::Error; 92*34585dc6SAndreas Hindborg 93*34585dc6SAndreas Hindborg fn try_from(value: u8) -> Result<Self> { 94*34585dc6SAndreas Hindborg match value { 95*34585dc6SAndreas Hindborg 0 => Ok(Self::None), 96*34585dc6SAndreas Hindborg 1 => Ok(Self::Soft), 97*34585dc6SAndreas Hindborg _ => Err(EINVAL), 98*34585dc6SAndreas Hindborg } 99*34585dc6SAndreas Hindborg } 100*34585dc6SAndreas Hindborg } 101*34585dc6SAndreas Hindborg 102*34585dc6SAndreas Hindborg impl Display for IRQMode { 103*34585dc6SAndreas Hindborg fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 104*34585dc6SAndreas Hindborg match self { 105*34585dc6SAndreas Hindborg Self::None => f.write_str("0")?, 106*34585dc6SAndreas Hindborg Self::Soft => f.write_str("1")?, 107*34585dc6SAndreas Hindborg } 108*34585dc6SAndreas Hindborg Ok(()) 109*34585dc6SAndreas Hindborg } 110*34585dc6SAndreas Hindborg } 111*34585dc6SAndreas Hindborg 112d969d504SAndreas Hindborg #[pin_data] 113d969d504SAndreas Hindborg pub(crate) struct DeviceConfig { 114d969d504SAndreas Hindborg #[pin] 115d969d504SAndreas Hindborg data: Mutex<DeviceConfigInner>, 116d969d504SAndreas Hindborg } 117d969d504SAndreas Hindborg 118d969d504SAndreas Hindborg #[pin_data] 119d969d504SAndreas Hindborg struct DeviceConfigInner { 120d969d504SAndreas Hindborg powered: bool, 121d969d504SAndreas Hindborg name: CString, 122d969d504SAndreas Hindborg block_size: u32, 123d969d504SAndreas Hindborg rotational: bool, 124d969d504SAndreas Hindborg capacity_mib: u64, 125*34585dc6SAndreas Hindborg irq_mode: IRQMode, 126d969d504SAndreas Hindborg disk: Option<GenDisk<NullBlkDevice>>, 127d969d504SAndreas Hindborg } 128d969d504SAndreas Hindborg 129d969d504SAndreas Hindborg #[vtable] 130d969d504SAndreas Hindborg impl configfs::AttributeOperations<0> for DeviceConfig { 131d969d504SAndreas Hindborg type Data = DeviceConfig; 132d969d504SAndreas Hindborg 133d969d504SAndreas Hindborg fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> { 134d969d504SAndreas Hindborg let mut writer = kernel::str::Formatter::new(page); 135d969d504SAndreas Hindborg 136d969d504SAndreas Hindborg if this.data.lock().powered { 137d969d504SAndreas Hindborg writer.write_str("1\n")?; 138d969d504SAndreas Hindborg } else { 139d969d504SAndreas Hindborg writer.write_str("0\n")?; 140d969d504SAndreas Hindborg } 141d969d504SAndreas Hindborg 142d969d504SAndreas Hindborg Ok(writer.bytes_written()) 143d969d504SAndreas Hindborg } 144d969d504SAndreas Hindborg 145d969d504SAndreas Hindborg fn store(this: &DeviceConfig, page: &[u8]) -> Result { 146d969d504SAndreas Hindborg let power_op = kstrtobool_bytes(page)?; 147d969d504SAndreas Hindborg let mut guard = this.data.lock(); 148d969d504SAndreas Hindborg 149d969d504SAndreas Hindborg if !guard.powered && power_op { 150d969d504SAndreas Hindborg guard.disk = Some(NullBlkDevice::new( 151d969d504SAndreas Hindborg &guard.name, 152d969d504SAndreas Hindborg guard.block_size, 153d969d504SAndreas Hindborg guard.rotational, 154d969d504SAndreas Hindborg guard.capacity_mib, 155*34585dc6SAndreas Hindborg guard.irq_mode, 156d969d504SAndreas Hindborg )?); 157d969d504SAndreas Hindborg guard.powered = true; 158d969d504SAndreas Hindborg } else if guard.powered && !power_op { 159d969d504SAndreas Hindborg drop(guard.disk.take()); 160d969d504SAndreas Hindborg guard.powered = false; 161d969d504SAndreas Hindborg } 162d969d504SAndreas Hindborg 163d969d504SAndreas Hindborg Ok(()) 164d969d504SAndreas Hindborg } 165d969d504SAndreas Hindborg } 166d969d504SAndreas Hindborg 167d969d504SAndreas Hindborg #[vtable] 168d969d504SAndreas Hindborg impl configfs::AttributeOperations<1> for DeviceConfig { 169d969d504SAndreas Hindborg type Data = DeviceConfig; 170d969d504SAndreas Hindborg 171d969d504SAndreas Hindborg fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> { 172d969d504SAndreas Hindborg let mut writer = kernel::str::Formatter::new(page); 173d969d504SAndreas Hindborg writer.write_fmt(fmt!("{}\n", this.data.lock().block_size))?; 174d969d504SAndreas Hindborg Ok(writer.bytes_written()) 175d969d504SAndreas Hindborg } 176d969d504SAndreas Hindborg 177d969d504SAndreas Hindborg fn store(this: &DeviceConfig, page: &[u8]) -> Result { 178d969d504SAndreas Hindborg if this.data.lock().powered { 179d969d504SAndreas Hindborg return Err(EBUSY); 180d969d504SAndreas Hindborg } 181d969d504SAndreas Hindborg 182d969d504SAndreas Hindborg let text = core::str::from_utf8(page)?.trim(); 183d969d504SAndreas Hindborg let value = text.parse::<u32>().map_err(|_| EINVAL)?; 184d969d504SAndreas Hindborg 185d969d504SAndreas Hindborg GenDiskBuilder::validate_block_size(value)?; 186d969d504SAndreas Hindborg this.data.lock().block_size = value; 187d969d504SAndreas Hindborg Ok(()) 188d969d504SAndreas Hindborg } 189d969d504SAndreas Hindborg } 190d969d504SAndreas Hindborg 191d969d504SAndreas Hindborg #[vtable] 192d969d504SAndreas Hindborg impl configfs::AttributeOperations<2> for DeviceConfig { 193d969d504SAndreas Hindborg type Data = DeviceConfig; 194d969d504SAndreas Hindborg 195d969d504SAndreas Hindborg fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> { 196d969d504SAndreas Hindborg let mut writer = kernel::str::Formatter::new(page); 197d969d504SAndreas Hindborg 198d969d504SAndreas Hindborg if this.data.lock().rotational { 199d969d504SAndreas Hindborg writer.write_str("1\n")?; 200d969d504SAndreas Hindborg } else { 201d969d504SAndreas Hindborg writer.write_str("0\n")?; 202d969d504SAndreas Hindborg } 203d969d504SAndreas Hindborg 204d969d504SAndreas Hindborg Ok(writer.bytes_written()) 205d969d504SAndreas Hindborg } 206d969d504SAndreas Hindborg 207d969d504SAndreas Hindborg fn store(this: &DeviceConfig, page: &[u8]) -> Result { 208d969d504SAndreas Hindborg if this.data.lock().powered { 209d969d504SAndreas Hindborg return Err(EBUSY); 210d969d504SAndreas Hindborg } 211d969d504SAndreas Hindborg 212d969d504SAndreas Hindborg this.data.lock().rotational = kstrtobool_bytes(page)?; 213d969d504SAndreas Hindborg 214d969d504SAndreas Hindborg Ok(()) 215d969d504SAndreas Hindborg } 216d969d504SAndreas Hindborg } 217d969d504SAndreas Hindborg 218d969d504SAndreas Hindborg #[vtable] 219d969d504SAndreas Hindborg impl configfs::AttributeOperations<3> for DeviceConfig { 220d969d504SAndreas Hindborg type Data = DeviceConfig; 221d969d504SAndreas Hindborg 222d969d504SAndreas Hindborg fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> { 223d969d504SAndreas Hindborg let mut writer = kernel::str::Formatter::new(page); 224d969d504SAndreas Hindborg writer.write_fmt(fmt!("{}\n", this.data.lock().capacity_mib))?; 225d969d504SAndreas Hindborg Ok(writer.bytes_written()) 226d969d504SAndreas Hindborg } 227d969d504SAndreas Hindborg 228d969d504SAndreas Hindborg fn store(this: &DeviceConfig, page: &[u8]) -> Result { 229d969d504SAndreas Hindborg if this.data.lock().powered { 230d969d504SAndreas Hindborg return Err(EBUSY); 231d969d504SAndreas Hindborg } 232d969d504SAndreas Hindborg 233d969d504SAndreas Hindborg let text = core::str::from_utf8(page)?.trim(); 234d969d504SAndreas Hindborg let value = text.parse::<u64>().map_err(|_| EINVAL)?; 235d969d504SAndreas Hindborg 236d969d504SAndreas Hindborg this.data.lock().capacity_mib = value; 237d969d504SAndreas Hindborg Ok(()) 238d969d504SAndreas Hindborg } 239d969d504SAndreas Hindborg } 240*34585dc6SAndreas Hindborg 241*34585dc6SAndreas Hindborg #[vtable] 242*34585dc6SAndreas Hindborg impl configfs::AttributeOperations<4> for DeviceConfig { 243*34585dc6SAndreas Hindborg type Data = DeviceConfig; 244*34585dc6SAndreas Hindborg 245*34585dc6SAndreas Hindborg fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> { 246*34585dc6SAndreas Hindborg let mut writer = kernel::str::Formatter::new(page); 247*34585dc6SAndreas Hindborg writer.write_fmt(fmt!("{}\n", this.data.lock().irq_mode))?; 248*34585dc6SAndreas Hindborg Ok(writer.bytes_written()) 249*34585dc6SAndreas Hindborg } 250*34585dc6SAndreas Hindborg 251*34585dc6SAndreas Hindborg fn store(this: &DeviceConfig, page: &[u8]) -> Result { 252*34585dc6SAndreas Hindborg if this.data.lock().powered { 253*34585dc6SAndreas Hindborg return Err(EBUSY); 254*34585dc6SAndreas Hindborg } 255*34585dc6SAndreas Hindborg 256*34585dc6SAndreas Hindborg let text = core::str::from_utf8(page)?.trim(); 257*34585dc6SAndreas Hindborg let value = text.parse::<u8>().map_err(|_| EINVAL)?; 258*34585dc6SAndreas Hindborg 259*34585dc6SAndreas Hindborg this.data.lock().irq_mode = IRQMode::try_from(value)?; 260*34585dc6SAndreas Hindborg Ok(()) 261*34585dc6SAndreas Hindborg } 262*34585dc6SAndreas Hindborg } 263