Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3232 Serge 1
; Disk driver to create FAT16/FAT32 memory-based temporary disk aka RAM disk.
2
; (c) CleverMouse
3
 
4
; Note: in the ideal world, a disk driver should not care about a file system
5
; on it. In the current world, however, there is no way to format a disk in
6
; FAT, so this part of file-system-specific operations is included in the
7
; driver.
8
 
9
; When this driver is loading, it registers itself in the system and does
10
; nothing more. When loaded, this driver controls pseudo-disk devices
11
; named /tmp#/, where # is a digit from 0 to 9. The driver does not create
12
; any device by itself, waiting for instructions from an application.
13
; The driver responds to the following IOCTLs from a control application:
14
SRV_GETVERSION          equ 0 ; input ignored,
15
                              ; output = dword API_VERSION
16
DEV_ADD_DISK            equ 1 ; input = structure add_disk_struc,
17
                              ; no output
18
DEV_DEL_DISK            equ 2 ; input = structure del_disk_struc,
19
                              ; no output
20
; For all IOCTLs the driver returns one of the following error codes:
21
NO_ERROR                equ 0
22
ERROR_INVALID_IOCTL     equ 1 ; unknown IOCTL code, wrong input/output size...
23
ERROR_INVALID_ID        equ 2 ; .DiskId must be from 0 to 9
24
ERROR_SIZE_TOO_LARGE    equ 3 ; .DiskSize is too large
25
ERROR_SIZE_TOO_SMALL    equ 4 ; .DiskSize is too small
26
ERROR_NO_MEMORY         equ 5 ; memory allocation failed
27
 
28
 
29
API_VERSION             equ 1
30
; Input structures:
31
struc add_disk_struc
32
{
33
.DiskSize       dd      ? ; disk size in sectors, 1 sector = 512 bytes
34
; Note: .DiskSize is the full size, including FAT service data.
35
; Size for useful data is slightly less than this number.
36
.DiskId         db      ? ; from 0 to 9
37
.sizeof:
38
}
39
virtual at 0
40
add_disk_struc  add_disk_struc
41
end virtual
42
struc del_disk_struc
43
{
44
.DiskId         db      ? ; from 0 to 9
45
.sizeof:
46
}
47
virtual at 0
48
del_disk_struc del_disk_struc
49
end virtual
50
 
51
max_num_disks   equ     10
52
 
53
; standard driver stuff
54
format MS COFF
55
 
56
DEBUG equ 0
57
include 'proc32.inc'
58
include 'imports.inc'
59
 
60
public START
61
public version
62
 
63
struc IOCTL
64
{
65
        .handle         dd ?
66
        .io_code        dd ?
67
        .input          dd ?
68
        .inp_size       dd ?
69
        .output         dd ?
70
        .out_size       dd ?
71
}
72
 
73
virtual at 0
74
IOCTL IOCTL
75
end virtual
76
 
77
section '.flat' code readable align 16
78
; the start procedure (see the description above)
79
proc START
80
; This procedure is called in two situations:
81
; when the driver is loading and when the system is shutting down.
82
; 1. Check that the driver is loading; do nothing unless so.
83
        xor     eax, eax ; set return value in case we will do nothing
84
        cmp     dword [esp+4], 1
85
        jne     .nothing
86
; 2. Register the driver in the system.
87
        stdcall RegService, my_service, service_proc
88
; 3. Return the value returned by RegService back to the system.
89
.nothing:
90
        retn    4
91
endp
92
 
93
; Service procedure for the driver - handle all IOCTL requests for the driver.
94
; The description of handled IOCTLs is located in the start of this file.
95
proc service_proc
96
; 1. Save used registers to be stdcall.
97
; Note: this shifts esp, so the first parameter [esp+4] becomes [esp+16].
98
; Note: edi is used not by this procedure itself, but by worker procedures.
99
        push    ebx esi edi
100
; 2. Get parameter from the stack: [esp+16] is the first parameter,
101
;    pointer to IOCTL structure.
102
        mov     edx, [esp+16]    ; edx -> IOCTL
103
; 3. Set the return value to 'invalid IOCTL'.
104
; Now, if one of conditions for IOCTL does not met, the code
105
; can simply return the value already loaded.
106
        mov     al, ERROR_INVALID_IOCTL
