Rev 3908 | Rev 5201 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
3555 | Serge | 1 | ; Functions for USB pipe manipulation: opening/closing, sending data etc. |
2 | ; |
||
3 | USB_STDCALL_VERIFY = 1 |
||
4 | macro stdcall_verify [arg] |
||
5 | { |
||
6 | common |
||
7 | if USB_STDCALL_VERIFY |
||
8 | pushad |
||
9 | stdcall arg |
||
10 | call verify_regs |
||
11 | popad |
||
12 | else |
||
13 | stdcall arg |
||
14 | end if |
||
15 | } |
||
16 | |||
17 | ; Initialization of usb_static_ep structure, |
||
18 | ; called from controller-specific initialization; edi -> usb_static_ep |
||
19 | proc usb_init_static_endpoint |
||
20 | mov [edi+usb_static_ep.NextVirt], edi |
||
21 | mov [edi+usb_static_ep.PrevVirt], edi |
||
22 | ret |
||
23 | endp |
||
24 | |||
25 | ; Part of API for drivers, see documentation for USBOpenPipe. |
||
26 | proc usb_open_pipe stdcall uses ebx esi edi,\ |
||
27 | config_pipe:dword, endpoint:dword, maxpacket:dword, type:dword, interval:dword |
||
28 | locals |
||
4423 | Serge | 29 | tt_vars rd 24 ; should be enough for ehci_select_tt_interrupt_list |
3555 | Serge | 30 | targetsmask dd ? ; S-Mask for USB2 |
31 | bandwidth dd ? |
||
32 | target dd ? |
||
33 | endl |
||
34 | ; 1. Verify type of pipe: it must be one of *_PIPE constants. |
||
35 | ; Isochronous pipes are not supported yet. |
||
36 | mov eax, [type] |
||
37 | cmp eax, INTERRUPT_PIPE |
||
38 | ja .badtype |
||
39 | cmp al, ISOCHRONOUS_PIPE |
||
40 | jnz .goodtype |
||
41 | .badtype: |
||
42 | dbgstr 'unsupported type of USB pipe' |
||
43 | jmp .return0 |
||
44 | .goodtype: |
||
45 | ; 2. Allocate memory for pipe and transfer queue. |
||
46 | ; Empty transfer queue consists of one inactive TD. |
||
47 | mov ebx, [config_pipe] |
||
48 | mov esi, [ebx+usb_pipe.Controller] |
||
49 | mov edx, [esi+usb_controller.HardwareFunc] |
||
50 | call [edx+usb_hardware_func.AllocPipe] |
||
51 | test eax, eax |
||
52 | jz .nothing |
||
53 | mov edi, eax |
||
54 | mov edx, [esi+usb_controller.HardwareFunc] |
||
55 | call [edx+usb_hardware_func.AllocTD] |
||
56 | test eax, eax |
||
57 | jz .free_and_return0 |
||
58 | ; 3. Initialize transfer queue: pointer to transfer descriptor, |
||
59 | ; pointers in transfer descriptor, queue lock. |
||
60 | mov [edi+usb_pipe.LastTD], eax |
||
61 | mov [eax+usb_gtd.NextVirt], eax |
||
62 | mov [eax+usb_gtd.PrevVirt], eax |
||
63 | mov [eax+usb_gtd.Pipe], edi |
||
64 | lea ecx, [edi+usb_pipe.Lock] |
||
65 | call mutex_init |
||
66 | ; 4. Initialize software part of pipe structure, except device-related fields. |
||
67 | mov al, byte [type] |
||
68 | mov [edi+usb_pipe.Type], al |
||
69 | xor eax, eax |
||
70 | mov [edi+usb_pipe.Flags], al |
||
71 | mov [edi+usb_pipe.DeviceData], eax |
||
72 | mov [edi+usb_pipe.Controller], esi |
||
73 | or [edi+usb_pipe.NextWait], -1 |
||
74 | ; 5. Initialize device-related fields: |
||
75 | ; for zero endpoint, set .NextSibling = .PrevSibling = this; |
||
76 | ; for other endpoins, copy device data, take the lock guarding pipe list |
||
77 | ; for the device and verify that disconnect processing has not yet started |
||
78 | ; for the device. (Since disconnect processing also takes that lock, |
||
79 | ; either it has completed or it will not start until we release the lock.) |
||
80 | ; Note: usb_device_disconnected should not see the new pipe until |
||
81 | ; initialization is complete, so that lock will be held during next steps |
||
82 | ; (disconnect processing should either not see it at all, or see fully |
||
83 | ; initialized pipe). |
||
84 | cmp [endpoint], eax |
||
85 | jz .zero_endpoint |
||
86 | mov ecx, [ebx+usb_pipe.DeviceData] |
||
87 | mov [edi+usb_pipe.DeviceData], ecx |
||
88 | call mutex_lock |
||
89 | test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED |
||
90 | jz .common |
||
91 | .fail: |
||
92 | ; If disconnect processing has completed, unlock the mutex, free memory |
||
93 | ; allocated in step 2 and return zero. |
||
94 | call mutex_unlock |
||
95 | mov edx, [esi+usb_controller.HardwareFunc] |
||
96 | stdcall [edx+usb_hardware_func.FreeTD], [edi+usb_pipe.LastTD] |
||
97 | .free_and_return0: |
||
98 | mov edx, [esi+usb_controller.HardwareFunc] |
||
99 | stdcall [edx+usb_hardware_func.FreePipe], edi |
||
100 | .return0: |
||
101 | xor eax, eax |
||
102 | jmp .nothing |
||
103 | .zero_endpoint: |
||
104 | mov [edi+usb_pipe.NextSibling], edi |
||
105 | mov [edi+usb_pipe.PrevSibling], edi |
||
106 | .common: |
||
107 | ; 6. Initialize hardware part of pipe structure. |
||
108 | ; 6a. Acquire the corresponding mutex. |
||
109 | lea ecx, [esi+usb_controller.ControlLock] |
||
110 | cmp [type], BULK_PIPE |
||
111 | jb @f ; control pipe |
||
112 | lea ecx, [esi+usb_controller.BulkLock] |
||
113 | jz @f ; bulk pipe |
||
114 | lea ecx, [esi+usb_controller.PeriodicLock] |
||
115 | @@: |
||
116 | call mutex_lock |
||
117 | ; 6b. Let the controller-specific code do its job. |
||
118 | push ecx |
||
119 | mov edx, [esi+usb_controller.HardwareFunc] |
||
120 | mov eax, [edi+usb_pipe.LastTD] |
||
121 | mov ecx, [config_pipe] |
||
122 | call [edx+usb_hardware_func.InitPipe] |
||
123 | pop ecx |
||
124 | ; 6c. Release the mutex. |
||
125 | push eax |
||
126 | call mutex_unlock |
||
127 | pop eax |
||
128 | ; 6d. If controller-specific code indicates failure, |
||
129 | ; release the lock taken in step 5, free memory allocated in step 2 |
||
130 | ; and return zero. |
||
131 | test eax, eax |
||
132 | jz .fail |
||
133 | ; 7. The pipe is initialized. If this is not the first pipe for the device, |
||
134 | ; insert it to the tail of pipe list for the device, |
||
135 | ; increment number of pipes, |
||
136 | ; release the lock taken at step 5. |
||
137 | mov ecx, [edi+usb_pipe.DeviceData] |
||
138 | test ecx, ecx |
||
139 | jz @f |
||
140 | mov eax, [ebx+usb_pipe.PrevSibling] |
||
141 | mov [edi+usb_pipe.NextSibling], ebx |
||
142 | mov [edi+usb_pipe.PrevSibling], eax |
||
143 | mov [ebx+usb_pipe.PrevSibling], edi |
||
144 | mov [eax+usb_pipe.NextSibling], edi |
||
145 | inc [ecx+usb_device_data.NumPipes] |
||
146 | call mutex_unlock |
||
147 | @@: |
||
148 | ; 8. Return pointer to usb_pipe. |
||
149 | mov eax, edi |
||
150 | .nothing: |
||
151 | ret |
||
152 | endp |
||
153 | |||
154 | ; This procedure is called several times during initial device configuration, |
||
155 | ; when usb_device_data structure is reallocated. |
||
156 | ; It (re)initializes all pointers in usb_device_data. |
||
157 | ; ebx -> usb_pipe |
||
158 | proc usb_reinit_pipe_list |
||
159 | push eax |
||
160 | ; 1. (Re)initialize the lock guarding pipe list. |
||
161 | mov ecx, [ebx+usb_pipe.DeviceData] |
||
162 | call mutex_init |
||
163 | ; 2. Initialize list of opened pipes: two entries, the head and ebx. |
||
164 | add ecx, usb_device_data.OpenedPipeList - usb_pipe.NextSibling |
||
165 | mov [ecx+usb_pipe.NextSibling], ebx |
||
166 | mov [ecx+usb_pipe.PrevSibling], ebx |
||
167 | mov [ebx+usb_pipe.NextSibling], ecx |
||
168 | mov [ebx+usb_pipe.PrevSibling], ecx |
||
169 | ; 3. Initialize list of closed pipes: empty list, only the head is present. |
||
170 | add ecx, usb_device_data.ClosedPipeList - usb_device_data.OpenedPipeList |
||
171 | mov [ecx+usb_pipe.NextSibling], ecx |
||
172 | mov [ecx+usb_pipe.PrevSibling], ecx |
||
173 | pop eax |
||
174 | ret |
||
175 | endp |
||
176 | |||
177 | ; Part of API for drivers, see documentation for USBClosePipe. |
||
178 | proc usb_close_pipe |
||
179 | push ebx esi ; save used registers to be stdcall |
||
180 | virtual at esp |
||
181 | rd 2 ; saved registers |
||
182 | dd ? ; return address |
||
183 | .pipe dd ? |
||
184 | end virtual |
||
185 | ; 1. Lock the pipe list for the device. |
||
186 | mov ebx, [.pipe] |
||
187 | mov esi, [ebx+usb_pipe.Controller] |
||
188 | mov ecx, [ebx+usb_pipe.DeviceData] |
||
189 | call mutex_lock |
||
190 | ; 2. Set the flag "the driver has abandoned this pipe, free it at any time". |
||
191 | lea ecx, [ebx+usb_pipe.Lock] |
||
192 | call mutex_lock |
||
193 | or [ebx+usb_pipe.Flags], USB_FLAG_CAN_FREE |
||
194 | call mutex_unlock |
||
195 | ; 3. Call the worker function. |
||
196 | call usb_close_pipe_nolock |
||
197 | ; 4. Unlock the pipe list for the device. |
||
198 | mov ecx, [ebx+usb_pipe.DeviceData] |
||
199 | call mutex_unlock |
||
200 | ; 5. Wakeup the USB thread so that it can proceed with releasing that pipe. |
||
201 | push edi |
||
202 | call usb_wakeup |
||
203 | pop edi |
||
204 | ; 6. Return. |
||
205 | pop esi ebx ; restore used registers to be stdcall |
||
206 | retn 4 |
||
207 | endp |
||
208 | |||
209 | ; Worker function for pipe closing. Called by usb_close_pipe API and |
||
210 | ; from disconnect processing. |
||
211 | ; The lock guarding pipe list for the device should be held by the caller. |
||
212 | ; ebx -> usb_pipe, esi -> usb_controller |
||
213 | proc usb_close_pipe_nolock |
||
214 | ; 1. Set the flag "pipe is closed, ignore new transfers". |
||
215 | ; If it was already set, do nothing. |
||
216 | lea ecx, [ebx+usb_pipe.Lock] |
||
217 | call mutex_lock |
||
218 | bts dword [ebx+usb_pipe.Flags], USB_FLAG_CLOSED_BIT |
||
219 | jc .closed |
||
220 | call mutex_unlock |
||
221 | ; 2. Remove the pipe from the list of opened pipes. |
||
222 | mov eax, [ebx+usb_pipe.NextSibling] |
||
223 | mov edx, [ebx+usb_pipe.PrevSibling] |
||
224 | mov [eax+usb_pipe.PrevSibling], edx |
||
225 | mov [edx+usb_pipe.NextSibling], eax |
||
226 | ; 3. Unlink the pipe from hardware structures. |
||
227 | ; 3a. Acquire the corresponding lock. |
||
228 | lea edx, [esi+usb_controller.WaitPipeListAsync] |
||
229 | lea ecx, [esi+usb_controller.ControlLock] |
||
230 | cmp [ebx+usb_pipe.Type], BULK_PIPE |
||
231 | jb @f ; control pipe |
||
232 | lea ecx, [esi+usb_controller.BulkLock] |
||
233 | jz @f ; bulk pipe |
||
234 | add edx, usb_controller.WaitPipeListPeriodic - usb_controller.WaitPipeListAsync |
||
235 | lea ecx, [esi+usb_controller.PeriodicLock] |
||
236 | @@: |
||
237 | push edx |
||
238 | call mutex_lock |
||
239 | push ecx |
||
240 | ; 3b. Let the controller-specific code do its job. |
||
241 | mov eax, [esi+usb_controller.HardwareFunc] |
||
242 | call [eax+usb_hardware_func.UnlinkPipe] |
||
243 | ; 3c. Release the corresponding lock. |
||
244 | pop ecx |
||
245 | call mutex_unlock |
||
246 | ; 4. Put the pipe into wait queue. |
||
247 | pop edx |
||
248 | cmp [ebx+usb_pipe.NextWait], -1 |
||
249 | jz .insert_new |
||
250 | or [ebx+usb_pipe.Flags], USB_FLAG_EXTRA_WAIT |
||
251 | jmp .inserted |
||
252 | .insert_new: |
||
253 | mov eax, [edx] |
||
254 | mov [ebx+usb_pipe.NextWait], eax |
||
255 | mov [edx], ebx |
||
256 | .inserted: |
||
257 | ; 5. Return. |
||
258 | ret |
||
259 | .closed: |
||
260 | call mutex_unlock |
||
261 | xor eax, eax |
||
262 | ret |
||
263 | endp |
||
264 | |||
265 | ; This procedure is called when a pipe with USB_FLAG_CLOSED is removed from the |
||
266 | ; corresponding wait list. It means that the hardware has fully forgot about it. |
||
267 | ; ebx -> usb_pipe, esi -> usb_controller |
||
268 | proc usb_pipe_closed |
||
269 | push edi |
||
270 | mov edi, [esi+usb_controller.HardwareFunc] |
||
271 | ; 1. Loop over all transfers, calling the driver with USB_STATUS_CLOSED |
||
272 | ; and freeing all descriptors. |
||
273 | mov edx, [ebx+usb_pipe.LastTD] |
||
274 | test edx, edx |
||
275 | jz .no_transfer |
||
276 | mov edx, [edx+usb_gtd.NextVirt] |
||
277 | .transfer_loop: |
||
278 | cmp edx, [ebx+usb_pipe.LastTD] |
||
279 | jz .transfer_done |
||
280 | mov ecx, [edx+usb_gtd.Callback] |
||
281 | test ecx, ecx |
||
282 | jz .no_callback |
||
283 | push edx |
||
284 | stdcall_verify ecx, ebx, USB_STATUS_CLOSED, \ |
||
285 | [edx+usb_gtd.Buffer], 0, [edx+usb_gtd.UserData] |
||
286 | pop edx |
||
287 | .no_callback: |
||
288 | push [edx+usb_gtd.NextVirt] |
||
289 | stdcall [edi+usb_hardware_func.FreeTD], edx |
||
290 | pop edx |
||
291 | jmp .transfer_loop |
||
292 | .transfer_done: |
||
293 | stdcall [edi+usb_hardware_func.FreeTD], edx |
||
294 | .no_transfer: |
||
295 | ; 2. Decrement number of pipes for the device. |
||
296 | ; If this pipe is the last pipe, go to 5. |
||
297 | mov ecx, [ebx+usb_pipe.DeviceData] |
||
298 | call mutex_lock |
||
299 | dec [ecx+usb_device_data.NumPipes] |
||
300 | jz .last_pipe |
||
301 | call mutex_unlock |
||
302 | ; 3. If the flag "the driver has abandoned this pipe" is set, |
||
303 | ; free memory and return. |
||
304 | test [ebx+usb_pipe.Flags], USB_FLAG_CAN_FREE |
||
305 | jz .nofree |
||
306 | stdcall [edi+usb_hardware_func.FreePipe], ebx |
||
307 | pop edi |
||
308 | ret |
||
309 | ; 4. Otherwise, add it to the list of closed pipes and return. |
||
310 | .nofree: |
||
311 | add ecx, usb_device_data.ClosedPipeList - usb_pipe.NextSibling |
||
312 | mov edx, [ecx+usb_pipe.PrevSibling] |
||
313 | mov [ebx+usb_pipe.NextSibling], ecx |
||
314 | mov [ebx+usb_pipe.PrevSibling], edx |
||
315 | mov [ecx+usb_pipe.PrevSibling], ebx |
||
316 | mov [edx+usb_pipe.NextSibling], ebx |
||
317 | pop edi |
||
318 | ret |
||
319 | .last_pipe: |
||
320 | ; That was the last pipe for the device. |
||
321 | ; 5. Notify device driver(s) about disconnect. |
||
322 | call mutex_unlock |
||
3908 | Serge | 323 | mov eax, [ecx+usb_device_data.NumInterfaces] |
3555 | Serge | 324 | test eax, eax |
325 | jz .notify_done |
||
326 | add ecx, [ecx+usb_device_data.Interfaces] |
||
327 | .notify_loop: |
||
328 | mov edx, [ecx+usb_interface_data.DriverFunc] |
||
329 | test edx, edx |
||
330 | jz @f |
||
331 | mov edx, [edx+USBSRV.usb_func] |
||
332 | cmp [edx+USBFUNC.strucsize], USBFUNC.device_disconnect + 4 |
||
333 | jb @f |
||
334 | mov edx, [edx+USBFUNC.device_disconnect] |
||
335 | test edx, edx |
||
336 | jz @f |
||
337 | push eax ecx |
||
338 | stdcall_verify edx, [ecx+usb_interface_data.DriverData] |
||
339 | pop ecx eax |
||
340 | @@: |
||
341 | add ecx, sizeof.usb_interface_data |
||
342 | dec eax |
||
343 | jnz .notify_loop |
||
344 | .notify_done: |
||
345 | ; 6. Bus address, if assigned, can now be reused. |
||
346 | call [edi+usb_hardware_func.GetDeviceAddress] |
||
347 | test eax, eax |
||
348 | jz @f |
||
349 | bts [esi+usb_controller.ExistingAddresses], eax |
||
350 | @@: |
||
351 | dbgstr 'USB device disconnected' |
||
352 | ; 7. All drivers have returned from disconnect callback, |
||
353 | ; so all drivers should not use any device-related pipes. |
||
354 | ; Free the remaining pipes. |
||
355 | mov eax, [ebx+usb_pipe.DeviceData] |
||
356 | add eax, usb_device_data.ClosedPipeList - usb_pipe.NextSibling |
||
357 | push eax |
||
358 | mov eax, [eax+usb_pipe.NextSibling] |
||
359 | .free_loop: |
||
360 | cmp eax, [esp] |
||
361 | jz .free_done |
||
362 | push [eax+usb_pipe.NextSibling] |
||
363 | stdcall [edi+usb_hardware_func.FreePipe], eax |
||
364 | pop eax |
||
365 | jmp .free_loop |
||
366 | .free_done: |
||
367 | stdcall [edi+usb_hardware_func.FreePipe], ebx |
||
368 | pop eax |
||
369 | ; 8. Free the usb_device_data structure. |
||
370 | sub eax, usb_device_data.ClosedPipeList - usb_pipe.NextSibling |
||
371 | call free |
||
372 | ; 9. Return. |
||
373 | .nothing: |
||
374 | pop edi |
||
375 | ret |
||
376 | endp |
||
377 | |||
378 | ; Part of API for drivers, see documentation for USBNormalTransferAsync. |
||
379 | proc usb_normal_transfer_async stdcall uses ebx edi,\ |
||
380 | pipe:dword, buffer:dword, size:dword, callback:dword, calldata:dword, flags:dword |
||
381 | ; 1. Sanity check: callback must be nonzero. |
||
382 | ; (It is important for other parts of code.) |
||
383 | xor eax, eax |
||
384 | cmp [callback], eax |
||
385 | jz .nothing |
||
386 | ; 2. Lock the transfer queue. |
||
387 | mov ebx, [pipe] |
||
388 | lea ecx, [ebx+usb_pipe.Lock] |
||
389 | call mutex_lock |
||
390 | ; 3. If the pipe has already been closed (presumably due to device disconnect), |
||
391 | ; release the lock taken in step 2 and return zero. |
||
392 | xor eax, eax |
||
393 | test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED |
||
394 | jnz .unlock |
||
395 | ; 4. Allocate and initialize TDs for the transfer. |
||
396 | mov edx, [ebx+usb_pipe.Controller] |
||
397 | mov edi, [edx+usb_controller.HardwareFunc] |
||
398 | stdcall [edi+usb_hardware_func.AllocTransfer], [buffer], [size], [flags], [ebx+usb_pipe.LastTD], 0 |
||
399 | ; If failed, release the lock taken in step 2 and return zero. |
||
400 | test eax, eax |
||
401 | jz .unlock |
||
402 | ; 5. Store callback and its parameters in the last descriptor for this transfer. |
||
403 | mov ecx, [eax+usb_gtd.PrevVirt] |
||
404 | mov edx, [callback] |
||
405 | mov [ecx+usb_gtd.Callback], edx |
||
406 | mov edx, [calldata] |
||
407 | mov [ecx+usb_gtd.UserData], edx |
||
408 | mov edx, [buffer] |
||
409 | mov [ecx+usb_gtd.Buffer], edx |
||
410 | ; 6. Advance LastTD pointer and activate transfer. |
||
411 | push [ebx+usb_pipe.LastTD] |
||
412 | mov [ebx+usb_pipe.LastTD], eax |
||
413 | call [edi+usb_hardware_func.InsertTransfer] |
||
414 | pop eax |
||
415 | ; 7. Release the lock taken in step 2 and |
||
416 | ; return pointer to the first descriptor for the new transfer. |
||
417 | .unlock: |
||
418 | push eax |
||
419 | lea ecx, [ebx+usb_pipe.Lock] |
||
420 | call mutex_unlock |
||
421 | pop eax |
||
422 | .nothing: |
||
423 | ret |
||
424 | endp |
||
425 | |||
426 | ; Part of API for drivers, see documentation for USBControlTransferAsync. |
||
427 | proc usb_control_async stdcall uses ebx edi,\ |
||
428 | pipe:dword, config:dword, buffer:dword, size:dword, callback:dword, calldata:dword, flags:dword |
||
429 | locals |
||
430 | last_td dd ? |
||
431 | endl |
||
432 | ; 1. Sanity check: callback must be nonzero. |
||
433 | ; (It is important for other parts of code.) |
||
434 | cmp [callback], 0 |
||
435 | jz .return0 |
||
436 | ; 2. Lock the transfer queue. |
||
437 | mov ebx, [pipe] |
||
438 | lea ecx, [ebx+usb_pipe.Lock] |
||
439 | call mutex_lock |
||
440 | ; 3. If the pipe has already been closed (presumably due to device disconnect), |
||
441 | ; release the lock taken in step 2 and return zero. |
||
442 | test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED |
||
443 | jnz .unlock_return0 |
||
444 | ; A control transfer contains two or three stages: |
||
445 | ; Setup stage, optional Data stage, Status stage. |
||
446 | ; 4. Allocate and initialize TDs for the Setup stage. |
||
447 | ; Payload is 8 bytes from [config]. |
||
448 | mov edx, [ebx+usb_pipe.Controller] |
||
449 | mov edi, [edx+usb_controller.HardwareFunc] |
||
450 | stdcall [edi+usb_hardware_func.AllocTransfer], [config], 8, 0, [ebx+usb_pipe.LastTD], (2 shl 2) + 0 |
||
451 | ; short transfer is an error, direction is DATA0, token is SETUP |
||
452 | mov [last_td], eax |
||
453 | test eax, eax |
||
454 | jz .fail |
||
455 | ; 5. Allocate and initialize TDs for the Data stage, if [size] is nonzero. |
||
456 | ; Payload is [size] bytes from [buffer]. |
||
457 | mov edx, [config] |
||
458 | mov ecx, (3 shl 2) + 1 ; DATA1, token is OUT |
||
459 | cmp byte [edx], 0 |
||
460 | jns @f |
||
461 | cmp [size], 0 |
||
462 | jz @f |
||
463 | inc ecx ; token is IN |
||
464 | @@: |
||
465 | cmp [size], 0 |
||
466 | jz .nodata |
||
467 | push ecx |
||
468 | stdcall [edi+usb_hardware_func.AllocTransfer], [buffer], [size], [flags], eax, ecx |
||
469 | pop ecx |
||
470 | test eax, eax |
||
471 | jz .fail |
||
472 | mov [last_td], eax |
||
473 | .nodata: |
||
474 | ; 6. Allocate and initialize TDs for the Status stage. |
||
475 | ; No payload. |
||
476 | xor ecx, 3 ; IN becomes OUT, OUT becomes IN |
||
477 | stdcall [edi+usb_hardware_func.AllocTransfer], 0, 0, 0, eax, ecx |
||
478 | test eax, eax |
||
479 | jz .fail |
||
480 | ; 7. Store callback and its parameters in the last descriptor for this transfer. |
||
481 | mov ecx, [eax+usb_gtd.PrevVirt] |
||
482 | mov edx, [callback] |
||
483 | mov [ecx+usb_gtd.Callback], edx |
||
484 | mov edx, [calldata] |
||
485 | mov [ecx+usb_gtd.UserData], edx |
||
486 | mov edx, [buffer] |
||
487 | mov [ecx+usb_gtd.Buffer], edx |
||
488 | ; 8. Advance LastTD pointer and activate transfer. |
||
489 | push [ebx+usb_pipe.LastTD] |
||
490 | mov [ebx+usb_pipe.LastTD], eax |
||
491 | call [edi+usb_hardware_func.InsertTransfer] |
||
492 | ; 9. Release the lock taken in step 2 and |
||
493 | ; return pointer to the first descriptor for the new transfer. |
||
494 | lea ecx, [ebx+usb_pipe.Lock] |
||
495 | call mutex_unlock |
||
496 | pop eax |
||
497 | ret |
||
498 | .fail: |
||
499 | mov eax, [last_td] |
||
500 | test eax, eax |
||
501 | jz .unlock_return0 |
||
502 | stdcall usb_undo_tds, [ebx+usb_pipe.LastTD] |
||
503 | .unlock_return0: |
||
504 | lea ecx, [ebx+usb_pipe.Lock] |
||
505 | call mutex_unlock |
||
506 | .return0: |
||
507 | xor eax, eax |
||
508 | ret |
||
509 | endp |
||
510 | |||
3908 | Serge | 511 | ; Part of API for drivers, see documentation for USBGetParam. |
512 | proc usb_get_param |
||
513 | virtual at esp |
||
514 | dd ? ; return address |
||
515 | .pipe dd ? |
||
516 | .param dd ? |
||
517 | end virtual |
||
518 | mov edx, [.param] |
||
519 | mov ecx, [.pipe] |
||
520 | mov eax, [ecx+usb_pipe.DeviceData] |
||
521 | test edx, edx |
||
522 | jz .get_device_descriptor |
||
523 | dec edx |
||
524 | jz .get_config_descriptor |
||
525 | dec edx |
||
526 | jz .get_speed |
||
527 | or eax, -1 |
||
528 | ret 8 |
||
529 | .get_device_descriptor: |
||
530 | add eax, usb_device_data.DeviceDescriptor |
||
531 | ret 8 |
||
532 | .get_config_descriptor: |
||
533 | movzx ecx, [eax+usb_device_data.DeviceDescrSize] |
||
534 | lea eax, [eax+ecx+usb_device_data.DeviceDescriptor] |
||
535 | ret 8 |
||
536 | .get_speed: |
||
537 | movzx eax, [eax+usb_device_data.Speed] |
||
538 | ret 8 |
||
539 | endp |
||
540 | |||
3555 | Serge | 541 | ; Initialize software part of usb_gtd. Called from controller-specific code |
542 | ; somewhere in AllocTransfer with eax -> next (inactive) usb_gtd, |
||
543 | ; ebx -> usb_pipe, ebp frame from call to AllocTransfer with [.td] -> |
||
544 | ; current (initializing) usb_gtd. |
||
545 | ; Returns ecx = [.td]. |
||
546 | proc usb_init_transfer |
||
547 | virtual at ebp-4 |
||
548 | .Size dd ? |
||
549 | rd 2 |
||
550 | .Buffer dd ? |
||
551 | dd ? |
||
552 | .Flags dd ? |
||
553 | .td dd ? |
||
554 | end virtual |
||
555 | mov [eax+usb_gtd.Pipe], ebx |
||
556 | mov ecx, [.td] |
||
557 | mov [eax+usb_gtd.PrevVirt], ecx |
||
558 | mov edx, [ecx+usb_gtd.NextVirt] |
||
559 | mov [ecx+usb_gtd.NextVirt], eax |
||
560 | mov [eax+usb_gtd.NextVirt], edx |
||
561 | mov [edx+usb_gtd.PrevVirt], eax |
||
562 | mov edx, [.Size] |
||
563 | mov [ecx+usb_gtd.Length], edx |
||
564 | xor edx, edx |
||
565 | mov [ecx+usb_gtd.Callback], edx |
||
566 | mov [ecx+usb_gtd.UserData], edx |
||
567 | ret |
||
568 | endp |
||
569 | |||
570 | ; Free all TDs for the current transfer if something has failed |
||
571 | ; during initialization (e.g. no memory for the next TD). |
||
572 | ; Stdcall with one stack argument = first TD for the transfer |
||
573 | ; and eax = last initialized TD for the transfer. |
||
574 | proc usb_undo_tds |
||
575 | push [eax+usb_gtd.NextVirt] |
||
576 | @@: |
||
577 | cmp eax, [esp+8] |
||
578 | jz @f |
||
579 | push [eax+usb_gtd.PrevVirt] |
||
580 | stdcall [edi+usb_hardware_func.FreeTD], eax |
||
581 | pop eax |
||
582 | jmp @b |
||
583 | @@: |
||
584 | pop ecx |
||
585 | mov [eax+usb_gtd.NextVirt], ecx |
||
586 | mov [ecx+usb_gtd.PrevVirt], eax |
||
587 | ret 4 |
||
588 | endp |
||
589 | |||
590 | ; Helper procedure for handling short packets in controller-specific code. |
||
591 | ; Returns with CF cleared if this is the final packet in some stage: |
||
592 | ; for control transfers that means one of Data and Status stages, |
||
593 | ; for other transfers - the final packet in the only stage. |
||
594 | proc usb_is_final_packet |
||
595 | cmp [ebx+usb_gtd.Callback], 0 |
||
596 | jnz .nothing |
||
597 | mov eax, [ebx+usb_gtd.NextVirt] |
||
598 | cmp [eax+usb_gtd.Callback], 0 |
||
599 | jz .stc |
||
600 | mov eax, [ebx+usb_gtd.Pipe] |
||
601 | cmp [eax+usb_pipe.Type], CONTROL_PIPE |
||
602 | jz .nothing |
||
603 | .stc: |
||
604 | stc |
||
605 | .nothing: |
||
606 | ret |
||
607 | endp |
||
608 | |||
609 | ; Helper procedure for controller-specific code: |
||
610 | ; removes one TD from the transfer queue, ebx -> usb_gtd to remove. |
||
611 | proc usb_unlink_td |
||
612 | mov ecx, [ebx+usb_gtd.Pipe] |
||
613 | add ecx, usb_pipe.Lock |
||
614 | call mutex_lock |
||
615 | mov eax, [ebx+usb_gtd.PrevVirt] |
||
616 | mov edx, [ebx+usb_gtd.NextVirt] |
||
617 | mov [edx+usb_gtd.PrevVirt], eax |
||
618 | mov [eax+usb_gtd.NextVirt], edx |
||
619 | call mutex_unlock |
||
620 | ret |
||
621 | endp |
||
622 | |||
4423 | Serge | 623 | ; One part of transfer is completed, run the associated callback |
624 | ; or update total length in the next part of transfer. |
||
625 | ; in: ebx -> usb_gtd, ecx = status, edx = length |
||
626 | proc usb_process_gtd |
||
627 | ; 1. Test whether it is the last descriptor in the transfer |
||
628 | ; <=> it has an associated callback. |
||
629 | mov eax, [ebx+usb_gtd.Callback] |
||
630 | test eax, eax |
||
631 | jz .nocallback |
||
632 | ; 2. It has an associated callback; call it with corresponding parameters. |
||
633 | stdcall_verify eax, [ebx+usb_gtd.Pipe], ecx, \ |
||
634 | [ebx+usb_gtd.Buffer], edx, [ebx+usb_gtd.UserData] |
||
635 | ret |
||
636 | .nocallback: |
||
637 | ; 3. It is an intermediate descriptor. Add its length to the length |
||
638 | ; in the following descriptor. |
||
639 | mov eax, [ebx+usb_gtd.NextVirt] |
||
640 | add [eax+usb_gtd.Length], edx |
||
641 | ret |
||
642 | endp |
||
643 | |||
3555 | Serge | 644 | if USB_STDCALL_VERIFY |
645 | proc verify_regs |
||
646 | virtual at esp |
||
647 | dd ? ; return address |
||
648 | .edi dd ? |
||
649 | .esi dd ? |
||
650 | .ebp dd ? |
||
651 | .esp dd ? |
||
652 | .ebx dd ? |
||
653 | .edx dd ? |
||
654 | .ecx dd ? |
||
655 | .eax dd ? |
||
656 | end virtual |
||
657 | cmp ebx, [.ebx] |
||
658 | jz @f |
||
659 | dbgstr 'ERROR!!! ebx changed' |
||
660 | @@: |
||
661 | cmp esi, [.esi] |
||
662 | jz @f |
||
663 | dbgstr 'ERROR!!! esi changed' |
||
664 | @@: |
||
665 | cmp edi, [.edi] |
||
666 | jz @f |
||
667 | dbgstr 'ERROR!!! edi changed' |
||
668 | @@: |
||
669 | cmp ebp, [.ebp] |
||
670 | jz @f |
||
671 | dbgstr 'ERROR!!! ebp changed' |
||
672 | @@: |
||
673 | ret |
||
674 | endp |
||
675 | end if=> |