xref: /linux/drivers/block/rnull/configfs.rs (revision 4f38da1f027ea2c9f01bb71daa7a299c191b6940)
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