1
+ use core:: default:: Default ;
1
2
use core:: ops:: Range ;
2
3
4
+ use alloc:: boxed:: Box ;
5
+
3
6
use crate :: bindings;
4
7
use crate :: c_types;
5
- use crate :: error;
8
+ use crate :: error:: { Error , KernelResult } ;
9
+ use crate :: user_ptr:: { UserSlicePtr , UserSlicePtrWriter } ;
6
10
7
11
pub struct DeviceNumberRegion {
8
12
dev : bindings:: dev_t ,
9
13
count : usize ,
10
14
}
11
15
12
16
impl DeviceNumberRegion {
13
- pub fn allocate (
14
- minors : Range < usize > ,
15
- name : & ' static str ,
16
- ) -> error:: KernelResult < DeviceNumberRegion > {
17
+ pub fn allocate ( minors : Range < usize > , name : & ' static str ) -> KernelResult < DeviceNumberRegion > {
17
18
if !name. ends_with ( '\x00' ) {
18
- return Err ( error :: Error :: EINVAL ) ;
19
+ return Err ( Error :: EINVAL ) ;
19
20
}
20
21
21
22
let count = minors. end - minors. start ;
@@ -29,10 +30,32 @@ impl DeviceNumberRegion {
29
30
)
30
31
} ;
31
32
if res != 0 {
32
- return Err ( error :: Error :: from_kernel_errno ( res) ) ;
33
+ return Err ( Error :: from_kernel_errno ( res) ) ;
33
34
}
34
35
return Ok ( DeviceNumberRegion { dev, count } ) ;
35
36
}
37
+
38
+ pub fn register_device < T : FileOperations > ( & mut self ) -> KernelResult < DeviceRegistration > {
39
+ let cdev = unsafe { bindings:: cdev_alloc ( ) } ;
40
+ if cdev. is_null ( ) {
41
+ return Err ( Error :: ENOMEM ) ;
42
+ }
43
+ unsafe {
44
+ ( * cdev) . owner = & mut bindings:: __this_module;
45
+ ( * cdev) . ops = & T :: VTABLE . 0 as * const bindings:: file_operations ;
46
+ }
47
+ let cdev = DeviceRegistration {
48
+ cdev,
49
+ _registration : self ,
50
+ } ;
51
+ // TODO: Need to handle multiple register_device calls by going through the devs and
52
+ // erroring if we run out.
53
+ let rc = unsafe { bindings:: cdev_add ( cdev. cdev , self . dev , 1 ) } ;
54
+ if rc != 0 {
55
+ return Err ( Error :: from_kernel_errno ( rc) ) ;
56
+ }
57
+ return Ok ( cdev) ;
58
+ }
36
59
}
37
60
38
61
impl Drop for DeviceNumberRegion {
@@ -42,3 +65,70 @@ impl Drop for DeviceNumberRegion {
42
65
}
43
66
}
44
67
}
68
+
69
+ pub struct DeviceRegistration < ' a > {
70
+ _registration : & ' a DeviceNumberRegion ,
71
+ cdev : * mut bindings:: cdev ,
72
+ }
73
+
74
+ impl Drop for DeviceRegistration < ' _ > {
75
+ fn drop ( & mut self ) {
76
+ unsafe {
77
+ bindings:: cdev_del ( self . cdev ) ;
78
+ }
79
+ }
80
+ }
81
+
82
+ pub struct FileOperationsVtable ( bindings:: file_operations ) ;
83
+
84
+ unsafe extern "C" fn open_callback < T : FileOperations > (
85
+ _inode : * mut bindings:: inode ,
86
+ file : * mut bindings:: file ,
87
+ ) -> c_types:: c_int {
88
+ let f = match T :: open ( ) {
89
+ Ok ( f) => Box :: new ( f) ,
90
+ Err ( e) => return e. to_kernel_errno ( ) ,
91
+ } ;
92
+ ( * file) . private_data = Box :: into_raw ( f) as * mut c_types:: c_void ;
93
+ return 0 ;
94
+ }
95
+
96
+ unsafe extern "C" fn read_callback < T : FileOperations > (
97
+ file : * mut bindings:: file ,
98
+ buf : * mut c_types:: c_char ,
99
+ len : c_types:: c_size_t ,
100
+ offset : * mut bindings:: loff_t ,
101
+ ) -> c_types:: c_ssize_t {
102
+ let mut data = match UserSlicePtr :: new ( buf as * mut c_types:: c_void , len) {
103
+ Ok ( ptr) => ptr. writer ( ) ,
104
+ Err ( e) => return e. to_kernel_errno ( ) as c_types:: c_ssize_t ,
105
+ } ;
106
+ let f = & * ( ( * file) . private_data as * const T ) ;
107
+ // TODO: Pass offset to read()?
108
+ match f. read ( & mut data) {
109
+ Ok ( ( ) ) => {
110
+ let written = len - data. len ( ) ;
111
+ ( * offset) += written as i64 ;
112
+ return written as c_types:: c_ssize_t ;
113
+ }
114
+ Err ( e) => return e. to_kernel_errno ( ) as c_types:: c_ssize_t ,
115
+ } ;
116
+ }
117
+
118
+ impl FileOperationsVtable {
119
+ pub fn new < T : FileOperations > ( ) -> FileOperationsVtable {
120
+ return FileOperationsVtable ( bindings:: file_operations {
121
+ open : Some ( open_callback :: < T > ) ,
122
+ read : Some ( read_callback :: < T > ) ,
123
+
124
+ ..Default :: default ( )
125
+ } ) ;
126
+ }
127
+ }
128
+
129
+ pub trait FileOperations : Sync + Sized {
130
+ const VTABLE : FileOperationsVtable ;
131
+
132
+ fn open ( ) -> KernelResult < Self > ;
133
+ fn read ( & self , buf : & mut UserSlicePtrWriter ) -> KernelResult < ( ) > ;
134
+ }
0 commit comments