1
1
use core:: convert:: TryInto ;
2
2
use core:: ops:: Range ;
3
+ use core:: { mem, ptr} ;
4
+
5
+ use alloc:: boxed:: Box ;
6
+ use alloc:: vec;
7
+ use alloc:: vec:: Vec ;
3
8
4
9
use crate :: bindings;
5
10
use crate :: c_types;
6
- use crate :: error;
11
+ use crate :: error:: { Error , KernelResult } ;
12
+ use crate :: user_ptr:: { UserSlicePtr , UserSlicePtrWriter } ;
7
13
8
- pub fn builder ( name : & ' static str , minors : Range < u16 > ) -> error :: KernelResult < Builder > {
14
+ pub fn builder ( name : & ' static str , minors : Range < u16 > ) -> KernelResult < Builder > {
9
15
if !name. ends_with ( '\x00' ) {
10
- return Err ( error :: Error :: EINVAL ) ;
16
+ return Err ( Error :: EINVAL ) ;
11
17
}
12
18
13
- return Ok ( Builder { name, minors } ) ;
19
+ return Ok ( Builder {
20
+ name,
21
+ minors,
22
+ file_ops : vec ! [ ] ,
23
+ } ) ;
14
24
}
15
25
16
26
pub struct Builder {
17
27
name : & ' static str ,
18
28
minors : Range < u16 > ,
29
+ file_ops : Vec < & ' static FileOperationsVtable > ,
19
30
}
20
31
21
32
impl Builder {
22
- pub fn build ( self ) -> error:: KernelResult < Registration > {
33
+ pub fn register_device < T : FileOperations > ( mut self ) -> Builder {
34
+ if self . file_ops . len ( ) >= self . minors . len ( ) {
35
+ panic ! ( "More devices registered than minor numbers allocated." )
36
+ }
37
+ self . file_ops . push ( & T :: VTABLE ) ;
38
+ return self ;
39
+ }
40
+
41
+ pub fn build ( self ) -> KernelResult < Registration > {
23
42
let mut dev: bindings:: dev_t = 0 ;
24
43
let res = unsafe {
25
44
bindings:: alloc_chrdev_region (
@@ -30,24 +49,145 @@ impl Builder {
30
49
)
31
50
} ;
32
51
if res != 0 {
33
- return Err ( error:: Error :: from_kernel_errno ( res) ) ;
52
+ return Err ( Error :: from_kernel_errno ( res) ) ;
53
+ }
54
+
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. 0 ) ;
61
+ cdevs[ i] . owner = & mut bindings:: __this_module;
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
+ }
71
+ }
34
72
}
73
+
35
74
return Ok ( Registration {
36
75
dev,
37
76
count : self . minors . len ( ) ,
77
+ cdevs,
38
78
} ) ;
39
79
}
40
80
}
41
81
42
82
pub struct Registration {
43
83
dev : bindings:: dev_t ,
44
84
count : usize ,
85
+ cdevs : Box < [ bindings:: cdev ] > ,
45
86
}
46
87
88
+ // This is safe because Registration doesn't actually expose any methods.
89
+ unsafe impl Sync for Registration { }
90
+
47
91
impl Drop for Registration {
48
92
fn drop ( & mut self ) {
49
93
unsafe {
94
+ for dev in self . cdevs . iter_mut ( ) {
95
+ bindings:: cdev_del ( dev) ;
96
+ }
50
97
bindings:: unregister_chrdev_region ( self . dev , self . count as _ ) ;
51
98
}
52
99
}
53
100
}
101
+
102
+ pub struct FileOperationsVtable ( bindings:: file_operations ) ;
103
+
104
+ unsafe extern "C" fn open_callback < T : FileOperations > (
105
+ _inode : * mut bindings:: inode ,
106
+ file : * mut bindings:: file ,
107
+ ) -> c_types:: c_int {
108
+ let f = match T :: open ( ) {
109
+ Ok ( f) => Box :: new ( f) ,
110
+ Err ( e) => return e. to_kernel_errno ( ) ,
111
+ } ;
112
+ ( * file) . private_data = Box :: into_raw ( f) as * mut c_types:: c_void ;
113
+ return 0 ;
114
+ }
115
+
116
+ unsafe extern "C" fn read_callback < T : FileOperations > (
117
+ file : * mut bindings:: file ,
118
+ buf : * mut c_types:: c_char ,
119
+ len : c_types:: c_size_t ,
120
+ offset : * mut bindings:: loff_t ,
121
+ ) -> c_types:: c_ssize_t {
122
+ let mut data = match UserSlicePtr :: new ( buf as * mut c_types:: c_void , len) {
123
+ Ok ( ptr) => ptr. writer ( ) ,
124
+ Err ( e) => return e. to_kernel_errno ( ) as c_types:: c_ssize_t ,
125
+ } ;
126
+ let f = & * ( ( * file) . private_data as * const T ) ;
127
+ // TODO: Pass offset to read()?
128
+ match f. read ( & mut data) {
129
+ Ok ( ( ) ) => {
130
+ let written = len - data. len ( ) ;
131
+ ( * offset) += written as bindings:: loff_t ;
132
+ return written as c_types:: c_ssize_t ;
133
+ }
134
+ Err ( e) => return e. to_kernel_errno ( ) as c_types:: c_ssize_t ,
135
+ } ;
136
+ }
137
+
138
+ unsafe extern "C" fn release_callback < T : FileOperations > (
139
+ _inode : * mut bindings:: inode ,
140
+ file : * mut bindings:: file ,
141
+ ) -> c_types:: c_int {
142
+ let ptr = mem:: replace ( & mut ( * file) . private_data , ptr:: null_mut ( ) ) ;
143
+ drop ( Box :: from_raw ( ptr as * mut T ) ) ;
144
+ return 0 ;
145
+ }
146
+
147
+ impl FileOperationsVtable {
148
+ pub const fn new < T : FileOperations > ( ) -> FileOperationsVtable {
149
+ return FileOperationsVtable ( bindings:: file_operations {
150
+ open : Some ( open_callback :: < T > ) ,
151
+ read : Some ( read_callback :: < T > ) ,
152
+ release : Some ( release_callback :: < T > ) ,
153
+
154
+ check_flags : None ,
155
+ clone_file_range : None ,
156
+ compat_ioctl : None ,
157
+ copy_file_range : None ,
158
+ dedupe_file_range : None ,
159
+ fallocate : None ,
160
+ fasync : None ,
161
+ flock : None ,
162
+ flush : None ,
163
+ fsync : None ,
164
+ get_unmapped_area : None ,
165
+ iterate : None ,
166
+ iterate_shared : None ,
167
+ llseek : None ,
168
+ lock : None ,
169
+ mmap : None ,
170
+ #[ cfg( kernel_4_15_0_or_greataer) ]
171
+ mmap_supported_flags : 0 ,
172
+ owner : ptr:: null_mut ( ) ,
173
+ poll : None ,
174
+ read_iter : None ,
175
+ sendpage : None ,
176
+ setfl : None ,
177
+ setlease : None ,
178
+ show_fdinfo : None ,
179
+ splice_read : None ,
180
+ splice_write : None ,
181
+ unlocked_ioctl : None ,
182
+ write : None ,
183
+ write_iter : None ,
184
+ } ) ;
185
+ }
186
+ }
187
+
188
+ pub trait FileOperations : Sync + Sized {
189
+ const VTABLE : FileOperationsVtable ;
190
+
191
+ fn open ( ) -> KernelResult < Self > ;
192
+ fn read ( & self , buf : & mut UserSlicePtrWriter ) -> KernelResult < ( ) > ;
193
+ }
0 commit comments