Subversion Repositories Kolibri OS

Rev

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