107
; 4. Get request code and select a handler for the code.
108
        mov     ecx, [edx+IOCTL.io_code]
109
        test    ecx, ecx        ; check for SRV_GETVERSION
110
        jnz     .no.srv_getversion
111
; 4. This is SRV_GETVERSION request, no input, 4 bytes output, API_VERSION.
112
; 4a. Output size must be at least 4 bytes.
113
        cmp     [edx+IOCTL.out_size], 4
114
        jl      .return
115
; 4b. Write result to the output buffer.
116
        mov     eax, [edx+IOCTL.output]
117
        mov     dword [eax], API_VERSION
118
; 4c. Return success.
119
        xor     eax, eax
120
        jmp     .return
121
.no.srv_getversion:
122
        dec     ecx     ; check for DEV_ADD_DISK
123
        jnz     .no.dev_add_disk
124
; 5. This is DEV_ADD_DISK request, input is add_disk_struc, output is 1 byte
125
; 5a. Input size must be exactly add_disk_struc.sizeof bytes.
126
        cmp     [edx+IOCTL.inp_size], add_disk_struc.sizeof
127
        jnz     .return
128
; 5b. Load input parameters and call the worker procedure.
129
        mov     eax, [edx+IOCTL.input]
130
        movzx   ebx, [eax+add_disk_struc.DiskId]
131
        mov     esi, [eax+add_disk_struc.DiskSize]
132
        call    add_disk
133
; 5c. Return back to the caller the value from the worker procedure.
134
        jmp     .return
135
.no.dev_add_disk:
136
        dec     ecx     ; check for DEV_DEL_DISK
137
        jnz     .return
138
; 6. This is DEV_DEL_DISK request, input is del_disk_struc
139
; 6a. Input size must be exactly del_disk_struc.sizeof bytes.
140
        cmp     [edx+IOCTL.inp_size], del_disk_struc.sizeof
141
        jnz     .return
142
; 6b. Load input parameters and call the worker procedure.
143
        mov     eax, [edx+IOCTL.input]
144
        movzx   ebx, [eax+del_disk_struc.DiskId]
145
        call    del_disk
146
; 6c. Return back to the caller the value from the worker procedure.
147
.return:
148
; 7. Exit.
149
; 7a. The code above returns a value in al for efficiency,
150
; propagate it to eax.
151
        movzx   eax, al
152
; 7b. Restore used registers to be stdcall.
153
        pop     edi esi ebx
154
; 7c. Return, popping one argument.
155
        retn    4
156
endp
157
 
158
; The worker procedure for DEV_ADD_DISK request.
159
; Creates a memory-based disk of given size and formats it in FAT16/32.
160
; Called with ebx = disk id, esi = disk size,
161
; returns error code in al.
162
proc add_disk
163
; 1. Check that disk id is correct and free.
164
; Otherwise, return the corresponding error code.
165
        mov     al, ERROR_INVALID_ID
166
        cmp     ebx, max_num_disks
167
        jae     .return
168
        cmp     [disk_pointers+ebx*4], 0
169
        jnz     .return
170
; 2. Check that the size is reasonable.
171
; Otherwise, return the corresponding error code.
172
        mov     al, ERROR_SIZE_TOO_LARGE
173
        cmp     esi, MAX_SIZE
174
        ja      .return
175
        mov     al, ERROR_SIZE_TOO_SMALL
176
        cmp     esi, MIN_FAT16_SIZE
177
        jb      .return
178
; 3. Allocate memory for the disk, store the pointer in edi.
179
; If failed, return the corresponding error code.
180
        mov     eax, esi
181
        shl     eax, 9
182
        stdcall KernelAlloc, eax
183
        mov     edi, eax
184
        test    eax, eax
185
        mov     al, ERROR_NO_MEMORY
186
        jz      .return
187
; 4. Store the pointer and the size in the global variables.
188
; It is possible, though very unlikely, that two threads
189
; have called this function in parallel with the same id,
190
; so [disk_pointers+ebx*4] could be filled by another thread.
191
; Play extra safe and store new value only if old value is zero.
192
        xor     eax, eax
