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 |
1117 | ; 1c. Generate a name of the form "usbhd |
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=> |