11use core:: convert:: TryInto ;
22use core:: ops:: Range ;
3+ use core:: { mem, ptr} ;
4+
5+ use alloc:: boxed:: Box ;
6+ use alloc:: vec;
7+ use alloc:: vec:: Vec ;
38
49use crate :: bindings;
510use crate :: c_types;
6- use crate :: error;
11+ use crate :: error:: { Error , KernelResult } ;
12+ use crate :: user_ptr:: { UserSlicePtr , UserSlicePtrWriter } ;
713
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 > {
915 if !name. ends_with ( '\x00' ) {
10- return Err ( error :: Error :: EINVAL ) ;
16+ return Err ( Error :: EINVAL ) ;
1117 }
1218
13- return Ok ( Builder { name, minors } ) ;
19+ return Ok ( Builder {
20+ name,
21+ minors,
22+ file_ops : vec ! [ ] ,
23+ } ) ;
1424}
1525
1626pub struct Builder {
1727 name : & ' static str ,
1828 minors : Range < u16 > ,
29+ file_ops : Vec < & ' static FileOperationsVtable > ,
1930}
2031
2132impl 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 > {
2342 let mut dev: bindings:: dev_t = 0 ;
2443 let res = unsafe {
2544 bindings:: alloc_chrdev_region (
@@ -30,24 +49,145 @@ impl Builder {
3049 )
3150 } ;
3251 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+ }
3472 }
73+
3574 return Ok ( Registration {
3675 dev,
3776 count : self . minors . len ( ) ,
77+ cdevs,
3878 } ) ;
3979 }
4080}
4181
4282pub struct Registration {
4383 dev : bindings:: dev_t ,
4484 count : usize ,
85+ cdevs : Box < [ bindings:: cdev ] > ,
4586}
4687
88+ // This is safe because Registration doesn't actually expose any methods.
89+ unsafe impl Sync for Registration { }
90+
4791impl Drop for Registration {
4892 fn drop ( & mut self ) {
4993 unsafe {
94+ for dev in self . cdevs . iter_mut ( ) {
95+ bindings:: cdev_del ( dev) ;
96+ }
5097 bindings:: unregister_chrdev_region ( self . dev , self . count as _ ) ;
5198 }
5299 }
53100}
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