Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
3520 | clevermous | 1 | ; standard driver stuff |
2 | format MS COFF |
||
3 | |||
4 | DEBUG = 1 |
||
5 | DUMP_PACKETS = 0 |
||
6 | |||
7 | ; this is for DEBUGF macro from 'fdo.inc' |
||
8 | __DEBUG__ = 1 |
||
9 | __DEBUG_LEVEL__ = 1 |
||
10 | |||
11 | include 'proc32.inc' |
||
12 | include 'imports.inc' |
||
13 | include 'fdo.inc' |
||
14 | |||
15 | public START |
||
16 | public version |
||
17 | |||
18 | ; USB constants |
||
19 | DEVICE_DESCR_TYPE = 1 |
||
20 | CONFIG_DESCR_TYPE = 2 |
||
21 | STRING_DESCR_TYPE = 3 |
||
22 | INTERFACE_DESCR_TYPE = 4 |
||
23 | ENDPOINT_DESCR_TYPE = 5 |
||
24 | DEVICE_QUALIFIER_DESCR_TYPE = 6 |
||
25 | |||
26 | CONTROL_PIPE = 0 |
||
27 | ISOCHRONOUS_PIPE = 1 |
||
28 | BULK_PIPE = 2 |
||
29 | INTERRUPT_PIPE = 3 |
||
30 | |||
31 | ; USB structures |
||
32 | virtual at 0 |
||
33 | config_descr: |
||
34 | .bLength db ? |
||
35 | .bDescriptorType db ? |
||
36 | .wTotalLength dw ? |
||
37 | .bNumInterfaces db ? |
||
38 | .bConfigurationValue db ? |
||
39 | .iConfiguration db ? |
||
40 | .bmAttributes db ? |
||
41 | .bMaxPower db ? |
||
42 | .sizeof: |
||
43 | end virtual |
||
44 | |||
45 | virtual at 0 |
||
46 | interface_descr: |
||
47 | .bLength db ? |
||
48 | .bDescriptorType db ? |
||
49 | .bInterfaceNumber db ? |
||
50 | .bAlternateSetting db ? |
||
51 | .bNumEndpoints db ? |
||
52 | .bInterfaceClass db ? |
||
53 | .bInterfaceSubClass db ? |
||
54 | .bInterfaceProtocol db ? |
||
55 | .iInterface db ? |
||
56 | .sizeof: |
||
57 | end virtual |
||
58 | |||
59 | virtual at 0 |
||
60 | endpoint_descr: |
||
61 | .bLength db ? |
||
62 | .bDescriptorType db ? |
||
63 | .bEndpointAddress db ? |
||
64 | .bmAttributes db ? |
||
65 | .wMaxPacketSize dw ? |
||
66 | .bInterval db ? |
||
67 | .sizeof: |
||
68 | end virtual |
||
69 | |||
70 | ; Mass storage protocol constants, USB layer |
||
71 | REQUEST_GETMAXLUN = 0xFE ; get max lun |
||
72 | REQUEST_BORESET = 0xFF ; bulk-only reset |
||
73 | |||
74 | ; Mass storage protocol structures, USB layer |
||
75 | ; Sent from host to device in the first stage of an operation. |
||
76 | struc command_block_wrapper |
||
77 | { |
||
78 | .Signature dd ? ; the constant 'USBC' |
||
79 | .Tag dd ? ; identifies response with request |
||
80 | .Length dd ? ; length of data-transport phase |
||
81 | .Flags db ? ; one of CBW_FLAG_* |
||
82 | CBW_FLAG_OUT = 0 |
||
83 | CBW_FLAG_IN = 80h |
||
84 | .LUN db ? ; addressed unit |
||
85 | .CommandLength db ? ; the length of the following field |
||
86 | .Command rb 16 |
||
87 | .sizeof: |
||
88 | } |
||
89 | virtual at 0 |
||
90 | command_block_wrapper command_block_wrapper |
||
91 | end virtual |
||
92 | |||
93 | ; Sent from device to host in the last stage of an operation. |
||
94 | struc command_status_wrapper |
||
95 | { |
||
96 | .Signature dd ? ; the constant 'USBS' |
||
97 | .Tag dd ? ; identifies response with request |
||
98 | .LengthRest dd ? ; .Length - (size of data which were transferred) |
||
99 | .Status db ? ; one of CSW_STATUS_* |
||
100 | CSW_STATUS_OK = 0 |
||
101 | CSW_STATUS_FAIL = 1 |
||
102 | CSW_STATUS_FATAL = 2 |
||
103 | .sizeof: |
||
104 | } |
||
105 | virtual at 0 |
||
106 | command_status_wrapper command_status_wrapper |
||
107 | end virtual |
||
108 | |||
109 | ; Constants of SCSI layer |
||
110 | SCSI_REQUEST_SENSE = 3 |
||
111 | SCSI_INQUIRY = 12h |
||
112 | SCSI_READ_CAPACITY = 25h |
||
113 | SCSI_READ10 = 28h |
||
114 | SCSI_WRITE10 = 2Ah |
||
115 | |||
116 | ; Result of SCSI REQUEST SENSE command. |
||
117 | SENSE_UNKNOWN = 0 |
||
118 | SENSE_RECOVERED_ERROR = 1 |
||
119 | SENSE_NOT_READY = 2 |
||
120 | SENSE_MEDIUM_ERROR = 3 |
||
121 | SENSE_HARDWARE_ERROR = 4 |
||
122 | SENSE_ILLEGAL_REQUEST = 5 |
||
123 | SENSE_UNIT_ATTENTION = 6 |
||
124 | SENSE_DATA_PROTECT = 7 |
||
125 | SENSE_BLANK_CHECK = 8 |
||
126 | ; 9 is vendor-specific |
||
127 | SENSE_COPY_ABORTED = 10 |
||
128 | SENSE_ABORTED_COMMAND = 11 |
||
129 | SENSE_EQUAL = 12 |
||
130 | SENSE_VOLUME_OVERFLOW = 13 |
||
131 | SENSE_MISCOMPARE = 14 |
||
132 | ; 15 is reserved |
||
133 | |||
134 | ; Structures of SCSI layer |
||
135 | ; Result of SCSI INQUIRY request. |
||
136 | struc inquiry_data |
||
137 | { |
||
138 | .PeripheralDevice db ? ; lower 5 bits are PeripheralDeviceType |
||
139 | ; upper 3 bits are PeripheralQualifier |
||
140 | .RemovableMedium db ? ; upper bit is RemovableMedium |
||
141 | ; other bits are for compatibility |
||
142 | .Version db ? ; lower 3 bits are ANSI-Approved version |
||
143 | ; next 3 bits are ECMA version |
||
144 | ; upper 2 bits are ISO version |
||
145 | .ResponseDataFormat db ? ; lower 4 bits are ResponseDataFormat |
||
146 | ; bit 6 is TrmIOP |
||
147 | ; bit 7 is AENC |
||
148 | .AdditionalLength db ? |
||
149 | dw ? ; reserved |
||
150 | .Flags db ? |
||
151 | .VendorID rb 8 ; vendor ID, big-endian |
||
152 | .ProductID rb 16 ; product ID, big-endian |
||
153 | .ProductRevBE dd ? ; product revision, big-endian |
||
154 | .sizeof: |
||
155 | } |
||
156 | virtual at 0 |
||
157 | inquiry_data inquiry_data |
||
158 | end virtual |
||
159 | |||
160 | struc sense_data |
||
161 | { |
||
162 | .ErrorCode db ? ; lower 7 bits are error code: |
||
163 | ; 70h = current error, |
||
164 | ; 71h = deferred error |
||
165 | ; upper bit is InformationValid |
||
166 | .SegmentNumber db ? ; number of segment descriptor |
||
167 | ; for commands COPY [+VERIFY], COMPARE |
||
168 | .SenseKey db ? ; bits 0-3 are one of SENSE_* |
||
169 | ; bit 4 is reserved |
||
170 | ; bit 5 is IncorrectLengthIndicator |
||
171 | ; bits 6 and 7 are used by |
||
172 | ; sequential-access devices |
||
173 | .Information dd ? ; command-specific |
||
174 | .AdditionalLength db ? ; length of data starting here |
||
175 | .CommandInformation dd ? ; command-specific |
||
176 | .AdditionalSenseCode db ? ; \ more detailed error code |
||
177 | .AdditionalSenseQual db ? ; / standard has a large table of them |
||
178 | .FRUCode db ? ; which part of device has failed |
||
179 | ; (device-specific, not regulated) |
||
180 | .SenseKeySpecific rb 3 ; depends on SenseKey |
||
181 | .sizeof: |
||
182 | } |
||
183 | virtual at 0 |
||
184 | sense_data sense_data |
||
185 | end virtual |
||
186 | |||
187 | ; Device data |
||
188 | ; USB Mass storage device has one or more logical units, identified by LUN, |
||
189 | ; logical unit number. The highest value of LUN, that is, number of units |
||
190 | ; minus 1, can be obtained via control request Get Max LUN. |
||
191 | virtual at 0 |
||
192 | usb_device_data: |
||
193 | .ConfigPipe dd ? ; configuration pipe |
||
194 | .OutPipe dd ? ; pipe for OUT bulk endpoint |
||
195 | .InPipe dd ? ; pipe for IN bulk endpoint |
||
196 | .MaxLUN dd ? ; maximum Logical Unit Number |
||
197 | .LogicalDevices dd ? ; pointer to array of usb_unit_data |
||
198 | ; 1 for a connected USB device, 1 for each disk device |
||
199 | ; the structure can be freed when .NumReferences decreases to zero |
||
200 | .NumReferences dd ? ; number of references |
||
201 | .ConfigRequest rb 8 ; buffer for configuration requests |
||
202 | .LengthRest dd ? ; Length - (size of data which were transferred) |
||
203 | ; All requests to a given device are serialized, |
||
204 | ; only one request to a given device can be processed at a time. |
||
205 | ; The current request and all pending requests are organized in the following |
||
206 | ; queue, the head being the current request. |
||
207 | ; NB: the queue must be device-wide due to the protocol: |
||
208 | ; data stage is not tagged (unlike command_*_wrapper), so the only way to know |
||
209 | ; what request the data are associated with is to guarantee that only one |
||
210 | ; request is processing at the time. |
||
211 | .RequestsQueue rd 2 |
||
212 | .QueueLock rd 3 ; protects .RequestsQueue |
||
213 | .InquiryData inquiry_data ; information about device |
||
214 | ; data for the current request |
||
215 | .Command command_block_wrapper |
||
216 | .DeviceDisconnected db ? |
||
217 | .Status command_status_wrapper |
||
218 | .Sense sense_data |
||
219 | .sizeof: |
||
220 | end virtual |
||
221 | |||
222 | ; Information about one logical device. |
||
223 | virtual at 0 |
||
224 | usb_unit_data: |
||
225 | .Parent dd ? ; pointer to parent usb_device_data |
||
226 | .LUN db ? ; index in usb_device_data.LogicalDevices array |
||
227 | .DiskIndex db ? ; for name "usbhd |
||
228 | .MediaPresent db ? |
||
229 | db ? ; alignment |
||
230 | .DiskDevice dd ? ; handle of disk device or NULL |
||
231 | .SectorSize dd ? ; sector size |
||
232 | ; For some devices, the first request to the medium fails with 'unit not ready'. |
||
233 | ; When the code sees this status, it retries the command several times. |
||
234 | ; Two following variables track the retry count and total time for those; |
||
235 | ; total time is currently used only for debug output. |
||
236 | .UnitReadyAttempts dd ? |
||
237 | .TimerTicks dd ? |
||
238 | .sizeof: |
||
239 | end virtual |
||
240 | |||
241 | ; This is the structure for items in the queue usb_device_data.RequestsQueue. |
||
242 | virtual at 0 |
||
243 | request_queue_item: |
||
244 | .Next dd ? ; next item in the queue |
||
245 | .Prev dd ? ; prev item in the queue |
||
246 | .ReqBuilder dd ? ; procedure to fill command_block_wrapper |
||
247 | .Buffer dd ? ; input or output data |
||
248 | ; (length is command_block_wrapper.Length) |
||
249 | .Callback dd ? ; procedure to call in the end of transfer |
||
250 | .UserData dd ? ; passed as-is to .Callback |
||
251 | ; There are 3 possible stages of any request, one of them optional: |
||
252 | ; command stage (host sends command_block_wrapper to device), |
||
253 | ; optional data stage, |
||
254 | ; status stage (device sends command_status_wrapper to host). |
||
255 | ; Also, if a request fails, the code queues additional request |
||
256 | ; SCSI_REQUEST_SENSE; sense_data from SCSI_REQUEST_SENSE |
||
257 | ; contains some information about the error. |
||
258 | .Stage db ? |
||
259 | .sizeof: |
||
260 | end virtual |
||
261 | |||
262 | section '.flat' code readable align 16 |
||
263 | ; The start procedure. |
||
264 | proc START |
||
265 | virtual at esp |
||
266 | dd ? ; return address |
||
267 | .reason dd ? ; DRV_ENTRY or DRV_EXIT |
||
268 | end virtual |
||
269 | ; 1. Test whether the procedure is called with the argument DRV_ENTRY. |
||
270 | ; If not, return 0. |
||
271 | xor eax, eax ; initialize return value |
||
272 | cmp [.reason], 1 ; compare the argument |
||
273 | jnz .nothing |
||
274 | ; 2. Initialize: we have one global mutex. |
||
275 | mov ecx, free_numbers_lock |
||
276 | call MutexInit |
||
277 | ; 3. Register self as a USB driver. |
||
278 | ; The name is my_driver = 'usbstor'; IOCTL interface is not supported; |
||
279 | ; usb_functions is an offset of a structure with callback functions. |
||
280 | stdcall RegUSBDriver, my_driver, 0, usb_functions |
||
281 | ; 4. Return the returned value of RegUSBDriver. |
||
282 | .nothing: |
||
283 | ret 4 |
||
284 | endp |
||
285 | |||
286 | ; Helper procedures to work with requests queue. |
||
287 | |||
288 | ; Add a request to the queue. Stdcall with 5 arguments. |
||
289 | proc queue_request |
||
290 | push ebx esi |
||
291 | virtual at esp |
||
292 | rd 2 ; saved registers |
||
293 | dd ? ; return address |
||
294 | .device dd ? ; pointer to usb_device_data |
||
295 | .ReqBuilder dd ? ; request_queue_item.ReqBuilder |
||
296 | .Buffer dd ? ; request_queue_item.Buffer |
||
297 | .Callback dd ? ; request_queue_item.Callback |
||
298 | .UserData dd ? ; request_queue_item.UserData |
||
299 | end virtual |
||
300 | ; 1. Allocate the memory for the request description. |
||
301 | push request_queue_item.sizeof |
||
302 | pop eax |
||
303 | call Kmalloc |
||
304 | test eax, eax |
||
305 | jnz @f |
||
306 | mov esi, nomemory |
||
307 | call SysMsgBoardStr |
||
308 | pop esi ebx |
||
309 | ret 20 |
||
310 | @@: |
||
311 | ; 2. Fill user-provided parts of the request description. |
||
312 | push edi |
||
313 | xchg eax, ebx |
||
314 | lea esi, [.ReqBuilder+4] |
||
315 | lea edi, [ebx+request_queue_item.ReqBuilder] |
||
316 | movsd ; ReqBuilder |
||
317 | movsd ; Buffer |
||
318 | movsd ; Callback |
||
319 | movsd ; UserData |
||
320 | pop edi |
||
321 | ; 3. Set stage to zero: not started. |
||
322 | mov [ebx+request_queue_item.Stage], 0 |
||
323 | ; 4. Lock the queue. |
||
324 | mov esi, [.device] |
||
325 | lea ecx, [esi+usb_device_data.QueueLock] |
||
326 | call MutexLock |
||
327 | ; 5. Insert the request to the tail of the queue. |
||
328 | add esi, usb_device_data.RequestsQueue |
||
329 | mov edx, [esi+request_queue_item.Prev] |
||
330 | mov [ebx+request_queue_item.Next], esi |
||
331 | mov [ebx+request_queue_item.Prev], edx |
||
332 | mov [edx+request_queue_item.Next], ebx |
||
333 | mov [esi+request_queue_item.Prev], ebx |
||
334 | ; 6. Test whether the queue was empty |
||
335 | ; and the request should be started immediately. |
||
336 | cmp [esi+request_queue_item.Next], ebx |
||
337 | jnz .unlock |
||
338 | ; 8. If the step 6 shows that the request is the first in the queue, |
||
339 | ; start it. |
||
340 | sub esi, usb_device_data.RequestsQueue |
||
341 | call setup_request |
||
342 | jmp .nothing |
||
343 | .unlock: |
||
344 | call MutexUnlock |
||
345 | ; 9. Return. |
||
346 | .nothing: |
||
347 | pop esi ebx |
||
348 | ret 20 |
||
349 | endp |
||
350 | |||
351 | ; The current request is completed. Call the callback, |
||
352 | ; remove the request from the queue, start the next |
||
353 | ; request if there is one. |
||
354 | ; esi points to usb_device_data |
||
355 | proc complete_request |
||
356 | ; 1. Print common debug messages on fails. |
||
357 | if DEBUG |
||
358 | cmp [esi+usb_device_data.Status.Status], CSW_STATUS_FAIL |
||
359 | jb .normal |
||
360 | jz .fail |
||
361 | DEBUGF 1, 'K : Fatal error during execution of command %x\n', [esi+usb_device_data.Command.Command]:2 |
||
362 | jmp .normal |
||
363 | .fail: |
||
364 | DEBUGF 1, 'K : Command %x failed\n', [esi+usb_device_data.Command.Command]:2 |
||
365 | .normal: |
||
366 | end if |
||
367 | ; 2. Get the current request. |
||
368 | mov ebx, [esi+usb_device_data.RequestsQueue+request_queue_item.Next] |
||
369 | ; 3. Call the callback. |
||
370 | stdcall [ebx+request_queue_item.Callback], esi, [ebx+request_queue_item.UserData] |
||
371 | ; 4. Lock the queue. |
||
372 | lea ecx, [esi+usb_device_data.QueueLock] |
||
373 | call MutexLock |
||
374 | ; 5. Remove the request. |
||
375 | lea edx, [esi+usb_device_data.RequestsQueue] |
||
376 | mov eax, [ebx+request_queue_item.Next] |
||
377 | mov [eax+request_queue_item.Prev], edx |
||
378 | mov [edx+request_queue_item.Next], eax |
||
379 | ; 6. Free the request memory. |
||
380 | push eax edx |
||
381 | xchg eax, ebx |
||
382 | call Kfree |
||
383 | pop edx ebx |
||
384 | ; 7. If there is a next request, start processing. |
||
385 | cmp ebx, edx |
||
386 | jnz setup_request |
||
387 | ; 8. Unlock the queue and return. |
||
388 | lea ecx, [esi+usb_device_data.QueueLock] |
||
389 | call MutexUnlock |
||
390 | ret |
||
391 | endp |
||
392 | |||
393 | ; Start processing the request. Called either by queue_request |
||
394 | ; or when the previous request has been processed. |
||
395 | ; Do not call directly, use queue_request. |
||
396 | ; Must be called when queue is locked; unlocks the queue when returns. |
||
397 | proc setup_request |
||
398 | xor eax, eax |
||
399 | ; 1. If DeviceDisconnected has been run, then all handles of pipes |
||
400 | ; are invalid, so we must fail immediately. |
||
401 | ; (That is why this function needs the locked queue: this |
||
402 | ; guarantee that either DeviceDisconnected has been already run, or |
||
403 | ; DeviceDisconnected will not return before the queue is unlocked.) |
||
404 | cmp [esi+usb_device_data.DeviceDisconnected], al |
||
405 | jnz .fatal |
||
406 | ; 2. If the previous command has encountered a fatal error, |
||
407 | ; perform reset recovery. |
||
408 | cmp [esi+usb_device_data.Status.Status], CSW_STATUS_FATAL |
||
409 | jb .norecovery |
||
410 | ; 2a. Send Bulk-Only Mass Storage Reset command to config pipe. |
||
411 | lea edx, [esi+usb_device_data.ConfigRequest] |
||
412 | mov word [edx], (REQUEST_BORESET shl 8) + 21h ; class request |
||
413 | mov word [edx+6], ax ; length = 0 |
||
414 | stdcall USBControlTransferAsync, [esi+usb_device_data.ConfigPipe], edx, eax, eax, recovery_callback1, esi, eax |
||
415 | ; 2b. Fail here = fatal error. |
||
416 | test eax, eax |
||
417 | jz .fatal |
||
418 | ; 2c. Otherwise, unlock the queue and return. recovery_callback1 will continue processing. |
||
419 | .unlock_return: |
||
420 | lea ecx, [esi+usb_device_data.QueueLock] |
||
421 | call MutexUnlock |
||
422 | ret |
||
423 | .norecovery: |
||
424 | ; 3. Send the command. Fail (no memory or device disconnected) = fatal error. |
||
425 | ; Otherwise, go to 2c. |
||
426 | call request_stage1 |
||
427 | test eax, eax |
||
428 | jnz .unlock_return |
||
429 | .fatal: |
||
430 | ; 4. Fatal error. Set status = FATAL, unlock the queue, complete the request. |
||
431 | mov [esi+usb_device_data.Status.Status], CSW_STATUS_FATAL |
||
432 | lea ecx, [esi+usb_device_data.QueueLock] |
||
433 | call MutexUnlock |
||
434 | jmp complete_request |
||
435 | endp |
||
436 | |||
437 | ; Initiate USB transfer for the first stage of a request (send command). |
||
438 | proc request_stage1 |
||
439 | mov ebx, [esi+usb_device_data.RequestsQueue+request_queue_item.Next] |
||
440 | ; 1. Set the stage to 1 = command stage. |
||
441 | inc [ebx+request_queue_item.Stage] |
||
442 | ; 2. Generate the command. Zero-initialize and use the caller-provided proc. |
||
443 | lea edx, [esi+usb_device_data.Command] |
||
444 | xor eax, eax |
||
445 | mov [edx+command_block_wrapper.CommandLength], 12 |
||
446 | mov dword [edx+command_block_wrapper.Command], eax |
||
447 | mov dword [edx+command_block_wrapper.Command+4], eax |
||
448 | mov dword [edx+command_block_wrapper.Command+8], eax |
||
449 | mov dword [edx+command_block_wrapper.Command+12], eax |
||
450 | inc [edx+command_block_wrapper.Tag] |
||
451 | stdcall [ebx+request_queue_item.ReqBuilder], edx, [ebx+request_queue_item.UserData] |
||
452 | ; 4. Initiate USB transfer. |
||
453 | lea edx, [esi+usb_device_data.Command] |
||
454 | if DUMP_PACKETS |
||
455 | DEBUGF 1,'K : USBSTOR out:' |
||
456 | mov eax, edx |
||
457 | mov ecx, command_block_wrapper.sizeof |
||
458 | call debug_dump |
||
459 | DEBUGF 1,'\n' |
||
460 | end if |
||
461 | stdcall USBNormalTransferAsync, [esi+usb_device_data.OutPipe], edx, command_block_wrapper.sizeof, request_callback1, esi, 0 |
||
462 | ret |
||
463 | endp |
||
464 | |||
465 | if DUMP_PACKETS |
||
466 | proc debug_dump |
||
467 | test ecx, ecx |
||
468 | jz .done |
||
469 | .loop: |
||
470 | test ecx, 0Fh |
||
471 | jnz @f |
||
472 | DEBUGF 1,'\nK :' |
||
473 | @@: |
||
474 | DEBUGF 1,' %x',[eax]:2 |
||
475 | inc eax |
||
476 | dec ecx |
||
477 | jnz .loop |
||
478 | .done: |
||
479 | ret |
||
480 | endp |
||
481 | end if |
||
482 | |||
483 | ; Called when the Reset command is completed, |
||
484 | ; either successfully or not. |
||
485 | proc recovery_callback1 |
||
486 | virtual at esp |
||
487 | dd ? ; return address |
||
488 | .pipe dd ? |
||
489 | .status dd ? |
||
490 | .buffer dd ? |
||
491 | .length dd ? |
||
492 | .calldata dd ? |
||
493 | end virtual |
||
494 | cmp [.status], 0 |
||
495 | jnz .error |
||
496 | ; todo: reset pipes |
||
497 | push ebx esi |
||
498 | mov esi, [.calldata+8] |
||
499 | call request_stage1 |
||
500 | pop esi ebx |
||
501 | test eax, eax |
||
502 | jz .error |
||
503 | ret 20 |
||
504 | .error: |
||
505 | DEBUGF 1, 'K : error %d while resetting', [.status] |
||
506 | jmp request_callback1.common_error |
||
507 | endp |
||
508 | |||
509 | ; Called when the first stage of request is completed, |
||
510 | ; either successfully or not. |
||
511 | proc request_callback1 |
||
512 | virtual at esp |
||
513 | dd ? ; return address |
||
514 | .pipe dd ? |
||
515 | .status dd ? |
||
516 | .buffer dd ? |
||
517 | .length dd ? |
||
518 | .calldata dd ? |
||
519 | end virtual |
||
520 | ; 1. Initialize. |
||
521 | mov ecx, [.calldata] |
||
522 | mov eax, [.status] |
||
523 | ; 2. Test for error. |
||
524 | test eax, eax |
||
525 | jnz .error |
||
526 | ; No error. |
||
527 | ; 3. Increment the stage. |
||
528 | mov edx, [ecx+usb_device_data.RequestsQueue+request_queue_item.Next] |
||
529 | inc [edx+request_queue_item.Stage] |
||
530 | ; 4. If there is no data, skip this stage. |
||
531 | cmp [ecx+usb_device_data.Command.Length], 0 |
||
532 | jz ..request_get_status |
||
533 | ; 5. Initiate USB transfer. If this fails, go to the error handler. |
||
534 | mov eax, [ecx+usb_device_data.InPipe] |
||
535 | cmp [ecx+usb_device_data.Command.Flags], 0 |
||
536 | js @f |
||
537 | mov eax, [ecx+usb_device_data.OutPipe] |
||
538 | if DUMP_PACKETS |
||
539 | DEBUGF 1,'K : USBSTOR out:' |
||
540 | push eax ecx |
||
541 | mov eax, [edx+request_queue_item.Buffer] |
||
542 | mov ecx, [ecx+usb_device_data.Command.Length] |
||
543 | call debug_dump |
||
544 | pop ecx eax |
||
545 | DEBUGF 1,'\n' |
||
546 | end if |
||
547 | @@: |
||
548 | stdcall USBNormalTransferAsync, eax, [edx+request_queue_item.Buffer], [ecx+usb_device_data.Command.Length], request_callback2, ecx, 0 |
||
549 | test eax, eax |
||
550 | jz .error |
||
551 | ; 6. Return. |
||
552 | ret 20 |
||
553 | .error: |
||
554 | ; Error. |
||
555 | ; 7. Print debug message and complete the request as failed. |
||
556 | DEBUGF 1,'K : error %d after %d bytes in request stage\n',eax,[.length] |
||
557 | .common_error: |
||
558 | ; TODO: add recovery after STALL |
||
559 | mov ecx, [.calldata] |
||
560 | mov [ecx+usb_device_data.Status.Status], CSW_STATUS_FATAL |
||
561 | push ebx esi |
||
562 | mov esi, ecx |
||
563 | call complete_request |
||
564 | pop esi ebx |
||
565 | ret 20 |
||
566 | endp |
||
567 | |||
568 | ; Called when the second stage of request is completed, |
||
569 | ; either successfully or not. |
||
570 | proc request_callback2 |
||
571 | virtual at esp |
||
572 | dd ? ; return address |
||
573 | .pipe dd ? |
||
574 | .status dd ? |
||
575 | .buffer dd ? |
||
576 | .length dd ? |
||
577 | .calldata dd ? |
||
578 | end virtual |
||
579 | if DUMP_PACKETS |
||
580 | mov eax, [.calldata] |
||
581 | mov eax, [eax+usb_device_data.InPipe] |
||
582 | cmp [.pipe], eax |
||
583 | jnz @f |
||
584 | DEBUGF 1,'K : USBSTOR in:' |
||
585 | push eax ecx |
||
586 | mov eax, [.buffer+8] |
||
587 | mov ecx, [.length+8] |
||
588 | call debug_dump |
||
589 | pop ecx eax |
||
590 | DEBUGF 1,'\n' |
||
591 | @@: |
||
592 | end if |
||
593 | ; 1. Initialize. |
||
594 | mov ecx, [.calldata] |
||
595 | mov eax, [.status] |
||
596 | ; 2. Test for error. |
||
597 | test eax, eax |
||
598 | jnz .error |
||
599 | ; No error. |
||
600 | ..request_get_status: |
||
601 | ; 3. Increment the stage. |
||
602 | mov edx, [ecx+usb_device_data.RequestsQueue+request_queue_item.Next] |
||
603 | inc [edx+request_queue_item.Stage] |
||
604 | ; 4. Initiate USB transfer. If this fails, go to the error handler. |
||
605 | lea edx, [ecx+usb_device_data.Status] |
||
606 | stdcall USBNormalTransferAsync, [ecx+usb_device_data.InPipe], edx, command_status_wrapper.sizeof, request_callback3, ecx, 0 |
||
607 | test eax, eax |
||
608 | jz .error |
||
609 | ret 20 |
||
610 | .error: |
||
611 | ; Error. |
||
612 | ; 7. Print debug message and complete the request as failed. |
||
613 | DEBUGF 1,'K : error %d after %d bytes in data stage\n',eax,[.length] |
||
614 | jmp request_callback1.common_error |
||
615 | endp |
||
616 | |||
617 | ; Called when the third stage of request is completed, |
||
618 | ; either successfully or not. |
||
619 | proc request_callback3 |
||
620 | virtual at esp |
||
621 | dd ? ; return address |
||
622 | .pipe dd ? |
||
623 | .status dd ? |
||
624 | .buffer dd ? |
||
625 | .length dd ? |
||
626 | .calldata dd ? |
||
627 | end virtual |
||
628 | if DUMP_PACKETS |
||
629 | DEBUGF 1,'K : USBSTOR in:' |
||
630 | mov eax, [.buffer] |
||
631 | mov ecx, [.length] |
||
632 | call debug_dump |
||
633 | DEBUGF 1,'\n' |
||
634 | end if |
||
635 | ; 1. Initialize. |
||
636 | mov eax, [.status] |
||
637 | ; 2. Test for error. |
||
638 | test eax, eax |
||
639 | jnz .transfer_error |
||
640 | ; Transfer is OK. |
||
641 | ; 3. Validate the status. Invalid status = fatal error. |
||
642 | push ebx esi |
||
643 | mov esi, [.calldata+8] |
||
644 | mov ebx, [esi+usb_device_data.RequestsQueue+request_queue_item.Next] |
||
645 | cmp [esi+usb_device_data.Status.Signature], 'USBS' |
||
646 | jnz .invalid |
||
647 | mov eax, [esi+usb_device_data.Command.Tag] |
||
648 | cmp [esi+usb_device_data.Status.Tag], eax |
||
649 | jnz .invalid |
||
650 | cmp [esi+usb_device_data.Status.Status], CSW_STATUS_FATAL |
||
651 | ja .invalid |
||
652 | ; 4. The status block is valid. Check the status code. |
||
653 | jz .complete |
||
654 | ; 5. If this command was not REQUEST_SENSE, copy status data to safe place. |
||
655 | ; Otherwise, the original command has failed, so restore the fail status. |
||
656 | cmp byte [esi+usb_device_data.Command.Command], SCSI_REQUEST_SENSE |
||
657 | jz .request_sense |
||
658 | mov eax, [esi+usb_device_data.Status.LengthRest] |
||
659 | mov [esi+usb_device_data.LengthRest], eax |
||
660 | cmp [esi+usb_device_data.Status.Status], CSW_STATUS_FAIL |
||
661 | jz .fail |
||
662 | .complete: |
||
663 | call complete_request |
||
664 | .nothing: |
||
665 | pop esi ebx |
||
666 | ret 20 |
||
667 | .request_sense: |
||
668 | mov [esi+usb_device_data.Status.Status], CSW_STATUS_FAIL |
||
669 | jmp .complete |
||
670 | .invalid: |
||
671 | ; 6. Invalid status block. Say error, set status to fatal and complete request. |
||
672 | push esi |
||
673 | mov esi, invresponse |
||
674 | call SysMsgBoardStr |
||
675 | pop esi |
||
676 | mov [esi+usb_device_data.Status.Status], CSW_STATUS_FATAL |
||
677 | jmp .complete |
||
678 | .fail: |
||
679 | ; 7. The command has failed. |
||
680 | ; If this command was not REQUEST_SENSE, schedule the REQUEST_SENSE command |
||
681 | ; to determine the reason of fail. Otherwise, assume that there is no error data. |
||
682 | cmp [esi+usb_device_data.Command.Command], SCSI_REQUEST_SENSE |
||
683 | jz .fail_request_sense |
||
684 | mov [ebx+request_queue_item.ReqBuilder], request_sense_req |
||
685 | lea eax, [esi+usb_device_data.Sense] |
||
686 | mov [ebx+request_queue_item.Buffer], eax |
||
687 | call request_stage1 |
||
688 | test eax, eax |
||
689 | jnz .nothing |
||
690 | .fail_request_sense: |
||
691 | DEBUGF 1,'K : fail during REQUEST SENSE\n' |
||
692 | mov byte [esi+usb_device_data.Sense], 0 |
||
693 | jmp .complete |
||
694 | .transfer_error: |
||
695 | ; TODO: add recovery after STALL |
||
696 | DEBUGF 1,'K : error %d after %d bytes in status stage\n',eax,[.length] |
||
697 | jmp request_callback1.common_error |
||
698 | endp |
||
699 | |||
700 | ; Builder for SCSI_REQUEST_SENSE request. |
||
701 | ; edx = first argument = pointer to usb_device_data.Command, |
||
702 | ; second argument = custom data given to queue_request (ignored). |
||
703 | proc request_sense_req |
||
704 | mov [edx+command_block_wrapper.Length], sense_data.sizeof |
||
705 | mov [edx+command_block_wrapper.Flags], CBW_FLAG_IN |
||
706 | mov byte [edx+command_block_wrapper.Command+0], SCSI_REQUEST_SENSE |
||
707 | mov byte [edx+command_block_wrapper.Command+4], sense_data.sizeof |
||
708 | ret 8 |
||
709 | endp |
||
710 | |||
711 | ; This procedure is called when new mass-storage device is detected. |
||
712 | ; It initializes the device. |
||
713 | ; Technically, initialization implies sending several USB queries, |
||
714 | ; so it is split in several procedures. The first is AddDevice, |
||
715 | ; other are callbacks which will be called at some time in the future, |
||
716 | ; when the device will respond. |
||
717 | ; The general scheme: |
||
718 | ; * AddDevice parses descriptors, opens pipes; if everything is ok, |
||
719 | ; AddDevice sends REQUEST_GETMAXLUN with callback known_lun_callback; |
||
720 | ; * known_lun_callback allocates memory for LogicalDevices and sends |
||
721 | ; SCSI_TEST_UNIT_READY to all logical devices with test_unit_ready_callback; |
||
722 | ; * test_unit_ready_callback checks whether the unit is ready; |
||
723 | ; if not, it repeats the same request several times; |
||
724 | ; if ok or there were too many attempts, it sends SCSI_INQUIRY with |
||
725 | ; callback inquiry_callback; |
||
726 | ; * inquiry_callback checks that a logical device is a block device |
||
727 | ; and the unit was ready; if so, it notifies the kernel about new disk device. |
||
728 | proc AddDevice |
||
729 | push ebx esi |
||
730 | virtual at esp |
||
731 | rd 2 ; saved registers ebx, esi |
||
732 | dd ? ; return address |
||
733 | .pipe0 dd ? ; handle of the config pipe |
||
734 | .config dd ? ; pointer to config_descr |
||
735 | .interface dd ? ; pointer to interface_descr |
||
736 | end virtual |
||
737 | ; 1. Check device type. Currently only SCSI-command-set Bulk-only devices |
||
738 | ; are supported. |
||
739 | ; 1a. Get the subclass and the protocol. Since bInterfaceSubClass and |
||
740 | ; bInterfaceProtocol are subsequent in interface_descr, just one |
||
741 | ; memory reference is used for both. |
||
742 | mov esi, [.interface] |
||
743 | xor ebx, ebx |
||
744 | mov cx, word [esi+interface_descr.bInterfaceSubClass] |
||
745 | ; 1b. For Mass-storage SCSI-command-set Bulk-only devices subclass must be 6 |
||
746 | ; and protocol must be 50h. Check. |
||
747 | cmp cx, 0x5006 |
||
748 | jz .known |
||
749 | ; There are devices with subclass 5 which use the same protocol 50h. |
||
750 | ; The difference is not important for the code except for this test, |
||
751 | ; so allow them to proceed also. |
||
752 | cmp cx, 0x5005 |
||
753 | jz .known |
||
754 | ; 1c. If the device is unknown, print a message and go to 11c. |
||
755 | mov esi, unkdevice |
||
756 | call SysMsgBoardStr |
||
757 | jmp .nothing |
||
758 | ; 1d. If the device uses known command set, print a message and continue |
||
759 | ; configuring. |
||
760 | .known: |
||
761 | push esi |
||
762 | mov esi, okdevice |
||
763 | call SysMsgBoardStr |
||
764 | pop esi |
||
765 | ; 2. Allocate memory for internal device data. |
||
766 | ; 2a. Call the kernel. |
||
767 | mov eax, usb_device_data.sizeof |
||
768 | call Kmalloc |
||
769 | ; 2b. Check return value. |
||
770 | test eax, eax |
||
771 | jnz @f |
||
772 | ; 2c. If failed, say a message and go to 11c. |
||
773 | mov esi, nomemory |
||
774 | call SysMsgBoardStr |
||
775 | jmp .nothing |
||
776 | @@: |
||
777 | ; 2d. If succeeded, zero the contents and continue configuring. |
||
778 | xchg ebx, eax ; ebx will point to usb_device_data |
||
779 | xor eax, eax |
||
780 | mov [ebx+usb_device_data.OutPipe], eax |
||
781 | mov [ebx+usb_device_data.InPipe], eax |
||
782 | mov [ebx+usb_device_data.MaxLUN], eax |
||
783 | mov [ebx+usb_device_data.LogicalDevices], eax |
||
784 | mov dword [ebx+usb_device_data.ConfigRequest], eax |
||
785 | mov dword [ebx+usb_device_data.ConfigRequest+4], eax |
||
786 | mov [ebx+usb_device_data.Status.Status], al |
||
787 | mov [ebx+usb_device_data.DeviceDisconnected], al |
||
788 | ; 2e. There is one reference: a connected USB device. |
||
789 | inc eax |
||
790 | mov [ebx+usb_device_data.NumReferences], eax |
||
791 | ; 2f. Save handle of configuration pipe for reset recovery. |
||
792 | mov eax, [.pipe0] |
||
793 | mov [ebx+usb_device_data.ConfigPipe], eax |
||
794 | ; 2g. Save the interface number for configuration requests. |
||
795 | mov al, [esi+interface_descr.bInterfaceNumber] |
||
796 | mov [ebx+usb_device_data.ConfigRequest+4], al |
||
797 | ; 2h. Initialize common fields in command wrapper. |
||
798 | mov [ebx+usb_device_data.Command.Signature], 'USBC' |
||
799 | mov [ebx+usb_device_data.Command.Tag], 'xxxx' |
||
800 | ; 2i. Initialize requests queue. |
||
801 | lea eax, [ebx+usb_device_data.RequestsQueue] |
||
802 | mov [eax+request_queue_item.Next], eax |
||
803 | mov [eax+request_queue_item.Prev], eax |
||
804 | lea ecx, [ebx+usb_device_data.QueueLock] |
||
805 | call MutexInit |
||
806 | ; Bulk-only mass storage devices use one OUT bulk endpoint for sending |
||
807 | ; command/data and one IN bulk endpoint for receiving data/status. |
||
808 | ; Look for those endpoints. |
||
809 | ; 3. Get the upper bound of all descriptors' data. |
||
810 | mov edx, [.config] ; configuration descriptor |
||
811 | movzx ecx, [edx+config_descr.wTotalLength] |
||
812 | add edx, ecx |
||
813 | ; 4. Loop over all descriptors until |
||
814 | ; either end-of-data reached - this is fail |
||
815 | ; or interface descriptor found - this is fail, all further data |
||
816 | ; correspond to that interface |
||
817 | ; or both endpoint descriptors found. |
||
818 | ; 4a. Loop start: esi points to the interface descriptor, |
||
819 | .lookep: |
||
820 | ; 4b. Get next descriptor. |
||
821 | movzx ecx, byte [esi] ; the first byte of all descriptors is length |
||
822 | add esi, ecx |
||
823 | ; 4c. Check that at least two bytes are readable. The opposite is an error. |
||
824 | inc esi |
||
825 | cmp esi, edx |
||
826 | jae .errorep |
||
827 | dec esi |
||
828 | ; 4d. Check that this descriptor is not interface descriptor. The opposite is |
||
829 | ; an error. |
||
830 | cmp byte [esi+endpoint_descr.bDescriptorType], INTERFACE_DESCR_TYPE |
||
831 | jz .errorep |
||
832 | ; 4e. Test whether this descriptor is an endpoint descriptor. If not, continue |
||
833 | ; the loop. |
||
834 | cmp byte [esi+endpoint_descr.bDescriptorType], ENDPOINT_DESCR_TYPE |
||
835 | jnz .lookep |
||
836 | ; 5. Check that the descriptor contains all required data and all data are |
||
837 | ; readable. The opposite is an error. |
||
838 | cmp byte [esi+endpoint_descr.bLength], endpoint_descr.sizeof |
||
839 | jb .errorep |
||
840 | lea ecx, [esi+endpoint_descr.sizeof] |
||
841 | cmp ecx, edx |
||
842 | ja .errorep |
||
843 | ; 6. Check that the endpoint is bulk endpoint. The opposite is an error. |
||
844 | mov cl, [esi+endpoint_descr.bmAttributes] |
||
845 | and cl, 3 |
||
846 | cmp cl, BULK_PIPE |
||
847 | jnz .errorep |
||
848 | ; 7. Get the direction of this endpoint. |
||
849 | movzx ecx, [esi+endpoint_descr.bEndpointAddress] |
||
850 | shr ecx, 7 |
||
851 | ; 8. Test whether a pipe for this direction is already opened. If so, continue |
||
852 | ; the loop. |
||
853 | cmp [ebx+usb_device_data.OutPipe+ecx*4], 0 |
||
854 | jnz .lookep |
||
855 | ; 9. Open pipe for this endpoint. |
||
856 | ; 9a. Save registers. |
||
857 | push ecx edx |
||
858 | ; 9b. Load parameters from the descriptor. |
||
859 | movzx ecx, [esi+endpoint_descr.bEndpointAddress] |
||
860 | movzx edx, [esi+endpoint_descr.wMaxPacketSize] |
||
861 | movzx eax, [esi+endpoint_descr.bInterval] ; not used for USB1, may be important for USB2 |
||
862 | ; 9c. Call the kernel. |
||
863 | stdcall USBOpenPipe, [ebx+usb_device_data.ConfigPipe], ecx, edx, BULK_PIPE, eax |
||
864 | ; 9d. Restore registers. |
||
865 | pop edx ecx |
||
866 | ; 9e. Check result. If failed, go to 11b. |
||
867 | test eax, eax |
||
868 | jz .free |
||
869 | ; 9f. Save result. |
||
870 | mov [ebx+usb_device_data.OutPipe+ecx*4], eax |
||
871 | ; 10. Test whether the second pipe is already opened. If not, continue loop. |
||
872 | xor ecx, 1 |
||
873 | cmp [ebx+usb_device_data.OutPipe+ecx*4], 0 |
||
874 | jz .lookep |
||
875 | jmp .created |
||
876 | ; 11. An error occured during processing endpoint descriptor. |
||
877 | .errorep: |
||
878 | ; 11a. Print a message. |
||
879 | DEBUGF 1,'K : error: invalid endpoint descriptor\n' |
||
880 | .free: |
||
881 | ; 11b. Free the allocated usb_device_data. |
||
882 | xchg eax, ebx |
||
883 | call Kfree |
||
884 | .nothing: |
||
885 | ; 11c. Return an error. |
||
886 | xor eax, eax |
||
887 | jmp .return |
||
888 | .created: |
||
889 | ; 12. Pipes are opened. Send GetMaxLUN control request. |
||
890 | lea eax, [ebx+usb_device_data.ConfigRequest] |
||
891 | mov byte [eax], 0A1h ; class request from interface |
||
892 | mov byte [eax+1], REQUEST_GETMAXLUN |
||
893 | mov byte [eax+6], 1 ; transfer 1 byte |
||
894 | lea ecx, [ebx+usb_device_data.MaxLUN] |
||
895 | if DUMP_PACKETS |
||
896 | DEBUGF 1,'K : GETMAXLUN: %x %x %x %x %x %x %x %x\n',[eax]:2,[eax+1]:2,[eax+2]:2,[eax+3]:2,[eax+4]:2,[eax+5]:2,[eax+6]:2,[eax+7]:2 |
||
897 | end if |
||
898 | stdcall USBControlTransferAsync, [ebx+usb_device_data.ConfigPipe], eax, ecx, 1, known_lun_callback, ebx, 0 |
||
899 | ; 13. Return with pointer to device data as returned value. |
||
900 | xchg eax, ebx |
||
901 | .return: |
||
902 | pop esi ebx |
||
903 | ret 12 |
||
904 | endp |
||
905 | |||
906 | ; This function is called when REQUEST_GETMAXLUN is done, |
||
907 | ; either successful or unsuccessful. |
||
908 | proc known_lun_callback |
||
909 | push ebx esi |
||
910 | virtual at esp |
||
911 | rd 2 ; saved registers |
||
912 | dd ? ; return address |
||
913 | .pipe dd ? |
||
914 | .status dd ? |
||
915 | .buffer dd ? |
||
916 | .length dd ? |
||
917 | .calldata dd ? |
||
918 | end virtual |
||
919 | ; 1. Check the status. If the request failed, assume that MaxLUN is zero. |
||
920 | mov ebx, [.calldata] |
||
921 | mov eax, [.status] |
||
922 | test eax, eax |
||
923 | jz @f |
||
924 | DEBUGF 1, 'K : GETMAXLUN failed with status %d, assuming zero\n', eax |
||
925 | mov [ebx+usb_device_data.MaxLUN], 0 |
||
926 | @@: |
||
927 | ; 2. Allocate the memory for logical devices. |
||
928 | mov eax, [ebx+usb_device_data.MaxLUN] |
||
929 | inc eax |
||
930 | DEBUGF 1,'K : %d logical unit(s)\n',eax |
||
931 | imul eax, usb_unit_data.sizeof |
||
932 | push ebx |
||
933 | call Kmalloc |
||
934 | pop ebx |
||
935 | ; If failed, print a message and do nothing. |
||
936 | test eax, eax |
||
937 | jnz @f |
||
938 | mov esi, nomemory |
||
939 | call SysMsgBoardStr |
||
940 | pop esi ebx |
||
941 | ret 20 |
||
942 | @@: |
||
943 | mov [ebx+usb_device_data.LogicalDevices], eax |
||
944 | ; 3. Initialize logical devices and initiate TEST_UNIT_READY request. |
||
945 | xchg esi, eax |
||
946 | xor ecx, ecx |
||
947 | .looplun: |
||
948 | mov [esi+usb_unit_data.Parent], ebx |
||
949 | mov [esi+usb_unit_data.LUN], cl |
||
950 | xor eax, eax |
||
951 | mov [esi+usb_unit_data.MediaPresent], al |
||
952 | mov [esi+usb_unit_data.DiskDevice], eax |
||
953 | mov [esi+usb_unit_data.SectorSize], eax |
||
954 | mov [esi+usb_unit_data.UnitReadyAttempts], eax |
||
955 | push ecx |
||
956 | call GetTimerTicks |
||
957 | mov [esi+usb_unit_data.TimerTicks], eax |
||
958 | stdcall queue_request, ebx, test_unit_ready_req, 0, test_unit_ready_callback, esi |
||
959 | pop ecx |
||
960 | inc ecx |
||
961 | add esi, usb_unit_data.sizeof |
||
962 | cmp ecx, [ebx+usb_device_data.MaxLUN] |
||
963 | jbe .looplun |
||
964 | ; 4. Return. |
||
965 | pop esi ebx |
||
966 | ret 20 |
||
967 | endp |
||
968 | |||
969 | ; Builder for SCSI INQUIRY request. |
||
970 | ; edx = first argument = pointer to usb_device_data.Command, |
||
971 | ; second argument = custom data given to queue_request. |
||
972 | proc inquiry_req |
||
973 | mov eax, [esp+8] |
||
974 | mov al, [eax+usb_unit_data.LUN] |
||
975 | mov [edx+command_block_wrapper.Length], inquiry_data.sizeof |
||
976 | mov [edx+command_block_wrapper.Flags], CBW_FLAG_IN |
||
977 | mov [edx+command_block_wrapper.LUN], al |
||
978 | mov byte [edx+command_block_wrapper.Command+0], SCSI_INQUIRY |
||
979 | mov byte [edx+command_block_wrapper.Command+4], inquiry_data.sizeof |
||
980 | ret 8 |
||
981 | endp |
||
982 | |||
983 | ; Called when SCSI INQUIRY request is completed. |
||
984 | proc inquiry_callback |
||
985 | ; 1. Check the status. |
||
986 | mov ecx, [esp+4] |
||
987 | cmp [ecx+usb_device_data.Status.Status], CSW_STATUS_OK |
||
988 | jnz .fail |
||
989 | ; 2. The command has completed successfully. |
||
990 | ; Print a message showing device type, ignore anything but block devices. |
||
991 | mov al, [ecx+usb_device_data.InquiryData.PeripheralDevice] |
||
992 | and al, 1Fh |
||
993 | DEBUGF 1,'K : peripheral device type is %x\n',al |
||
994 | test al, al |
||
995 | jnz .nothing |
||
996 | DEBUGF 1,'K : direct-access mass storage device detected\n' |
||
997 | ; 3. We have found a new disk device. Increment number of references. |
||
998 | lock inc [ecx+usb_device_data.NumReferences] |
||
999 | ; Unfortunately, we are now in the context of the USB thread, |
||
1000 | ; so we can't notify the kernel immediately: it would try to do something |
||
1001 | ; with a new disk, those actions would be synchronous and would require |
||
1002 | ; waiting for results of USB requests, but we need to exit this callback |
||
1003 | ; to allow the USB thread to continue working and handling those requests. |
||
1004 | ; 4. Thus, create a temporary kernel thread which would do it. |
||
1005 | mov edx, [esp+8] |
||
1006 | push ebx ecx |
||
1007 | push 51 |
||
1008 | pop eax |
||
1009 | push 1 |
||
1010 | pop ebx |
||
1011 | mov ecx, new_disk_thread |
||
1012 | ; edx = parameter |
||
1013 | int 0x40 |
||
1014 | pop ecx ebx |
||
1015 | cmp eax, -1 |
||
1016 | jnz .nothing |
||
1017 | ; on error, reverse step 3 |
||
1018 | lock dec [ecx+usb_device_data.NumReferences] |
||
1019 | .nothing: |
||
1020 | ret 8 |
||
1021 | .fail: |
||
1022 | ; 4. The command has failed. Print a message and do nothing. |
||
1023 | push esi |
||
1024 | mov esi, inquiry_fail |
||
1025 | call SysMsgBoardStr |
||
1026 | pop esi |
||
1027 | ret 8 |
||
1028 | endp |
||
1029 | |||
1030 | ; Builder for SCSI TEST_UNIT_READY request. |
||
1031 | ; edx = first argument = pointer to usb_device_data.Command, |
||
1032 | ; second argument = custom data given to queue_request. |
||
1033 | proc test_unit_ready_req |
||
1034 | mov eax, [esp+8] |
||
1035 | mov al, [eax+usb_unit_data.LUN] |
||
1036 | mov [edx+command_block_wrapper.Length], 0 |
||
1037 | mov [edx+command_block_wrapper.Flags], CBW_FLAG_IN |
||
1038 | mov [edx+command_block_wrapper.LUN], al |
||
1039 | ret 8 |
||
1040 | endp |
||
1041 | |||
1042 | ; Called when SCSI TEST_UNIT_READY request is completed. |
||
1043 | proc test_unit_ready_callback |
||
1044 | virtual at esp |
||
1045 | dd ? ; return address |
||
1046 | .device dd ? |
||
1047 | .calldata dd ? |
||
1048 | end virtual |
||
1049 | ; 1. Check the status. |
||
1050 | mov ecx, [.device] |
||
1051 | mov edx, [.calldata] |
||
1052 | cmp [ecx+usb_device_data.Status.Status], CSW_STATUS_OK |
||
1053 | jnz .fail |
||
1054 | ; 2. The command has completed successfully, |
||
1055 | ; possibly after some repetitions. Print a debug message showing |
||
1056 | ; number and time of those. Remember that media is ready and go to 4. |
||
1057 | DEBUGF 1,'K : media is ready\n' |
||
1058 | call GetTimerTicks |
||
1059 | sub eax, [edx+usb_unit_data.TimerTicks] |
||
1060 | DEBUGF 1,'K : %d attempts, %d ticks\n',[edx+usb_unit_data.UnitReadyAttempts],eax |
||
1061 | inc [edx+usb_unit_data.MediaPresent] |
||
1062 | jmp .inquiry |
||
1063 | .fail: |
||
1064 | ; 3. The command has failed. |
||
1065 | ; Retry the same request up to 3 times with 10ms delay; |
||
1066 | ; if limit of retries is not reached, exit from the function. |
||
1067 | ; Otherwise, go to 4. |
||
1068 | inc [edx+usb_unit_data.UnitReadyAttempts] |
||
1069 | cmp [edx+usb_unit_data.UnitReadyAttempts], 3 |
||
1070 | jz @f |
||
1071 | push ecx edx esi |
||
1072 | push 10 |
||
1073 | pop esi |
||
1074 | call Sleep |
||
1075 | pop esi edx ecx |
||
1076 | stdcall queue_request, ecx, test_unit_ready_req, 0, test_unit_ready_callback, edx |
||
1077 | ret 8 |
||
1078 | @@: |
||
1079 | DEBUGF 1,'K : media not ready\n' |
||
1080 | .inquiry: |
||
1081 | ; 4. initiate INQUIRY request. |
||
1082 | lea eax, [ecx+usb_device_data.InquiryData] |
||
1083 | stdcall queue_request, ecx, inquiry_req, eax, inquiry_callback, edx |
||
1084 | ret 8 |
||
1085 | endp |
||
1086 | |||
1087 | ; Temporary thread for initial actions with a new disk device. |
||
1088 | proc new_disk_thread |
||
1089 | sub esp, 32 |
||
1090 | virtual at esp |
||
1091 | .name rb 32 ; device name |
||
1092 | .param dd ? ; contents of edx at the moment of int 0x40/eax=51 |
||
1093 | dd ? ; stack segment |
||
1094 | end virtual |
||
1095 | ; We are ready to notify the kernel about a new disk device. |
||
1096 | mov esi, [.param] |
||
1097 | ; 1. Generate name. |
||
1098 | ; 1a. Find a free index. |
||
1099 | mov ecx, free_numbers_lock |
||
1100 | call MutexLock |
||
1101 | xor eax, eax |
||
1102 | @@: |
||
1103 | bsf edx, [free_numbers+eax] |
||
1104 | jnz @f |
||
1105 | add eax, 4 |
||
1106 | cmp eax, 4*4 |
||
1107 | jnz @b |
||
1108 | call MutexUnlock |
||
1109 | push esi |
||
1110 | mov esi, noindex |
||
1111 | call SysMsgBoardStr |
||
1112 | pop esi |
||
1113 | jmp .drop_reference |
||
1114 | @@: |
||
1115 | ; 1b. Mark the index as busy. |
||
1116 | btr [free_numbers+eax], edx |
||
1117 | lea eax, [eax*8+edx] |
||
1118 | push eax |
||
1119 | call MutexUnlock |
||
1120 | pop eax |
||
1121 | ; 1c. Generate a name of the form "usbhd |
||
1122 | mov dword [esp], 'usbh' |
||
1123 | lea edi, [esp+5] |
||
1124 | mov byte [edi-1], 'd' |
||
1125 | push eax |
||
1126 | push -'0' |
||
1127 | push 10 |
||
1128 | pop ecx |
||
1129 | @@: |
||
1130 | cdq |
||
1131 | div ecx |
||
1132 | push edx |
||
1133 | test eax, eax |
||
1134 | jnz @b |
||
1135 | @@: |
||
1136 | pop eax |
||
1137 | add al, '0' |
||
1138 | stosb |
||
1139 | jnz @b |
||
1140 | pop ecx |
||
1141 | mov edx, esp |
||
1142 | ; 3d. Store the index in usb_unit_data to free it later. |
||
1143 | mov [esi+usb_unit_data.DiskIndex], cl |
||
1144 | ; 4. Notify the kernel about a new disk. |
||
1145 | ; 4a. Add a disk. |
||
1146 | ; stdcall queue_request, ecx, read_capacity_req, eax, read_capacity_callback, eax |
||
1147 | stdcall DiskAdd, disk_functions, edx, esi, 0 |
||
1148 | mov ebx, eax |
||
1149 | ; 4b. If it failed, release the index and do nothing. |
||
1150 | test eax, eax |
||
1151 | jz .free_index |
||
1152 | ; 4c. Notify the kernel that a media is present. |
||
1153 | stdcall DiskMediaChanged, eax, 1 |
||
1154 | ; 5. Lock the requests queue, check that device is not disconnected, |
||
1155 | ; store the disk handle, unlock the requests queue. |
||
1156 | mov ecx, [esi+usb_unit_data.Parent] |
||
1157 | add ecx, usb_device_data.QueueLock |
||
1158 | call MutexLock |
||
1159 | cmp byte [ecx+usb_device_data.DeviceDisconnected-usb_device_data.QueueLock], 0 |
||
1160 | jnz .disconnected |
||
1161 | mov [esi+usb_unit_data.DiskDevice], ebx |
||
1162 | call MutexUnlock |
||
1163 | jmp .exit |
||
1164 | .disconnected: |
||
1165 | call MutexUnlock |
||
1166 | stdcall disk_close, ebx |
||
1167 | jmp .exit |
||
1168 | .free_index: |
||
1169 | mov ecx, free_numbers_lock |
||
1170 | call MutexLock |
||
1171 | movzx eax, [esi+usb_unit_data.DiskIndex] |
||
1172 | bts [free_numbers], eax |
||
1173 | call MutexUnlock |
||
1174 | .drop_reference: |
||
1175 | mov esi, [esi+usb_unit_data.Parent] |
||
1176 | lock dec [esi+usb_device_data.NumReferences] |
||
1177 | jnz .exit |
||
1178 | mov eax, [esi+usb_device_data.LogicalDevices] |
||
1179 | call Kfree |
||
1180 | xchg eax, esi |
||
1181 | call Kfree |
||
1182 | .exit: |
||
1183 | or eax, -1 |
||
1184 | int 0x40 |
||
1185 | endp |
||
1186 | |||
1187 | ; This function is called when the device is disconnected. |
||
1188 | proc DeviceDisconnected |
||
1189 | push ebx esi |
||
1190 | virtual at esp |
||
1191 | rd 2 ; saved registers |
||
1192 | dd ? ; return address |
||
1193 | .device dd ? |
||
1194 | end virtual |
||
1195 | ; 1. Say a message. |
||
1196 | mov esi, disconnectmsg |
||
1197 | call SysMsgBoardStr |
||
1198 | ; 2. Lock the requests queue, set .DeviceDisconnected to 1, |
||
1199 | ; unlock the requests queue. |
||
1200 | ; Locking is required for synchronization with queue_request: |
||
1201 | ; all USB callbacks are executed in the same thread and are |
||
1202 | ; synchronized automatically, but queue_request can be running |
||
1203 | ; from any thread which wants to do something with a filesystem. |
||
1204 | ; Without locking, it would be possible that queue_request has |
||
1205 | ; been started, has checked that device is not yet disconnected, |
||
1206 | ; then DeviceDisconnected completes and all handles become invalid, |
||
1207 | ; then queue_request tries to use them. |
||
1208 | mov esi, [.device] |
||
1209 | lea ecx, [esi+usb_device_data.QueueLock] |
||
1210 | call MutexLock |
||
1211 | mov [esi+usb_device_data.DeviceDisconnected], 1 |
||
1212 | call MutexUnlock |
||
1213 | ; 3. Drop one reference to the structure and check whether |
||
1214 | ; that was the last reference. |
||
1215 | lock dec [esi+usb_device_data.NumReferences] |
||
1216 | jz .free |
||
1217 | ; 4. If not, there are some additional references due to disk devices; |
||
1218 | ; notify the kernel that those disks are deleted. |
||
1219 | ; Note that new disks cannot be added while we are looping here, |
||
1220 | ; because new_disk_thread checks for .DeviceDisconnected. |
||
1221 | mov ebx, [esi+usb_device_data.MaxLUN] |
||
1222 | mov esi, [esi+usb_device_data.LogicalDevices] |
||
1223 | inc ebx |
||
1224 | .diskdel: |
||
1225 | mov eax, [esi+usb_unit_data.DiskDevice] |
||
1226 | test eax, eax |
||
1227 | jz @f |
||
1228 | stdcall DiskDel, eax |
||
1229 | @@: |
||
1230 | add esi, usb_unit_data.sizeof |
||
1231 | dec ebx |
||
1232 | jnz .diskdel |
||
1233 | ; In this case, some operations with those disks are still possible, |
||
1234 | ; so we can't do anything more now. disk_close will take care of the rest. |
||
1235 | .return: |
||
1236 | pop esi ebx |
||
1237 | ret 4 |
||
1238 | ; 5. If there are no disk devices, free all resources which were allocated. |
||
1239 | .free: |
||
1240 | mov eax, [esi+usb_device_data.LogicalDevices] |
||
1241 | test eax, eax |
||
1242 | jz @f |
||
1243 | call Kfree |
||
1244 | @@: |
||
1245 | xchg eax, esi |
||
1246 | call Kfree |
||
1247 | jmp .return |
||
1248 | endp |
||
1249 | |||
1250 | ; Disk functions. |
||
1251 | DISK_STATUS_OK = 0 ; success |
||
1252 | DISK_STATUS_GENERAL_ERROR = -1; if no other code is suitable |
||
1253 | DISK_STATUS_INVALID_CALL = 1 ; invalid input parameters |
||
1254 | DISK_STATUS_NO_MEDIA = 2 ; no media present |
||
1255 | DISK_STATUS_END_OF_MEDIA = 3 ; end of media while reading/writing data |
||
1256 | |||
1257 | ; Called when all operations with the given disk are done. |
||
1258 | proc disk_close |
||
1259 | push ebx esi |
||
1260 | virtual at esp |
||
1261 | rd 2 ; saved registers |
||
1262 | dd ? ; return address |
||
1263 | .userdata dd ? |
||
1264 | end virtual |
||
1265 | mov esi, [.userdata] |
||
1266 | mov ecx, free_numbers_lock |
||
1267 | call MutexLock |
||
1268 | movzx eax, [esi+usb_unit_data.DiskIndex] |
||
1269 | bts [free_numbers], eax |
||
1270 | call MutexUnlock |
||
1271 | mov esi, [esi+usb_unit_data.Parent] |
||
1272 | lock dec [esi+usb_device_data.NumReferences] |
||
1273 | jnz .nothing |
||
1274 | mov eax, [esi+usb_device_data.LogicalDevices] |
||
1275 | call Kfree |
||
1276 | xchg eax, esi |
||
1277 | call Kfree |
||
1278 | .nothing: |
||
1279 | pop esi ebx |
||
1280 | ret 4 |
||
1281 | endp |
||
1282 | |||
1283 | ; Returns sector size, capacity and flags of the media. |
||
1284 | proc disk_querymedia stdcall uses ebx esi edi, \ |
||
1285 | userdata:dword, mediainfo:dword |
||
1286 | ; 1. Create event for waiting. |
||
1287 | xor esi, esi |
||
1288 | xor ecx, ecx |
||
1289 | call CreateEvent |
||
1290 | test eax, eax |
||
1291 | jz .generic_fail |
||
1292 | push eax |
||
1293 | push edx |
||
1294 | push ecx |
||
1295 | push 0 |
||
1296 | push 0 |
||
1297 | virtual at ebp-.localsize |
||
1298 | .locals: |
||
1299 | ; two following dwords are the output of READ_CAPACITY |
||
1300 | .LastLBABE dd ? |
||
1301 | .SectorSizeBE dd ? |
||
1302 | .Status dd ? |
||
1303 | ; two following dwords identify an event |
||
1304 | .event_code dd ? |
||
1305 | .event dd ? |
||
1306 | rd 3 ; saved registers |
||
1307 | .localsize = $ - .locals |
||
1308 | dd ? ; saved ebp |
||
1309 | dd ? ; return address |
||
1310 | .userdata dd ? |
||
1311 | .mediainfo dd ? |
||
1312 | end virtual |
||
1313 | ; 2. Initiate SCSI READ_CAPACITY request. |
||
1314 | mov eax, [userdata] |
||
1315 | mov ecx, [eax+usb_unit_data.Parent] |
||
1316 | mov edx, esp |
||
1317 | stdcall queue_request, ecx, read_capacity_req, edx, read_capacity_callback, edx |
||
1318 | ; 3. Wait for event. This destroys it. |
||
1319 | mov eax, [.event] |
||
1320 | mov ebx, [.event_code] |
||
1321 | call WaitEvent |
||
1322 | ; 4. Get the status and results. |
||
1323 | pop ecx |
||
1324 | bswap ecx ; .LastLBA |
||
1325 | pop edx |
||
1326 | bswap edx ; .SectorSize |
||
1327 | pop eax ; .Status |
||
1328 | ; 5. If the request has completed successfully, store results. |
||
1329 | test eax, eax |
||
1330 | jnz @f |
||
1331 | DEBUGF 1,'K : sector size is %d, last sector is %d\n',edx,ecx |
||
1332 | mov ebx, [mediainfo] |
||
1333 | mov [ebx], eax ; flags = 0 |
||
1334 | mov [ebx+4], edx ; sectorsize |
||
1335 | add ecx, 1 |
||
1336 | adc eax, 0 |
||
1337 | mov [ebx+8], ecx |
||
1338 | mov [ebx+12], eax ; capacity |
||
1339 | mov eax, [userdata] |
||
1340 | mov [eax+usb_unit_data.SectorSize], edx |
||
1341 | xor eax, eax |
||
1342 | @@: |
||
1343 | ; 6. Restore the stack and return. |
||
1344 | pop ecx |
||
1345 | pop ecx |
||
1346 | ret |
||
1347 | .generic_fail: |
||
1348 | or eax, -1 |
||
1349 | ret |
||
1350 | endp |
||
1351 | |||
1352 | ; Builder for SCSI READ_CAPACITY request. |
||
1353 | ; edx = first argument = pointer to usb_device_data.Command, |
||
1354 | ; second argument = custom data given to queue_request, |
||
1355 | ; pointer to disk_querymedia.locals. |
||
1356 | proc read_capacity_req |
||
1357 | mov eax, [esp+8] |
||
1358 | mov eax, [eax+disk_querymedia.userdata-disk_querymedia.locals] |
||
1359 | mov al, [eax+usb_unit_data.LUN] |
||
1360 | mov [edx+command_block_wrapper.Length], 8 |
||
1361 | mov [edx+command_block_wrapper.Flags], CBW_FLAG_IN |
||
1362 | mov [edx+command_block_wrapper.LUN], al |
||
1363 | mov byte [edx+command_block_wrapper.Command+0], SCSI_READ_CAPACITY |
||
1364 | ret 8 |
||
1365 | endp |
||
1366 | |||
1367 | ; Called when SCSI READ_CAPACITY request is completed. |
||
1368 | proc read_capacity_callback |
||
1369 | ; Transform the status to return value of disk_querymedia |
||
1370 | ; and set the event. |
||
1371 | mov ecx, [esp+4] |
||
1372 | xor eax, eax |
||
1373 | cmp [ecx+usb_device_data.Status.Status], al |
||
1374 | jz @f |
||
1375 | or eax, -1 |
||
1376 | @@: |
||
1377 | mov ecx, [esp+8] |
||
1378 | mov [ecx+disk_querymedia.Status-disk_querymedia.locals], eax |
||
1379 | push ebx esi edi |
||
1380 | mov eax, [ecx+disk_querymedia.event-disk_querymedia.locals] |
||
1381 | mov ebx, [ecx+disk_querymedia.event_code-disk_querymedia.locals] |
||
1382 | xor edx, edx |
||
1383 | xor esi, esi |
||
1384 | call RaiseEvent |
||
1385 | pop edi esi ebx |
||
1386 | ret 8 |
||
1387 | endp |
||
1388 | |||
1389 | disk_write: |
||
1390 | mov al, SCSI_WRITE10 |
||
1391 | jmp disk_read_write |
||
1392 | |||
1393 | disk_read: |
||
1394 | mov al, SCSI_READ10 |
||
1395 | |||
1396 | ; Reads from the device or writes to the device. |
||
1397 | proc disk_read_write stdcall uses ebx esi edi, \ |
||
1398 | userdata:dword, buffer:dword, startsector:qword, numsectors:dword |
||
1399 | ; 1. Initialize. |
||
1400 | push eax ; .command |
||
1401 | mov eax, [userdata] |
||
1402 | mov eax, [eax+usb_unit_data.SectorSize] |
||
1403 | push eax ; .SectorSize |
||
1404 | push 0 ; .processed |
||
1405 | mov eax, [numsectors] |
||
1406 | mov eax, [eax] |
||
1407 | ; 2. The transfer length for SCSI_{READ,WRITE}10 commands can not be greater |
||
1408 | ; than 0xFFFF, so split the request to slices with <= 0xFFFF sectors. |
||
1409 | max_sectors_at_time = 0xFFFF |
||
1410 | .split: |
||
1411 | push eax ; .length_rest |
||
1412 | cmp eax, max_sectors_at_time |
||
1413 | jb @f |
||
1414 | mov eax, max_sectors_at_time |
||
1415 | @@: |
||
1416 | sub [esp], eax |
||
1417 | push eax ; .length_cur |
||
1418 | ; 3. startsector must fit in 32 bits, otherwise abort the request. |
||
1419 | cmp dword [startsector+4], 0 |
||
1420 | jnz .generic_fail |
||
1421 | ; 4. Create event for waiting. |
||
1422 | xor esi, esi |
||
1423 | xor ecx, ecx |
||
1424 | call CreateEvent |
||
1425 | test eax, eax |
||
1426 | jz .generic_fail |
||
1427 | push eax ; .event |
||
1428 | push edx ; .event_code |
||
1429 | push ecx ; .status |
||
1430 | virtual at ebp-.localsize |
||
1431 | .locals: |
||
1432 | .status dd ? |
||
1433 | .event_code dd ? |
||
1434 | .event dd ? |
||
1435 | .length_cur dd ? |
||
1436 | .length_rest dd ? |
||
1437 | .processed dd ? |
||
1438 | .SectorSize dd ? |
||
1439 | .command db ? |
||
1440 | rb 3 |
||
1441 | rd 3 ; saved registers |
||
1442 | .localsize = $ - .locals |
||
1443 | dd ? ; saved ebp |
||
1444 | dd ? ; return address |
||
1445 | .userdata dd ? |
||
1446 | .buffer dd ? |
||
1447 | .startsector dq ? |
||
1448 | .numsectors dd ? |
||
1449 | end virtual |
||
1450 | ; 5. Initiate SCSI READ10 or WRITE10 request. |
||
1451 | mov eax, [userdata] |
||
1452 | mov ecx, [eax+usb_unit_data.Parent] |
||
1453 | stdcall queue_request, ecx, read_write_req, [buffer], read_write_callback, esp |
||
1454 | ; 6. Wait for event. This destroys it. |
||
1455 | mov eax, [.event] |
||
1456 | mov ebx, [.event_code] |
||
1457 | call WaitEvent |
||
1458 | ; 7. Get the status. If the operation has failed, abort. |
||
1459 | pop eax ; .status |
||
1460 | pop ecx ecx ; cleanup .event_code, .event |
||
1461 | pop ecx ; .length_cur |
||
1462 | test eax, eax |
||
1463 | jnz .return |
||
1464 | ; 8. Otherwise, continue the loop started at step 2. |
||
1465 | add dword [startsector], ecx |
||
1466 | adc dword [startsector+4], eax |
||
1467 | imul ecx, [.SectorSize] |
||
1468 | add [buffer], ecx |
||
1469 | pop eax |
||
1470 | test eax, eax |
||
1471 | jnz .split |
||
1472 | push eax |
||
1473 | .return: |
||
1474 | ; 9. Restore the stack, store .processed to [numsectors], return. |
||
1475 | pop ecx ; .length_rest |
||
1476 | pop ecx ; .processed |
||
1477 | mov edx, [numsectors] |
||
1478 | mov [edx], ecx |
||
1479 | pop ecx ; .SectorSize |
||
1480 | pop ecx ; .command |
||
1481 | ret |
||
1482 | .generic_fail: |
||
1483 | or eax, -1 |
||
1484 | pop ecx ; .length_cur |
||
1485 | jmp .return |
||
1486 | endp |
||
1487 | |||
1488 | ; Builder for SCSI READ10 or WRITE10 request. |
||
1489 | ; edx = first argument = pointer to usb_device_data.Command, |
||
1490 | ; second argument = custom data given to queue_request, |
||
1491 | ; pointer to disk_read_write.locals. |
||
1492 | proc read_write_req |
||
1493 | mov eax, [esp+8] |
||
1494 | mov ecx, [eax+disk_read_write.userdata-disk_read_write.locals] |
||
1495 | mov cl, [ecx+usb_unit_data.LUN] |
||
1496 | mov [edx+command_block_wrapper.LUN], cl |
||
1497 | mov ecx, [eax+disk_read_write.length_cur-disk_read_write.locals] |
||
1498 | imul ecx, [eax+disk_read_write.SectorSize-disk_read_write.locals] |
||
1499 | mov [edx+command_block_wrapper.Length], ecx |
||
1500 | mov cl, [eax+disk_read_write.command-disk_read_write.locals] |
||
1501 | mov [edx+command_block_wrapper.Flags], CBW_FLAG_OUT |
||
1502 | cmp cl, SCSI_READ10 |
||
1503 | jnz @f |
||
1504 | mov [edx+command_block_wrapper.Flags], CBW_FLAG_IN |
||
1505 | @@: |
||
1506 | mov byte [edx+command_block_wrapper.Command], cl |
||
1507 | mov ecx, dword [eax+disk_read_write.startsector-disk_read_write.locals] |
||
1508 | bswap ecx |
||
1509 | mov dword [edx+command_block_wrapper.Command+2], ecx |
||
1510 | mov ecx, [eax+disk_read_write.length_cur-disk_read_write.locals] |
||
1511 | xchg cl, ch |
||
1512 | mov word [edx+command_block_wrapper.Command+7], cx |
||
1513 | ret 8 |
||
1514 | endp |
||
1515 | |||
1516 | ; Called when SCSI READ10 or WRITE10 request is completed. |
||
1517 | proc read_write_callback |
||
1518 | ; 1. Initialize. |
||
1519 | push ebx esi edi |
||
1520 | virtual at esp |
||
1521 | rd 3 ; saved registers |
||
1522 | dd ? ; return address |
||
1523 | .device dd ? |
||
1524 | .calldata dd ? |
||
1525 | end virtual |
||
1526 | mov ecx, [.device] |
||
1527 | mov esi, [.calldata] |
||
1528 | ; 2. Get the number of sectors which were read. |
||
1529 | ; If the status is OK or FAIL, the field .LengthRest is valid. |
||
1530 | ; Otherwise, it is invalid, so assume zero sectors. |
||
1531 | xor eax, eax |
||
1532 | cmp [ecx+usb_device_data.Status.Status], CSW_STATUS_FAIL |
||
1533 | ja .sectors_calculated |
||
1534 | mov eax, [ecx+usb_device_data.LengthRest] |
||
1535 | xor edx, edx |
||
1536 | div [esi+disk_read_write.SectorSize-disk_read_write.locals] |
||
1537 | test edx, edx |
||
1538 | jz @f |
||
1539 | inc eax |
||
1540 | @@: |
||
1541 | mov edx, eax |
||
1542 | mov eax, [esi+disk_read_write.length_cur-disk_read_write.locals] |
||
1543 | sub eax, edx |
||
1544 | jae .sectors_calculated |
||
1545 | xor eax, eax |
||
1546 | .sectors_calculated: |
||
1547 | ; 3. Increase the total number of processed sectors. |
||
1548 | add [esi+disk_read_write.processed-disk_read_write.locals], eax |
||
1549 | ; 4. Set status to OK if all sectors were read, to ERROR otherwise. |
||
1550 | cmp eax, [esi+disk_read_write.length_cur-disk_read_write.locals] |
||
1551 | setz al |
||
1552 | movzx eax, al |
||
1553 | dec eax |
||
1554 | mov [esi+disk_read_write.status-disk_read_write.locals], eax |
||
1555 | ; 5. Set the event. |
||
1556 | mov eax, [esi+disk_read_write.event-disk_read_write.locals] |
||
1557 | mov ebx, [esi+disk_read_write.event_code-disk_read_write.locals] |
||
1558 | xor edx, edx |
||
1559 | xor esi, esi |
||
1560 | call RaiseEvent |
||
1561 | ; 6. Return. |
||
1562 | pop edi esi ebx |
||
1563 | ret 8 |
||
1564 | endp |
||
1565 | |||
1566 | ; strings |
||
1567 | my_driver db 'usbstor',0 |
||
1568 | disconnectmsg db 'K : USB mass storage device disconnected',13,10,0 |
||
1569 | nomemory db 'K : no memory',13,10,0 |
||
1570 | unkdevice db 'K : unknown mass storage device',13,10,0 |
||
1571 | okdevice db 'K : USB mass storage device detected',13,10,0 |
||
1572 | transfererror db 'K : USB transfer error, disabling mass storage',13,10,0 |
||
1573 | invresponse db 'K : invalid response from mass storage device',13,10,0 |
||
1574 | fatalerr db 'K : mass storage device reports fatal error',13,10,0 |
||
1575 | inquiry_fail db 'K : INQUIRY command failed',13,10,0 |
||
1576 | ;read_capacity_fail db 'K : READ CAPACITY command failed',13,10,0 |
||
1577 | ;read_fail db 'K : READ command failed',13,10,0 |
||
1578 | noindex db 'K : failed to generate disk name',13,10,0 |
||
1579 | |||
1580 | ; Exported variable: kernel API version. |
||
1581 | align 4 |
||
1582 | version dd 50005h |
||
1583 | ; Structure with callback functions. |
||
1584 | usb_functions: |
||
1585 | dd usb_functions_end - usb_functions |
||
1586 | dd AddDevice |
||
1587 | dd DeviceDisconnected |
||
1588 | usb_functions_end: |
||
1589 | |||
1590 | disk_functions: |
||
1591 | dd disk_functions_end - disk_functions |
||
1592 | dd disk_close |
||
1593 | dd 0 ; closemedia |
||
1594 | dd disk_querymedia |
||
1595 | dd disk_read |
||
1596 | dd disk_write |
||
1597 | dd 0 ; flush |
||
1598 | dd 0 ; adjust_cache_size: use default cache |
||
1599 | disk_functions_end: |
||
1600 | |||
1601 | free_numbers_lock rd 3 |
||
1602 | ; 128 devices should be enough for everybody |
||
1603 | free_numbers dd -1, -1, -1, -1 |
||
1604 | |||
1605 | ; for DEBUGF macro |
||
1606 | include_debug_strings |
||
1607 | |||
1608 | ; for uninitialized data |
||
1609 | section '.data' data readable writable align 16=> |