Subversion Repositories Kolibri OS

Rev

Rev 3520 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

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