Subversion Repositories Kolibri OS

Rev

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