|
1 | 1 | // SPDX-License-Identifier: GPL-2.0
|
2 | 2 |
|
3 | 3 | use core::convert::TryInto;
|
4 |
| -use core::mem; |
5 |
| -use core::ops::Range; |
6 |
| - |
7 |
| -use alloc::boxed::Box; |
8 |
| -use alloc::vec; |
9 |
| -use alloc::vec::Vec; |
| 4 | +use core::marker::PhantomPinned; |
| 5 | +use core::mem::MaybeUninit; |
| 6 | +use core::pin::Pin; |
10 | 7 |
|
11 | 8 | use crate::bindings;
|
12 | 9 | use crate::c_types;
|
13 | 10 | use crate::error::{Error, KernelResult};
|
14 | 11 | use crate::file_operations;
|
15 | 12 | use crate::types::CStr;
|
16 | 13 |
|
17 |
| -pub fn builder(name: CStr<'static>, minors: Range<u16>) -> KernelResult<Builder> { |
18 |
| - Ok(Builder { |
19 |
| - name, |
20 |
| - minors, |
21 |
| - file_ops: vec![], |
22 |
| - }) |
| 14 | +struct RegistrationInner<const N: usize> { |
| 15 | + dev: bindings::dev_t, |
| 16 | + used: usize, |
| 17 | + cdevs: [MaybeUninit<bindings::cdev>; N], |
| 18 | + _pin: PhantomPinned, |
23 | 19 | }
|
24 | 20 |
|
25 |
| -pub struct Builder { |
| 21 | +/// chrdev registration. May contain up to a fixed number (N) of devices. Must |
| 22 | +/// be pinned. |
| 23 | +pub struct Registration<const N: usize> { |
26 | 24 | name: CStr<'static>,
|
27 |
| - minors: Range<u16>, |
28 |
| - file_ops: Vec<&'static bindings::file_operations>, |
| 25 | + minors_start: u16, |
| 26 | + this_module: &'static crate::ThisModule, |
| 27 | + inner: Option<RegistrationInner<N>>, |
29 | 28 | }
|
30 | 29 |
|
31 |
| -impl Builder { |
32 |
| - pub fn register_device<T: file_operations::FileOperations>(mut self) -> Builder { |
33 |
| - if self.file_ops.len() >= self.minors.len() { |
34 |
| - panic!("More devices registered than minor numbers allocated.") |
| 30 | +impl<const N: usize> Registration<{ N }> { |
| 31 | + pub fn new( |
| 32 | + name: CStr<'static>, |
| 33 | + minors_start: u16, |
| 34 | + this_module: &'static crate::ThisModule, |
| 35 | + ) -> Self { |
| 36 | + Registration { |
| 37 | + name, |
| 38 | + minors_start, |
| 39 | + this_module, |
| 40 | + inner: None, |
35 | 41 | }
|
36 |
| - self.file_ops |
37 |
| - .push(&file_operations::FileOperationsVtable::<T>::VTABLE); |
38 |
| - self |
39 | 42 | }
|
40 | 43 |
|
41 |
| - pub fn build(self, this_module: &'static crate::ThisModule) -> KernelResult<Registration> { |
42 |
| - let mut dev: bindings::dev_t = 0; |
43 |
| - let res = unsafe { |
44 |
| - bindings::alloc_chrdev_region( |
45 |
| - &mut dev, |
46 |
| - self.minors.start.into(), |
47 |
| - self.minors.len().try_into()?, |
48 |
| - self.name.as_ptr() as *const c_types::c_char, |
49 |
| - ) |
50 |
| - }; |
51 |
| - if res != 0 { |
52 |
| - return Err(Error::from_kernel_errno(res)); |
| 44 | + pub fn register<T: file_operations::FileOperations>(self: Pin<&mut Self>) -> KernelResult<()> { |
| 45 | + // SAFETY: we must ensure that we never move out of `this`. |
| 46 | + let this = unsafe { self.get_unchecked_mut() }; |
| 47 | + if this.inner.is_none() { |
| 48 | + let mut dev: bindings::dev_t = 0; |
| 49 | + // SAFETY: Calling unsafe function. `this.name` has 'static |
| 50 | + // lifetime |
| 51 | + let res = unsafe { |
| 52 | + bindings::alloc_chrdev_region( |
| 53 | + &mut dev, |
| 54 | + this.minors_start.into(), |
| 55 | + N.try_into()?, |
| 56 | + this.name.as_ptr() as *const c_types::c_char, |
| 57 | + ) |
| 58 | + }; |
| 59 | + if res != 0 { |
| 60 | + return Err(Error::from_kernel_errno(res)); |
| 61 | + } |
| 62 | + this.inner = Some(RegistrationInner { |
| 63 | + dev: dev, |
| 64 | + used: 0, |
| 65 | + cdevs: [MaybeUninit::<bindings::cdev>::uninit(); N], |
| 66 | + _pin: PhantomPinned, |
| 67 | + }); |
53 | 68 | }
|
54 | 69 |
|
55 |
| - // Turn this into a boxed slice immediately because the kernel stores pointers into it, and |
56 |
| - // so that data should never be moved. |
57 |
| - let mut cdevs = vec![unsafe { mem::zeroed() }; self.file_ops.len()].into_boxed_slice(); |
58 |
| - for (i, file_op) in self.file_ops.iter().enumerate() { |
59 |
| - unsafe { |
60 |
| - bindings::cdev_init(&mut cdevs[i], *file_op); |
61 |
| - cdevs[i].owner = this_module.0; |
62 |
| - let rc = bindings::cdev_add(&mut cdevs[i], dev + i as bindings::dev_t, 1); |
63 |
| - if rc != 0 { |
64 |
| - // Clean up the ones that were allocated. |
65 |
| - for j in 0..=i { |
66 |
| - bindings::cdev_del(&mut cdevs[j]); |
67 |
| - } |
68 |
| - bindings::unregister_chrdev_region(dev, self.minors.len() as _); |
69 |
| - return Err(Error::from_kernel_errno(rc)); |
70 |
| - } |
| 70 | + let mut inner = this.inner.as_mut().unwrap(); |
| 71 | + if inner.used == N { |
| 72 | + panic!("More devices registered than minor numbers allocated.") |
| 73 | + } |
| 74 | + let cdev = inner.cdevs[inner.used].as_mut_ptr(); |
| 75 | + // SAFETY: calling unsafe functions and manipulating MaybeUninit ptr. |
| 76 | + unsafe { |
| 77 | + bindings::cdev_init(cdev, &file_operations::FileOperationsVtable::<T>::VTABLE); |
| 78 | + (*cdev).owner = this.this_module.0; |
| 79 | + let rc = bindings::cdev_add(cdev, inner.dev + inner.used as bindings::dev_t, 1); |
| 80 | + if rc != 0 { |
| 81 | + return Err(Error::from_kernel_errno(rc)); |
71 | 82 | }
|
72 | 83 | }
|
73 |
| - |
74 |
| - Ok(Registration { |
75 |
| - dev, |
76 |
| - count: self.minors.len(), |
77 |
| - cdevs, |
78 |
| - }) |
| 84 | + inner.used += 1; |
| 85 | + Ok(()) |
79 | 86 | }
|
80 | 87 | }
|
81 | 88 |
|
82 |
| -pub struct Registration { |
83 |
| - dev: bindings::dev_t, |
84 |
| - count: usize, |
85 |
| - cdevs: Box<[bindings::cdev]>, |
86 |
| -} |
87 |
| - |
88 |
| -// This is safe because Registration doesn't actually expose any methods. |
89 |
| -unsafe impl Sync for Registration {} |
| 89 | +// SAFETY: `Registration` doesn't expose any of its state across threads (it's |
| 90 | +// fine for multiple threads to have a shared reference to it). |
| 91 | +unsafe impl<const N: usize> Sync for Registration<{ N }> {} |
90 | 92 |
|
91 |
| -impl Drop for Registration { |
| 93 | +impl<const N: usize> Drop for Registration<{ N }> { |
92 | 94 | fn drop(&mut self) {
|
93 |
| - unsafe { |
94 |
| - for dev in self.cdevs.iter_mut() { |
95 |
| - bindings::cdev_del(dev); |
| 95 | + if let Some(inner) = self.inner.as_mut() { |
| 96 | + // SAFETY: calling unsafe functions, `0..inner.used` of |
| 97 | + // `inner.cdevs` are initialized in `Registration::register`. |
| 98 | + unsafe { |
| 99 | + for i in 0..inner.used { |
| 100 | + bindings::cdev_del(inner.cdevs[i].as_mut_ptr()); |
| 101 | + } |
| 102 | + bindings::unregister_chrdev_region(inner.dev, N.try_into().unwrap()); |
96 | 103 | }
|
97 |
| - bindings::unregister_chrdev_region(self.dev, self.count as _); |
98 | 104 | }
|
99 | 105 | }
|
100 | 106 | }
|
0 commit comments