1
1
use core:: ops:: Range ;
2
+ use core:: ptr;
3
+
4
+ use alloc:: boxed:: Box ;
5
+ use alloc:: sync:: Arc ;
2
6
3
7
use crate :: bindings;
4
8
use crate :: c_types;
5
- use crate :: error;
9
+ use crate :: error:: { Error , KernelResult } ;
10
+ use crate :: user_ptr:: { UserSlicePtr , UserSlicePtrWriter } ;
6
11
7
12
pub struct DeviceNumberRegion {
8
13
dev : bindings:: dev_t ,
@@ -13,9 +18,9 @@ impl DeviceNumberRegion {
13
18
pub fn allocate (
14
19
minors : Range < usize > ,
15
20
name : & ' static str ,
16
- ) -> error :: KernelResult < DeviceNumberRegion > {
21
+ ) -> KernelResult < Arc < DeviceNumberRegion > > {
17
22
if !name. ends_with ( '\x00' ) {
18
- return Err ( error :: Error :: EINVAL ) ;
23
+ return Err ( Error :: EINVAL ) ;
19
24
}
20
25
21
26
let count = minors. end - minors. start ;
@@ -29,9 +34,33 @@ impl DeviceNumberRegion {
29
34
)
30
35
} ;
31
36
if res != 0 {
32
- return Err ( error:: Error :: from_kernel_errno ( res) ) ;
37
+ return Err ( Error :: from_kernel_errno ( res) ) ;
38
+ }
39
+ return Ok ( Arc :: new ( DeviceNumberRegion { dev, count } ) ) ;
40
+ }
41
+
42
+ pub fn register_device < T : FileOperations > (
43
+ self : & Arc < Self > ,
44
+ ) -> KernelResult < DeviceRegistration > {
45
+ let cdev = unsafe { bindings:: cdev_alloc ( ) } ;
46
+ if cdev. is_null ( ) {
47
+ return Err ( Error :: ENOMEM ) ;
48
+ }
49
+ unsafe {
50
+ ( * cdev) . owner = & mut bindings:: __this_module;
51
+ ( * cdev) . ops = & T :: VTABLE . 0 as * const bindings:: file_operations ;
52
+ }
53
+ let cdev = DeviceRegistration {
54
+ cdev,
55
+ _region : Arc :: clone ( & self ) ,
56
+ } ;
57
+ // TODO: Need to handle multiple register_device calls by going through the devs and
58
+ // erroring if we run out.
59
+ let rc = unsafe { bindings:: cdev_add ( cdev. cdev , self . dev , 1 ) } ;
60
+ if rc != 0 {
61
+ return Err ( Error :: from_kernel_errno ( rc) ) ;
33
62
}
34
- return Ok ( DeviceNumberRegion { dev , count } ) ;
63
+ return Ok ( cdev ) ;
35
64
}
36
65
}
37
66
@@ -42,3 +71,112 @@ impl Drop for DeviceNumberRegion {
42
71
}
43
72
}
44
73
}
74
+
75
+ pub struct DeviceRegistration {
76
+ _region : Arc < DeviceNumberRegion > ,
77
+ cdev : * mut bindings:: cdev ,
78
+ }
79
+
80
+ unsafe impl Sync for DeviceRegistration { }
81
+
82
+ impl Drop for DeviceRegistration {
83
+ fn drop ( & mut self ) {
84
+ unsafe {
85
+ bindings:: cdev_del ( self . cdev ) ;
86
+ }
87
+ }
88
+ }
89
+
90
+ pub struct FileOperationsVtable ( bindings:: file_operations ) ;
91
+
92
+ unsafe extern "C" fn open_callback < T : FileOperations > (
93
+ _inode : * mut bindings:: inode ,
94
+ file : * mut bindings:: file ,
95
+ ) -> c_types:: c_int {
96
+ let f = match T :: open ( ) {
97
+ Ok ( f) => Box :: new ( f) ,
98
+ Err ( e) => return e. to_kernel_errno ( ) ,
99
+ } ;
100
+ ( * file) . private_data = Box :: into_raw ( f) as * mut c_types:: c_void ;
101
+ return 0 ;
102
+ }
103
+
104
+ unsafe extern "C" fn read_callback < T : FileOperations > (
105
+ file : * mut bindings:: file ,
106
+ buf : * mut c_types:: c_char ,
107
+ len : c_types:: c_size_t ,
108
+ offset : * mut bindings:: loff_t ,
109
+ ) -> c_types:: c_ssize_t {
110
+ let mut data = match UserSlicePtr :: new ( buf as * mut c_types:: c_void , len) {
111
+ Ok ( ptr) => ptr. writer ( ) ,
112
+ Err ( e) => return e. to_kernel_errno ( ) as c_types:: c_ssize_t ,
113
+ } ;
114
+ let f = & * ( ( * file) . private_data as * const T ) ;
115
+ // TODO: Pass offset to read()?
116
+ match f. read ( & mut data) {
117
+ Ok ( ( ) ) => {
118
+ let written = len - data. len ( ) ;
119
+ ( * offset) += written as bindings:: loff_t ;
120
+ return written as c_types:: c_ssize_t ;
121
+ }
122
+ Err ( e) => return e. to_kernel_errno ( ) as c_types:: c_ssize_t ,
123
+ } ;
124
+ }
125
+
126
+ unsafe extern "C" fn release_callback < T : FileOperations > (
127
+ _inode : * mut bindings:: inode ,
128
+ file : * mut bindings:: file ,
129
+ ) -> c_types:: c_int {
130
+ let ptr = ( * file) . private_data as * mut T ;
131
+ ( * file) . private_data = ptr:: null_mut ( ) ;
132
+ Box :: from_raw ( ptr) ;
133
+ return 0 ;
134
+ }
135
+
136
+ impl FileOperationsVtable {
137
+ pub const fn new < T : FileOperations > ( ) -> FileOperationsVtable {
138
+ return FileOperationsVtable ( bindings:: file_operations {
139
+ open : Some ( open_callback :: < T > ) ,
140
+ read : Some ( read_callback :: < T > ) ,
141
+ release : Some ( release_callback :: < T > ) ,
142
+
143
+ check_flags : None ,
144
+ clone_file_range : None ,
145
+ compat_ioctl : None ,
146
+ copy_file_range : None ,
147
+ dedupe_file_range : None ,
148
+ fallocate : None ,
149
+ fasync : None ,
150
+ flock : None ,
151
+ flush : None ,
152
+ fsync : None ,
153
+ get_unmapped_area : None ,
154
+ iterate : None ,
155
+ iterate_shared : None ,
156
+ llseek : None ,
157
+ lock : None ,
158
+ mmap : None ,
159
+ #[ cfg( kernel_4_15_0_or_greataer) ]
160
+ mmap_supported_flags : 0 ,
161
+ owner : ptr:: null_mut ( ) ,
162
+ poll : None ,
163
+ read_iter : None ,
164
+ sendpage : None ,
165
+ setfl : None ,
166
+ setlease : None ,
167
+ show_fdinfo : None ,
168
+ splice_read : None ,
169
+ splice_write : None ,
170
+ unlocked_ioctl : None ,
171
+ write : None ,
172
+ write_iter : None ,
173
+ } ) ;
174
+ }
175
+ }
176
+
177
+ pub trait FileOperations : Sync + Sized {
178
+ const VTABLE : FileOperationsVtable ;
179
+
180
+ fn open ( ) -> KernelResult < Self > ;
181
+ fn read ( & self , buf : & mut UserSlicePtrWriter ) -> KernelResult < ( ) > ;
182
+ }
0 commit comments