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,144 @@ 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
+ return Err ( Error :: from_kernel_errno ( rc) ) ;
69
+ }
70
+ }
34
71
}
72
+
35
73
return Ok ( Registration {
36
74
dev,
37
75
count : self . minors . len ( ) ,
76
+ cdevs,
38
77
} ) ;
39
78
}
40
79
}
41
80
42
81
pub struct Registration {
43
82
dev : bindings:: dev_t ,
44
83
count : usize ,
84
+ cdevs : Box < [ bindings:: cdev ] > ,
45
85
}
46
86
87
+ // This is safe because Registration doesn't actually expose any methods.
88
+ unsafe impl Sync for Registration { }
89
+
47
90
impl Drop for Registration {
48
91
fn drop ( & mut self ) {
49
92
unsafe {
93
+ for dev in self . cdevs . iter_mut ( ) {
94
+ bindings:: cdev_del ( dev) ;
95
+ }
50
96
bindings:: unregister_chrdev_region ( self . dev , self . count as _ ) ;
51
97
}
52
98
}
53
99
}
100
+
101
+ pub struct FileOperationsVtable ( bindings:: file_operations ) ;
102
+
103
+ unsafe extern "C" fn open_callback < T : FileOperations > (
104
+ _inode : * mut bindings:: inode ,
105
+ file : * mut bindings:: file ,
106
+ ) -> c_types:: c_int {
107
+ let f = match T :: open ( ) {
108
+ Ok ( f) => Box :: new ( f) ,
109
+ Err ( e) => return e. to_kernel_errno ( ) ,
110
+ } ;
111
+ ( * file) . private_data = Box :: into_raw ( f) as * mut c_types:: c_void ;
112
+ return 0 ;
113
+ }
114
+
115
+ unsafe extern "C" fn read_callback < T : FileOperations > (
116
+ file : * mut bindings:: file ,
117
+ buf : * mut c_types:: c_char ,
118
+ len : c_types:: c_size_t ,
119
+ offset : * mut bindings:: loff_t ,
120
+ ) -> c_types:: c_ssize_t {
121
+ let mut data = match UserSlicePtr :: new ( buf as * mut c_types:: c_void , len) {
122
+ Ok ( ptr) => ptr. writer ( ) ,
123
+ Err ( e) => return e. to_kernel_errno ( ) as c_types:: c_ssize_t ,
124
+ } ;
125
+ let f = & * ( ( * file) . private_data as * const T ) ;
126
+ // TODO: Pass offset to read()?
127
+ match f. read ( & mut data) {
128
+ Ok ( ( ) ) => {
129
+ let written = len - data. len ( ) ;
130
+ ( * offset) += written as bindings:: loff_t ;
131
+ return written as c_types:: c_ssize_t ;
132
+ }
133
+ Err ( e) => return e. to_kernel_errno ( ) as c_types:: c_ssize_t ,
134
+ } ;
135
+ }
136
+
137
+ unsafe extern "C" fn release_callback < T : FileOperations > (
138
+ _inode : * mut bindings:: inode ,
139
+ file : * mut bindings:: file ,
140
+ ) -> c_types:: c_int {
141
+ let ptr = mem:: replace ( & mut ( * file) . private_data , ptr:: null_mut ( ) ) ;
142
+ drop ( Box :: from_raw ( ptr as * mut T ) ) ;
143
+ return 0 ;
144
+ }
145
+
146
+ impl FileOperationsVtable {
147
+ pub const fn new < T : FileOperations > ( ) -> FileOperationsVtable {
148
+ return FileOperationsVtable ( bindings:: file_operations {
149
+ open : Some ( open_callback :: < T > ) ,
150
+ read : Some ( read_callback :: < T > ) ,
151
+ release : Some ( release_callback :: < T > ) ,
152
+
153
+ check_flags : None ,
154
+ clone_file_range : None ,
155
+ compat_ioctl : None ,
156
+ copy_file_range : None ,
157
+ dedupe_file_range : None ,
158
+ fallocate : None ,
159
+ fasync : None ,
160
+ flock : None ,
161
+ flush : None ,
162
+ fsync : None ,
163
+ get_unmapped_area : None ,
164
+ iterate : None ,
165
+ iterate_shared : None ,
166
+ llseek : None ,
167
+ lock : None ,
168
+ mmap : None ,
169
+ #[ cfg( kernel_4_15_0_or_greataer) ]
170
+ mmap_supported_flags : 0 ,
171
+ owner : ptr:: null_mut ( ) ,
172
+ poll : None ,
173
+ read_iter : None ,
174
+ sendpage : None ,
175
+ setfl : None ,
176
+ setlease : None ,
177
+ show_fdinfo : None ,
178
+ splice_read : None ,
179
+ splice_write : None ,
180
+ unlocked_ioctl : None ,
181
+ write : None ,
182
+ write_iter : None ,
183
+ } ) ;
184
+ }
185
+ }
186
+
187
+ pub trait FileOperations : Sync + Sized {
188
+ const VTABLE : FileOperationsVtable ;
189
+
190
+ fn open ( ) -> KernelResult < Self > ;
191
+ fn read ( & self , buf : & mut UserSlicePtrWriter ) -> KernelResult < ( ) > ;
192
+ }
0 commit comments