193
        lock cmpxchg [disk_pointers+ebx*4], edi
194
        jz      @f
195
; Otherwise, free the allocated memory and return the corresponding error code.
196
        stdcall KernelFree, edi
197
        mov     al, ERROR_INVALID_ID
198
        jmp     .return
199
@@:
200
        mov     [disk_sizes+ebx*4], esi
201
; 5. Call the worker procedure for formatting this disk.
202
; It should not fail.
203
        call    format_disk
204
; 6. Register the disk in the system.
205
; 6a. Generate name as /tmp#, where # = ebx + '0'. Use two dwords in the stack.
206
        push    0
207
        push    'tmp'
208
        mov     eax, esp ; eax points to 'tmp' + zero byte + zero dword
209
        lea     ecx, [ebx+'0'] ; ecx = digit
210
        mov     [eax+3], cl ; eax points to 'tmp#' + zero dword
211
; 6b. Call the kernel API. Use disk id as 'userdata' parameter for callbacks.
212
        stdcall DiskAdd, disk_functions, eax, ebx, 0
213
; 6c. Restore the stack after 6a.
214
        pop     ecx ecx
215
; 6c. Check the result. If DiskAdd has failed, cleanup and return
216
; ERROR_NO_MEMORY, this is the most probable or even the only reason to fail.
217
        test    eax, eax
218
        jnz     @f
219
        mov     [disk_sizes+ebx*4], 0
220
        mov     [disk_pointers+ebx*4], 0
221
        stdcall KernelFree, edi
222
        mov     al, ERROR_NO_MEMORY
223
        jmp     .return
224
@@:
225
        push    eax
226
; 6d. Notify the kernel that media is inserted.
227
        stdcall DiskMediaChanged, eax, 1
228
; 6e. Disk is fully configured; store its handle in the global variable
229
; and return success.
230
        pop     [disk_handles+ebx*4]
231
        xor     eax, eax
232
; 7. Return.
233
.return:
234
        retn
235
endp
236
 
237
; The worker procedure for DEV_DEL_DISK request.
238
; Deletes a previously created memory-based disk.
239
; Called with ebx = disk id,
240
; returns error code in al.
241
proc del_disk
242
; 1. Check that disk id is correct.
243
; Otherwise, return the corresponding error code.
244
        mov     al, ERROR_INVALID_ID
245
        cmp     ebx, max_num_disks
246
        jae     .return
247
; 2. Get the disk handle, simultaneously clearing the global variable.
248
        xor     edx, edx
249
        xchg    edx, [disk_handles+ebx*4]
250
; 3. Check that the handle is non-zero.
251
; Otherwise, return the corresponding error code.
252
        test    edx, edx
253
        jz      .return
254
; 4. Delete the disk from the system.
255
        stdcall DiskDel, edx
256
; 5. Return success.
257
; Note that we can't free memory yet; it will be done in tmpdisk_close.
258
        xor     eax, eax
259
.return:
260
        retn
261
endp
262
 
263
; Include implementation of tmpdisk_* callbacks.
264
include 'tmpdisk_work.inc'
265
; Include FAT-specific code.
266
include 'tmpdisk_fat.inc'
267
 
268
; initialized data
269
align 4
270
disk_functions:
271
        dd      disk_functions_end - disk_functions
272
        dd      tmpdisk_close
273
        dd      0 ; no need in .closemedia
274
        dd      tmpdisk_querymedia
275
        dd      tmpdisk_read
276
        dd      tmpdisk_write
277
        dd      0 ; no need in .flush
278
        dd      tmpdisk_adjust_cache_size
279
disk_functions_end:
280
; disk_handles = array of values for Disk* kernel functions
281
label disk_handles dword
282
times max_num_disks dd 0
283
; disk_pointers = array of pointers to disk data
284
label disk_pointers dword
285
times max_num_disks dd 0
286
; disk_sizes = array of disk sizes
287
label disk_sizes dword
288
times max_num_disks dd 0
289
 
290
version         dd      0x00060006
291
my_service      db      'tmpdisk',0
292
 
293
; uninitialized data
294
; actually, not used here
295
;section '.data' data readable writable align 16 ; standard driver stuff