Rev 3745 | Rev 4547 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
3520 | clevermous | 1 | ; Functions for USB pipe manipulation: opening/closing, sending data etc. |
2 | ; |
||
3 | ; ============================================================================= |
||
4 | ; ================================= Constants ================================= |
||
5 | ; ============================================================================= |
||
6 | ; USB pipe types |
||
7 | CONTROL_PIPE = 0 |
||
8 | ISOCHRONOUS_PIPE = 1 |
||
9 | BULK_PIPE = 2 |
||
10 | INTERRUPT_PIPE = 3 |
||
11 | |||
12 | ; Status codes for transfer callbacks. |
||
13 | ; Taken from OHCI as most verbose controller in this sense. |
||
14 | USB_STATUS_OK = 0 ; no error |
||
15 | USB_STATUS_CRC = 1 ; CRC error |
||
16 | USB_STATUS_BITSTUFF = 2 ; bit stuffing violation |
||
17 | USB_STATUS_TOGGLE = 3 ; data toggle mismatch |
||
18 | USB_STATUS_STALL = 4 ; device returned STALL |
||
19 | USB_STATUS_NORESPONSE = 5 ; device not responding |
||
20 | USB_STATUS_PIDCHECK = 6 ; invalid PID check bits |
||
21 | USB_STATUS_WRONGPID = 7 ; unexpected PID value |
||
22 | USB_STATUS_OVERRUN = 8 ; too many data from endpoint |
||
23 | USB_STATUS_UNDERRUN = 9 ; too few data from endpoint |
||
24 | USB_STATUS_BUFOVERRUN = 12 ; overflow of internal controller buffer |
||
25 | USB_STATUS_BUFUNDERRUN = 13 ; underflow of internal controller buffer |
||
26 | USB_STATUS_CLOSED = 16 ; pipe closed |
||
27 | ; either explicitly with USBClosePipe |
||
28 | ; or implicitly due to device disconnect |
||
29 | |||
30 | ; flags for usb_pipe.Flags |
||
31 | USB_FLAG_CLOSED = 1 ; pipe is closed, no new transfers |
||
32 | ; pipe is closed, return error instead of submitting any new transfer |
||
33 | USB_FLAG_CAN_FREE = 2 |
||
34 | ; pipe is closed via explicit call to USBClosePipe, so it can be freed without |
||
35 | ; any driver notification; if this flag is not set, then the pipe is closed due |
||
36 | ; to device disconnect, so it must remain valid until return from disconnect |
||
37 | ; callback provided by the driver |
||
38 | USB_FLAG_EXTRA_WAIT = 4 |
||
39 | ; The pipe was in wait list, while another event occured; |
||
40 | ; when the first wait will be done, reinsert the pipe to wait list |
||
41 | USB_FLAG_CLOSED_BIT = 0 ; USB_FLAG_CLOSED = 1 shl USB_FLAG_CLOSED_BIT |
||
42 | |||
43 | ; ============================================================================= |
||
44 | ; ================================ Structures ================================= |
||
45 | ; ============================================================================= |
||
46 | |||
47 | ; Pipe descriptor. |
||
48 | ; * An USB pipe is described by two structures, for hardware and for software. |
||
49 | ; * This is the software part. The hardware part is defined in a driver |
||
50 | ; of the corresponding controller. |
||
51 | ; * The hardware part is located immediately before usb_pipe, |
||
52 | ; both are allocated at once by controller-specific code |
||
53 | ; (it knows the total length, which depends on the hardware part). |
||
54 | struct usb_pipe |
||
55 | Controller dd ? |
||
56 | ; Pointer to usb_controller structure corresponding to this pipe. |
||
57 | ; Must be the first dword after hardware part, see *hci_new_device. |
||
58 | ; |
||
59 | ; Every endpoint is included into one of processing lists: |
||
60 | ; * Bulk list contains all Bulk endpoints. |
||
61 | ; * Control list contains all Control endpoints. |
||
62 | ; * Several Periodic lists serve Interrupt endpoints with different interval. |
||
63 | ; - There are N=2^n "leaf" periodic lists for N ms interval, one is processed |
||
64 | ; in the frames 0,N,2N,..., another is processed in the frames |
||
65 | ; 1,1+N,1+2N,... and so on. The hardware starts processing of periodic |
||
66 | ; endpoints in every frame from the list identified by lower n bits of the |
||
67 | ; frame number; the addresses of these N lists are written to the |
||
68 | ; controller data area during the initialization. |
||
69 | ; - We assume that n=5, N=32 to simplify the code and compact the data. |
||
70 | ; OHCI works in this way. UHCI and EHCI actually have n=10, N=1024, |
||
71 | ; but this is an overkill for interrupt endpoints; the large value of N is |
||
72 | ; useful only for isochronous transfers in UHCI and EHCI. UHCI/EHCI code |
||
73 | ; initializes "leaf" lists k,k+32,k+64,...,k+(1024-32) to the same value, |
||
74 | ; giving essentially N=32. |
||
75 | ; This restriction means that the actual maximum interval of polling any |
||
76 | ; interrupt endpoint is 32ms, which seems to be a reasonable value. |
||
77 | ; - Similarly, there are 16 lists for 16-ms interval, 8 lists for 8-ms |
||
78 | ; interval and so on. Finally, there is one list for 1ms interval. Their |
||
79 | ; addresses are not directly known to the controller. |
||
80 | ; - The hardware serves endpoints following a physical link from the hardware |
||
81 | ; part. |
||
82 | ; - The hardware links are organized as follows. If the list item is not the |
||
83 | ; last, it's hardware link points to the next item. The hardware link of |
||
84 | ; the last item points to the first item of the "next" list. |
||
85 | ; - The "next" list for k-th and (k+M)-th periodic lists for interval 2M ms |
||
86 | ; is the k-th periodic list for interval M ms, M >= 1. In this scheme, |
||
87 | ; if two "previous" lists are served in the frames k,k+2M,k+4M,... |
||
88 | ; and k+M,k+3M,k+5M,... correspondingly, the "next" list is served in |
||
89 | ; the frames k,k+M,k+2M,k+3M,k+4M,k+5M,..., which is exactly what we want. |
||
90 | ; - The links between Periodic, Control, Bulk lists and the processing of |
||
91 | ; Isochronous endpoints are controller-specific. |
||
92 | ; * The head of every processing list is a static entry which does not |
||
93 | ; correspond to any real pipe. It is described by usb_static_ep |
||
94 | ; structure, not usb_pipe. For OHCI and UHCI, sizeof.usb_static_ep plus |
||
95 | ; sizeof hardware part is 20h, the total number of lists is |
||
96 | ; 32+16+8+4+2+1+1+1 = 65, so all these structures fit in one page, |
||
97 | ; leaving space for other data. This is another reason for 32ms limit. |
||
98 | ; * Static endpoint descriptors are kept in *hci_controller structure. |
||
99 | ; * All items in every processing list, including the static head, are |
||
100 | ; organized in a double-linked list using .NextVirt and .PrevVirt fields. |
||
101 | ; * [[item.NextVirt].PrevVirt] = [[item.PrevVirt].NextVirt] for all items. |
||
102 | NextVirt dd ? |
||
103 | ; Next endpoint in the processing list. |
||
104 | ; See also PrevVirt field and the description before NextVirt field. |
||
105 | PrevVirt dd ? |
||
106 | ; Previous endpoint in the processing list. |
||
107 | ; See also NextVirt field and the description before NextVirt field. |
||
108 | ; |
||
109 | ; Every pipe has the associated transfer queue, that is, the double-linked |
||
110 | ; list of Transfer Descriptors aka TD. For Control, Bulk and Interrupt |
||
111 | ; endpoints this list consists of usb_gtd structures |
||
112 | ; (GTD = General Transfer Descriptors), for Isochronous endpoints |
||
113 | ; this list consists of usb_itd structures, which are not developed yet. |
||
114 | ; The pipe needs to know only the last TD; the first TD can be |
||
115 | ; obtained as [[pipe.LastTD].NextVirt]. |
||
116 | LastTD dd ? |
||
117 | ; Last TD in the transfer queue. |
||
118 | ; |
||
119 | ; All opened pipes corresponding to the same physical device are organized in |
||
120 | ; the double-linked list using .NextSibling and .PrevSibling fields. |
||
121 | ; The head of this list is kept in usb_device_data structure (OpenedPipeList). |
||
122 | ; This list is used when the device is disconnected and all pipes for the |
||
123 | ; device should be closed. |
||
124 | ; Also, all pipes closed due to disconnect must remain valid at least until |
||
125 | ; driver-provided disconnect function returns; all should-be-freed-but-not-now |
||
126 | ; pipes for one device are organized in another double-linked list with |
||
127 | ; the head in usb_device_data.ClosedPipeList; this list uses the same link |
||
128 | ; fields, one pipe can never be in both lists. |
||
129 | NextSibling dd ? |
||
130 | ; Next pipe for the physical device. |
||
131 | PrevSibling dd ? |
||
132 | ; Previous pipe for the physical device. |
||
133 | ; |
||
134 | ; When hardware part of pipe is changed, some time is needed before further |
||
135 | ; actions so that hardware reacts on this change. During that time, |
||
136 | ; all changed pipes are organized in single-linked list with the head |
||
137 | ; usb_controller.WaitPipeList* and link field NextWait. |
||
138 | ; Currently there are two possible reasons to change: |
||
139 | ; change of address/packet size in initial configuration, |
||
140 | ; close of the pipe. They are distinguished by USB_FLAG_CLOSED. |
||
141 | NextWait dd ? |
||
142 | Lock MUTEX |
||
143 | ; Mutex that guards operations with transfer queue for this pipe. |
||
144 | Type db ? |
||
145 | ; Type of pipe, one of {CONTROL,ISOCHRONOUS,BULK,INTERRUPT}_PIPE. |
||
146 | Flags db ? |
||
147 | ; Combination of flags, USB_FLAG_*. |
||
148 | rb 2 ; dword alignment |
||
149 | DeviceData dd ? |
||
150 | ; Pointer to usb_device_data, common for all pipes for one device. |
||
151 | ends |
||
152 | |||
153 | ; This structure describes the static head of every list of pipes. |
||
154 | struct usb_static_ep |
||
155 | ; software fields |
||
156 | Bandwidth dd ? |
||
157 | ; valid only for interrupt/isochronous USB1 lists |
||
158 | ; The offsets of the following two fields must be the same in this structure |
||
159 | ; and in usb_pipe. |
||
160 | NextVirt dd ? |
||
161 | PrevVirt dd ? |
||
162 | ends |
||
163 | |||
164 | ; This structure represents one transfer descriptor |
||
165 | ; ('g' stands for "general" as opposed to isochronous usb_itd). |
||
166 | ; Note that one transfer can have several descriptors: |
||
167 | ; a control transfer has three stages. |
||
168 | ; Additionally, every controller has a limit on transfer length with |
||
169 | ; one descriptor (packet size for UHCI, 1K for OHCI, 4K for EHCI), |
||
170 | ; large transfers must be split into individual packets according to that limit. |
||
171 | struct usb_gtd |
||
172 | Callback dd ? |
||
173 | ; Zero for intermediate descriptors, pointer to callback function |
||
174 | ; for final descriptor. See the docs for description of the callback. |
||
175 | UserData dd ? |
||
176 | ; Dword which is passed to Callback as is, not used by USB code itself. |
||
177 | ; Two following fields organize all descriptors for one pipe in |
||
178 | ; the linked list. |
||
179 | NextVirt dd ? |
||
180 | PrevVirt dd ? |
||
181 | Pipe dd ? |
||
182 | ; Pointer to the parent usb_pipe. |
||
183 | Buffer dd ? |
||
184 | ; Pointer to data for this descriptor. |
||
185 | Length dd ? |
||
186 | ; Length of data for this descriptor. |
||
187 | ends |
||
188 | |||
189 | ; ============================================================================= |
||
190 | ; =================================== Code ==================================== |
||
191 | ; ============================================================================= |
||
192 | |||
193 | USB_STDCALL_VERIFY = 1 |
||
194 | macro stdcall_verify [arg] |
||
195 | { |
||
196 | common |
||
197 | if USB_STDCALL_VERIFY |
||
198 | pushad |
||
199 | stdcall arg |
||
200 | call verify_regs |
||
201 | popad |
||
202 | else |
||
203 | stdcall arg |
||
204 | end if |
||
205 | } |
||
206 | |||
207 | ; Initialization of usb_static_ep structure, |
||
208 | ; called from controller-specific initialization; edi -> usb_static_ep |
||
209 | proc usb_init_static_endpoint |
||
210 | mov [edi+usb_static_ep.NextVirt], edi |
||
211 | mov [edi+usb_static_ep.PrevVirt], edi |
||
212 | ret |
||
213 | endp |
||
214 | |||
215 | ; Part of API for drivers, see documentation for USBOpenPipe. |
||
216 | proc usb_open_pipe stdcall uses ebx esi edi,\ |
||
217 | config_pipe:dword, endpoint:dword, maxpacket:dword, type:dword, interval:dword |
||
218 | locals |
||
3826 | clevermous | 219 | tt_vars rd (ehci_select_tt_interrupt_list.local_vars_size + 3) / 4 |
3520 | clevermous | 220 | targetsmask dd ? ; S-Mask for USB2 |
221 | bandwidth dd ? |
||
222 | target dd ? |
||
223 | endl |
||
224 | ; 1. Verify type of pipe: it must be one of *_PIPE constants. |
||
225 | ; Isochronous pipes are not supported yet. |
||
226 | mov eax, [type] |
||
227 | cmp eax, INTERRUPT_PIPE |
||
228 | ja .badtype |
||
229 | cmp al, ISOCHRONOUS_PIPE |
||
230 | jnz .goodtype |
||
231 | .badtype: |
||
232 | dbgstr 'unsupported type of USB pipe' |
||
233 | jmp .return0 |
||
234 | .goodtype: |
||
235 | ; 2. Allocate memory for pipe and transfer queue. |
||
236 | ; Empty transfer queue consists of one inactive TD. |
||
237 | mov ebx, [config_pipe] |
||
238 | mov esi, [ebx+usb_pipe.Controller] |
||
239 | mov edx, [esi+usb_controller.HardwareFunc] |
||
240 | call [edx+usb_hardware_func.AllocPipe] |
||
241 | test eax, eax |
||
242 | jz .nothing |
||
243 | mov edi, eax |
||
244 | mov edx, [esi+usb_controller.HardwareFunc] |
||
245 | call [edx+usb_hardware_func.AllocTD] |
||
246 | test eax, eax |
||
247 | jz .free_and_return0 |
||
248 | ; 3. Initialize transfer queue: pointer to transfer descriptor, |
||
249 | ; pointers in transfer descriptor, queue lock. |
||
250 | mov [edi+usb_pipe.LastTD], eax |
||
251 | mov [eax+usb_gtd.NextVirt], eax |
||
252 | mov [eax+usb_gtd.PrevVirt], eax |
||
253 | mov [eax+usb_gtd.Pipe], edi |
||
254 | lea ecx, [edi+usb_pipe.Lock] |
||
255 | call mutex_init |
||
256 | ; 4. Initialize software part of pipe structure, except device-related fields. |
||
257 | mov al, byte [type] |
||
258 | mov [edi+usb_pipe.Type], al |
||
259 | xor eax, eax |
||
260 | mov [edi+usb_pipe.Flags], al |
||
261 | mov [edi+usb_pipe.DeviceData], eax |
||
262 | mov [edi+usb_pipe.Controller], esi |
||
263 | or [edi+usb_pipe.NextWait], -1 |
||
264 | ; 5. Initialize device-related fields: |
||
265 | ; for zero endpoint, set .NextSibling = .PrevSibling = this; |
||
266 | ; for other endpoins, copy device data, take the lock guarding pipe list |
||
267 | ; for the device and verify that disconnect processing has not yet started |
||
268 | ; for the device. (Since disconnect processing also takes that lock, |
||
269 | ; either it has completed or it will not start until we release the lock.) |
||
270 | ; Note: usb_device_disconnected should not see the new pipe until |
||
271 | ; initialization is complete, so that lock will be held during next steps |
||
272 | ; (disconnect processing should either not see it at all, or see fully |
||
273 | ; initialized pipe). |
||
274 | cmp [endpoint], eax |
||
275 | jz .zero_endpoint |
||
276 | mov ecx, [ebx+usb_pipe.DeviceData] |
||
277 | mov [edi+usb_pipe.DeviceData], ecx |
||
278 | call mutex_lock |
||
279 | test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED |
||
280 | jz .common |
||
281 | .fail: |
||
282 | ; If disconnect processing has completed, unlock the mutex, free memory |
||
283 | ; allocated in step 2 and return zero. |
||
284 | call mutex_unlock |
||
285 | mov edx, [esi+usb_controller.HardwareFunc] |
||
286 | stdcall [edx+usb_hardware_func.FreeTD], [edi+usb_pipe.LastTD] |
||
287 | .free_and_return0: |
||
288 | mov edx, [esi+usb_controller.HardwareFunc] |
||
289 | stdcall [edx+usb_hardware_func.FreePipe], edi |
||
290 | .return0: |
||
291 | xor eax, eax |
||
292 | jmp .nothing |
||
293 | .zero_endpoint: |
||
294 | mov [edi+usb_pipe.NextSibling], edi |
||
295 | mov [edi+usb_pipe.PrevSibling], edi |
||
296 | .common: |
||
297 | ; 6. Initialize hardware part of pipe structure. |
||
298 | ; 6a. Acquire the corresponding mutex. |
||
299 | lea ecx, [esi+usb_controller.ControlLock] |
||
300 | cmp [type], BULK_PIPE |
||
301 | jb @f ; control pipe |
||
302 | lea ecx, [esi+usb_controller.BulkLock] |
||
303 | jz @f ; bulk pipe |
||
304 | lea ecx, [esi+usb_controller.PeriodicLock] |
||
305 | @@: |
||
306 | call mutex_lock |
||
307 | ; 6b. Let the controller-specific code do its job. |
||
308 | push ecx |
||
309 | mov edx, [esi+usb_controller.HardwareFunc] |
||
310 | mov eax, [edi+usb_pipe.LastTD] |
||
311 | mov ecx, [config_pipe] |
||
312 | call [edx+usb_hardware_func.InitPipe] |
||
313 | pop ecx |
||
314 | ; 6c. Release the mutex. |
||
315 | push eax |
||
316 | call mutex_unlock |
||
317 | pop eax |
||
318 | ; 6d. If controller-specific code indicates failure, |
||
319 | ; release the lock taken in step 5, free memory allocated in step 2 |
||
320 | ; and return zero. |
||
321 | test eax, eax |
||
322 | jz .fail |
||
323 | ; 7. The pipe is initialized. If this is not the first pipe for the device, |
||
324 | ; insert it to the tail of pipe list for the device, |
||
325 | ; increment number of pipes, |
||
326 | ; release the lock taken at step 5. |
||
327 | mov ecx, [edi+usb_pipe.DeviceData] |
||
328 | test ecx, ecx |
||
329 | jz @f |
||
330 | mov eax, [ebx+usb_pipe.PrevSibling] |
||
331 | mov [edi+usb_pipe.NextSibling], ebx |
||
332 | mov [edi+usb_pipe.PrevSibling], eax |
||
333 | mov [ebx+usb_pipe.PrevSibling], edi |
||
334 | mov [eax+usb_pipe.NextSibling], edi |
||
335 | inc [ecx+usb_device_data.NumPipes] |
||
336 | call mutex_unlock |
||
337 | @@: |
||
338 | ; 8. Return pointer to usb_pipe. |
||
339 | mov eax, edi |
||
340 | .nothing: |
||
341 | ret |
||
342 | endp |
||
343 | |||
344 | ; This procedure is called several times during initial device configuration, |
||
345 | ; when usb_device_data structure is reallocated. |
||
346 | ; It (re)initializes all pointers in usb_device_data. |
||
347 | ; ebx -> usb_pipe |
||
348 | proc usb_reinit_pipe_list |
||
349 | push eax |
||
350 | ; 1. (Re)initialize the lock guarding pipe list. |
||
351 | mov ecx, [ebx+usb_pipe.DeviceData] |
||
352 | call mutex_init |
||
353 | ; 2. Initialize list of opened pipes: two entries, the head and ebx. |
||
354 | add ecx, usb_device_data.OpenedPipeList - usb_pipe.NextSibling |
||
355 | mov [ecx+usb_pipe.NextSibling], ebx |
||
356 | mov [ecx+usb_pipe.PrevSibling], ebx |
||
357 | mov [ebx+usb_pipe.NextSibling], ecx |
||
358 | mov [ebx+usb_pipe.PrevSibling], ecx |
||
359 | ; 3. Initialize list of closed pipes: empty list, only the head is present. |
||
360 | add ecx, usb_device_data.ClosedPipeList - usb_device_data.OpenedPipeList |
||
361 | mov [ecx+usb_pipe.NextSibling], ecx |
||
362 | mov [ecx+usb_pipe.PrevSibling], ecx |
||
363 | pop eax |
||
364 | ret |
||
365 | endp |
||
366 | |||
367 | ; Part of API for drivers, see documentation for USBClosePipe. |
||
368 | proc usb_close_pipe |
||
369 | push ebx esi ; save used registers to be stdcall |
||
370 | virtual at esp |
||
371 | rd 2 ; saved registers |
||
372 | dd ? ; return address |
||
373 | .pipe dd ? |
||
374 | end virtual |
||
375 | ; 1. Lock the pipe list for the device. |
||
376 | mov ebx, [.pipe] |
||
377 | mov esi, [ebx+usb_pipe.Controller] |
||
378 | mov ecx, [ebx+usb_pipe.DeviceData] |
||
379 | call mutex_lock |
||
380 | ; 2. Set the flag "the driver has abandoned this pipe, free it at any time". |
||
381 | lea ecx, [ebx+usb_pipe.Lock] |
||
382 | call mutex_lock |
||
383 | or [ebx+usb_pipe.Flags], USB_FLAG_CAN_FREE |
||
384 | call mutex_unlock |
||
385 | ; 3. Call the worker function. |
||
386 | call usb_close_pipe_nolock |
||
387 | ; 4. Unlock the pipe list for the device. |
||
388 | mov ecx, [ebx+usb_pipe.DeviceData] |
||
389 | call mutex_unlock |
||
390 | ; 5. Wakeup the USB thread so that it can proceed with releasing that pipe. |
||
391 | push edi |
||
392 | call usb_wakeup |
||
393 | pop edi |
||
394 | ; 6. Return. |
||
395 | pop esi ebx ; restore used registers to be stdcall |
||
396 | retn 4 |
||
397 | endp |
||
398 | |||
399 | ; Worker function for pipe closing. Called by usb_close_pipe API and |
||
400 | ; from disconnect processing. |
||
401 | ; The lock guarding pipe list for the device should be held by the caller. |
||
402 | ; ebx -> usb_pipe, esi -> usb_controller |
||
403 | proc usb_close_pipe_nolock |
||
404 | ; 1. Set the flag "pipe is closed, ignore new transfers". |
||
405 | ; If it was already set, do nothing. |
||
406 | lea ecx, [ebx+usb_pipe.Lock] |
||
407 | call mutex_lock |
||
408 | bts dword [ebx+usb_pipe.Flags], USB_FLAG_CLOSED_BIT |
||
409 | jc .closed |
||
410 | call mutex_unlock |
||
411 | ; 2. Remove the pipe from the list of opened pipes. |
||
412 | mov eax, [ebx+usb_pipe.NextSibling] |
||
413 | mov edx, [ebx+usb_pipe.PrevSibling] |
||
414 | mov [eax+usb_pipe.PrevSibling], edx |
||
415 | mov [edx+usb_pipe.NextSibling], eax |
||
416 | ; 3. Unlink the pipe from hardware structures. |
||
417 | ; 3a. Acquire the corresponding lock. |
||
418 | lea edx, [esi+usb_controller.WaitPipeListAsync] |
||
419 | lea ecx, [esi+usb_controller.ControlLock] |
||
420 | cmp [ebx+usb_pipe.Type], BULK_PIPE |
||
421 | jb @f ; control pipe |
||
422 | lea ecx, [esi+usb_controller.BulkLock] |
||
423 | jz @f ; bulk pipe |
||
424 | add edx, usb_controller.WaitPipeListPeriodic - usb_controller.WaitPipeListAsync |
||
425 | lea ecx, [esi+usb_controller.PeriodicLock] |
||
426 | @@: |
||
427 | push edx |
||
428 | call mutex_lock |
||
429 | push ecx |
||
430 | ; 3b. Let the controller-specific code do its job. |
||
431 | mov eax, [esi+usb_controller.HardwareFunc] |
||
432 | call [eax+usb_hardware_func.UnlinkPipe] |
||
433 | ; 3c. Release the corresponding lock. |
||
434 | pop ecx |
||
435 | call mutex_unlock |
||
436 | ; 4. Put the pipe into wait queue. |
||
437 | pop edx |
||
438 | cmp [ebx+usb_pipe.NextWait], -1 |
||
439 | jz .insert_new |
||
440 | or [ebx+usb_pipe.Flags], USB_FLAG_EXTRA_WAIT |
||
441 | jmp .inserted |
||
442 | .insert_new: |
||
443 | mov eax, [edx] |
||
444 | mov [ebx+usb_pipe.NextWait], eax |
||
445 | mov [edx], ebx |
||
446 | .inserted: |
||
447 | ; 5. Return. |
||
448 | ret |
||
449 | .closed: |
||
450 | call mutex_unlock |
||
451 | xor eax, eax |
||
452 | ret |
||
453 | endp |
||
454 | |||
455 | ; This procedure is called when a pipe with USB_FLAG_CLOSED is removed from the |
||
456 | ; corresponding wait list. It means that the hardware has fully forgot about it. |
||
457 | ; ebx -> usb_pipe, esi -> usb_controller |
||
458 | proc usb_pipe_closed |
||
459 | push edi |
||
460 | mov edi, [esi+usb_controller.HardwareFunc] |
||
461 | ; 1. Loop over all transfers, calling the driver with USB_STATUS_CLOSED |
||
462 | ; and freeing all descriptors. |
||
463 | mov edx, [ebx+usb_pipe.LastTD] |
||
464 | test edx, edx |
||
465 | jz .no_transfer |
||
466 | mov edx, [edx+usb_gtd.NextVirt] |
||
467 | .transfer_loop: |
||
468 | cmp edx, [ebx+usb_pipe.LastTD] |
||
469 | jz .transfer_done |
||
470 | mov ecx, [edx+usb_gtd.Callback] |
||
471 | test ecx, ecx |
||
472 | jz .no_callback |
||
473 | push edx |
||
474 | stdcall_verify ecx, ebx, USB_STATUS_CLOSED, \ |
||
475 | [edx+usb_gtd.Buffer], 0, [edx+usb_gtd.UserData] |
||
476 | pop edx |
||
477 | .no_callback: |
||
478 | push [edx+usb_gtd.NextVirt] |
||
479 | stdcall [edi+usb_hardware_func.FreeTD], edx |
||
480 | pop edx |
||
481 | jmp .transfer_loop |
||
482 | .transfer_done: |
||
483 | stdcall [edi+usb_hardware_func.FreeTD], edx |
||
484 | .no_transfer: |
||
485 | ; 2. Decrement number of pipes for the device. |
||
486 | ; If this pipe is the last pipe, go to 5. |
||
487 | mov ecx, [ebx+usb_pipe.DeviceData] |
||
488 | call mutex_lock |
||
489 | dec [ecx+usb_device_data.NumPipes] |
||
490 | jz .last_pipe |
||
491 | call mutex_unlock |
||
492 | ; 3. If the flag "the driver has abandoned this pipe" is set, |
||
493 | ; free memory and return. |
||
494 | test [ebx+usb_pipe.Flags], USB_FLAG_CAN_FREE |
||
495 | jz .nofree |
||
496 | stdcall [edi+usb_hardware_func.FreePipe], ebx |
||
497 | pop edi |
||
498 | ret |
||
499 | ; 4. Otherwise, add it to the list of closed pipes and return. |
||
500 | .nofree: |
||
501 | add ecx, usb_device_data.ClosedPipeList - usb_pipe.NextSibling |
||
502 | mov edx, [ecx+usb_pipe.PrevSibling] |
||
503 | mov [ebx+usb_pipe.NextSibling], ecx |
||
504 | mov [ebx+usb_pipe.PrevSibling], edx |
||
505 | mov [ecx+usb_pipe.PrevSibling], ebx |
||
506 | mov [edx+usb_pipe.NextSibling], ebx |
||
507 | pop edi |
||
508 | ret |
||
509 | .last_pipe: |
||
510 | ; That was the last pipe for the device. |
||
511 | ; 5. Notify device driver(s) about disconnect. |
||
512 | call mutex_unlock |
||
3826 | clevermous | 513 | mov eax, [ecx+usb_device_data.NumInterfaces] |
3520 | clevermous | 514 | test eax, eax |
515 | jz .notify_done |
||
516 | add ecx, [ecx+usb_device_data.Interfaces] |
||
517 | .notify_loop: |
||
518 | mov edx, [ecx+usb_interface_data.DriverFunc] |
||
519 | test edx, edx |
||
520 | jz @f |
||
521 | mov edx, [edx+USBSRV.usb_func] |
||
522 | cmp [edx+USBFUNC.strucsize], USBFUNC.device_disconnect + 4 |
||
523 | jb @f |
||
524 | mov edx, [edx+USBFUNC.device_disconnect] |
||
525 | test edx, edx |
||
526 | jz @f |
||
527 | push eax ecx |
||
528 | stdcall_verify edx, [ecx+usb_interface_data.DriverData] |
||
529 | pop ecx eax |
||
530 | @@: |
||
531 | add ecx, sizeof.usb_interface_data |
||
532 | dec eax |
||
533 | jnz .notify_loop |
||
534 | .notify_done: |
||
535 | ; 6. Bus address, if assigned, can now be reused. |
||
536 | call [edi+usb_hardware_func.GetDeviceAddress] |
||
537 | test eax, eax |
||
538 | jz @f |
||
539 | bts [esi+usb_controller.ExistingAddresses], eax |
||
540 | @@: |
||
541 | dbgstr 'USB device disconnected' |
||
542 | ; 7. All drivers have returned from disconnect callback, |
||
543 | ; so all drivers should not use any device-related pipes. |
||
544 | ; Free the remaining pipes. |
||
545 | mov eax, [ebx+usb_pipe.DeviceData] |
||
546 | add eax, usb_device_data.ClosedPipeList - usb_pipe.NextSibling |
||
547 | push eax |
||
548 | mov eax, [eax+usb_pipe.NextSibling] |
||
549 | .free_loop: |
||
550 | cmp eax, [esp] |
||
551 | jz .free_done |
||
552 | push [eax+usb_pipe.NextSibling] |
||
553 | stdcall [edi+usb_hardware_func.FreePipe], eax |
||
554 | pop eax |
||
555 | jmp .free_loop |
||
556 | .free_done: |
||
557 | stdcall [edi+usb_hardware_func.FreePipe], ebx |
||
558 | pop eax |
||
559 | ; 8. Free the usb_device_data structure. |
||
560 | sub eax, usb_device_data.ClosedPipeList - usb_pipe.NextSibling |
||
561 | call free |
||
562 | ; 9. Return. |
||
563 | .nothing: |
||
564 | pop edi |
||
565 | ret |
||
566 | endp |
||
567 | |||
568 | ; Part of API for drivers, see documentation for USBNormalTransferAsync. |
||
569 | proc usb_normal_transfer_async stdcall uses ebx edi,\ |
||
570 | pipe:dword, buffer:dword, size:dword, callback:dword, calldata:dword, flags:dword |
||
571 | ; 1. Sanity check: callback must be nonzero. |
||
572 | ; (It is important for other parts of code.) |
||
573 | xor eax, eax |
||
574 | cmp [callback], eax |
||
575 | jz .nothing |
||
576 | ; 2. Lock the transfer queue. |
||
577 | mov ebx, [pipe] |
||
578 | lea ecx, [ebx+usb_pipe.Lock] |
||
579 | call mutex_lock |
||
580 | ; 3. If the pipe has already been closed (presumably due to device disconnect), |
||
581 | ; release the lock taken in step 2 and return zero. |
||
582 | xor eax, eax |
||
583 | test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED |
||
584 | jnz .unlock |
||
585 | ; 4. Allocate and initialize TDs for the transfer. |
||
586 | mov edx, [ebx+usb_pipe.Controller] |
||
587 | mov edi, [edx+usb_controller.HardwareFunc] |
||
588 | stdcall [edi+usb_hardware_func.AllocTransfer], [buffer], [size], [flags], [ebx+usb_pipe.LastTD], 0 |
||
589 | ; If failed, release the lock taken in step 2 and return zero. |
||
590 | test eax, eax |
||
591 | jz .unlock |
||
592 | ; 5. Store callback and its parameters in the last descriptor for this transfer. |
||
593 | mov ecx, [eax+usb_gtd.PrevVirt] |
||
594 | mov edx, [callback] |
||
595 | mov [ecx+usb_gtd.Callback], edx |
||
596 | mov edx, [calldata] |
||
597 | mov [ecx+usb_gtd.UserData], edx |
||
598 | mov edx, [buffer] |
||
599 | mov [ecx+usb_gtd.Buffer], edx |
||
600 | ; 6. Advance LastTD pointer and activate transfer. |
||
601 | push [ebx+usb_pipe.LastTD] |
||
602 | mov [ebx+usb_pipe.LastTD], eax |
||
603 | call [edi+usb_hardware_func.InsertTransfer] |
||
604 | pop eax |
||
605 | ; 7. Release the lock taken in step 2 and |
||
606 | ; return pointer to the first descriptor for the new transfer. |
||
607 | .unlock: |
||
608 | push eax |
||
609 | lea ecx, [ebx+usb_pipe.Lock] |
||
610 | call mutex_unlock |
||
611 | pop eax |
||
612 | .nothing: |
||
613 | ret |
||
614 | endp |
||
615 | |||
616 | ; Part of API for drivers, see documentation for USBControlTransferAsync. |
||
617 | proc usb_control_async stdcall uses ebx edi,\ |
||
618 | pipe:dword, config:dword, buffer:dword, size:dword, callback:dword, calldata:dword, flags:dword |
||
619 | locals |
||
620 | last_td dd ? |
||
621 | endl |
||
622 | ; 1. Sanity check: callback must be nonzero. |
||
623 | ; (It is important for other parts of code.) |
||
624 | cmp [callback], 0 |
||
625 | jz .return0 |
||
626 | ; 2. Lock the transfer queue. |
||
627 | mov ebx, [pipe] |
||
628 | lea ecx, [ebx+usb_pipe.Lock] |
||
629 | call mutex_lock |
||
630 | ; 3. If the pipe has already been closed (presumably due to device disconnect), |
||
631 | ; release the lock taken in step 2 and return zero. |
||
632 | test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED |
||
633 | jnz .unlock_return0 |
||
634 | ; A control transfer contains two or three stages: |
||
635 | ; Setup stage, optional Data stage, Status stage. |
||
636 | ; 4. Allocate and initialize TDs for the Setup stage. |
||
637 | ; Payload is 8 bytes from [config]. |
||
638 | mov edx, [ebx+usb_pipe.Controller] |
||
639 | mov edi, [edx+usb_controller.HardwareFunc] |
||
640 | stdcall [edi+usb_hardware_func.AllocTransfer], [config], 8, 0, [ebx+usb_pipe.LastTD], (2 shl 2) + 0 |
||
641 | ; short transfer is an error, direction is DATA0, token is SETUP |
||
642 | mov [last_td], eax |
||
643 | test eax, eax |
||
644 | jz .fail |
||
645 | ; 5. Allocate and initialize TDs for the Data stage, if [size] is nonzero. |
||
646 | ; Payload is [size] bytes from [buffer]. |
||
647 | mov edx, [config] |
||
648 | mov ecx, (3 shl 2) + 1 ; DATA1, token is OUT |
||
649 | cmp byte [edx], 0 |
||
650 | jns @f |
||
651 | cmp [size], 0 |
||
652 | jz @f |
||
653 | inc ecx ; token is IN |
||
654 | @@: |
||
655 | cmp [size], 0 |
||
656 | jz .nodata |
||
657 | push ecx |
||
658 | stdcall [edi+usb_hardware_func.AllocTransfer], [buffer], [size], [flags], eax, ecx |
||
659 | pop ecx |
||
660 | test eax, eax |
||
661 | jz .fail |
||
662 | mov [last_td], eax |
||
663 | .nodata: |
||
664 | ; 6. Allocate and initialize TDs for the Status stage. |
||
665 | ; No payload. |
||
666 | xor ecx, 3 ; IN becomes OUT, OUT becomes IN |
||
667 | stdcall [edi+usb_hardware_func.AllocTransfer], 0, 0, 0, eax, ecx |
||
668 | test eax, eax |
||
669 | jz .fail |
||
670 | ; 7. Store callback and its parameters in the last descriptor for this transfer. |
||
671 | mov ecx, [eax+usb_gtd.PrevVirt] |
||
672 | mov edx, [callback] |
||
673 | mov [ecx+usb_gtd.Callback], edx |
||
674 | mov edx, [calldata] |
||
675 | mov [ecx+usb_gtd.UserData], edx |
||
676 | mov edx, [buffer] |
||
677 | mov [ecx+usb_gtd.Buffer], edx |
||
678 | ; 8. Advance LastTD pointer and activate transfer. |
||
679 | push [ebx+usb_pipe.LastTD] |
||
680 | mov [ebx+usb_pipe.LastTD], eax |
||
681 | call [edi+usb_hardware_func.InsertTransfer] |
||
682 | ; 9. Release the lock taken in step 2 and |
||
683 | ; return pointer to the first descriptor for the new transfer. |
||
684 | lea ecx, [ebx+usb_pipe.Lock] |
||
685 | call mutex_unlock |
||
686 | pop eax |
||
687 | ret |
||
688 | .fail: |
||
689 | mov eax, [last_td] |
||
690 | test eax, eax |
||
691 | jz .unlock_return0 |
||
692 | stdcall usb_undo_tds, [ebx+usb_pipe.LastTD] |
||
693 | .unlock_return0: |
||
694 | lea ecx, [ebx+usb_pipe.Lock] |
||
695 | call mutex_unlock |
||
696 | .return0: |
||
697 | xor eax, eax |
||
698 | ret |
||
699 | endp |
||
700 | |||
3745 | clevermous | 701 | ; Part of API for drivers, see documentation for USBGetParam. |
702 | proc usb_get_param |
||
703 | virtual at esp |
||
704 | dd ? ; return address |
||
705 | .pipe dd ? |
||
706 | .param dd ? |
||
707 | end virtual |
||
708 | mov edx, [.param] |
||
709 | mov ecx, [.pipe] |
||
710 | mov eax, [ecx+usb_pipe.DeviceData] |
||
711 | test edx, edx |
||
712 | jz .get_device_descriptor |
||
713 | dec edx |
||
714 | jz .get_config_descriptor |
||
715 | dec edx |
||
716 | jz .get_speed |
||
717 | or eax, -1 |
||
718 | ret 8 |
||
719 | .get_device_descriptor: |
||
720 | add eax, usb_device_data.DeviceDescriptor |
||
721 | ret 8 |
||
722 | .get_config_descriptor: |
||
723 | movzx ecx, [eax+usb_device_data.DeviceDescrSize] |
||
724 | lea eax, [eax+ecx+usb_device_data.DeviceDescriptor] |
||
725 | ret 8 |
||
726 | .get_speed: |
||
727 | movzx eax, [eax+usb_device_data.Speed] |
||
728 | ret 8 |
||
729 | endp |
||
730 | |||
3520 | clevermous | 731 | ; Initialize software part of usb_gtd. Called from controller-specific code |
732 | ; somewhere in AllocTransfer with eax -> next (inactive) usb_gtd, |
||
733 | ; ebx -> usb_pipe, ebp frame from call to AllocTransfer with [.td] -> |
||
734 | ; current (initializing) usb_gtd. |
||
735 | ; Returns ecx = [.td]. |
||
736 | proc usb_init_transfer |
||
737 | virtual at ebp-4 |
||
738 | .Size dd ? |
||
739 | rd 2 |
||
740 | .Buffer dd ? |
||
741 | dd ? |
||
742 | .Flags dd ? |
||
743 | .td dd ? |
||
744 | end virtual |
||
745 | mov [eax+usb_gtd.Pipe], ebx |
||
746 | mov ecx, [.td] |
||
747 | mov [eax+usb_gtd.PrevVirt], ecx |
||
748 | mov edx, [ecx+usb_gtd.NextVirt] |
||
749 | mov [ecx+usb_gtd.NextVirt], eax |
||
750 | mov [eax+usb_gtd.NextVirt], edx |
||
751 | mov [edx+usb_gtd.PrevVirt], eax |
||
752 | mov edx, [.Size] |
||
753 | mov [ecx+usb_gtd.Length], edx |
||
754 | xor edx, edx |
||
755 | mov [ecx+usb_gtd.Callback], edx |
||
756 | mov [ecx+usb_gtd.UserData], edx |
||
757 | ret |
||
758 | endp |
||
759 | |||
760 | ; Free all TDs for the current transfer if something has failed |
||
761 | ; during initialization (e.g. no memory for the next TD). |
||
762 | ; Stdcall with one stack argument = first TD for the transfer |
||
763 | ; and eax = last initialized TD for the transfer. |
||
764 | proc usb_undo_tds |
||
765 | push [eax+usb_gtd.NextVirt] |
||
766 | @@: |
||
767 | cmp eax, [esp+8] |
||
768 | jz @f |
||
769 | push [eax+usb_gtd.PrevVirt] |
||
770 | stdcall [edi+usb_hardware_func.FreeTD], eax |
||
771 | pop eax |
||
772 | jmp @b |
||
773 | @@: |
||
774 | pop ecx |
||
775 | mov [eax+usb_gtd.NextVirt], ecx |
||
776 | mov [ecx+usb_gtd.PrevVirt], eax |
||
777 | ret 4 |
||
778 | endp |
||
779 | |||
780 | ; Helper procedure for handling short packets in controller-specific code. |
||
781 | ; Returns with CF cleared if this is the final packet in some stage: |
||
782 | ; for control transfers that means one of Data and Status stages, |
||
783 | ; for other transfers - the final packet in the only stage. |
||
784 | proc usb_is_final_packet |
||
785 | cmp [ebx+usb_gtd.Callback], 0 |
||
786 | jnz .nothing |
||
787 | mov eax, [ebx+usb_gtd.NextVirt] |
||
788 | cmp [eax+usb_gtd.Callback], 0 |
||
789 | jz .stc |
||
790 | mov eax, [ebx+usb_gtd.Pipe] |
||
791 | cmp [eax+usb_pipe.Type], CONTROL_PIPE |
||
792 | jz .nothing |
||
793 | .stc: |
||
794 | stc |
||
795 | .nothing: |
||
796 | ret |
||
797 | endp |
||
798 | |||
799 | ; Helper procedure for controller-specific code: |
||
800 | ; removes one TD from the transfer queue, ebx -> usb_gtd to remove. |
||
801 | proc usb_unlink_td |
||
802 | mov ecx, [ebx+usb_gtd.Pipe] |
||
803 | add ecx, usb_pipe.Lock |
||
804 | call mutex_lock |
||
805 | mov eax, [ebx+usb_gtd.PrevVirt] |
||
806 | mov edx, [ebx+usb_gtd.NextVirt] |
||
807 | mov [edx+usb_gtd.PrevVirt], eax |
||
808 | mov [eax+usb_gtd.NextVirt], edx |
||
809 | call mutex_unlock |
||
810 | ret |
||
811 | endp |
||
812 | |||
813 | if USB_STDCALL_VERIFY |
||
814 | proc verify_regs |
||
815 | virtual at esp |
||
816 | dd ? ; return address |
||
817 | .edi dd ? |
||
818 | .esi dd ? |
||
819 | .ebp dd ? |
||
820 | .esp dd ? |
||
821 | .ebx dd ? |
||
822 | .edx dd ? |
||
823 | .ecx dd ? |
||
824 | .eax dd ? |
||
825 | end virtual |
||
826 | cmp ebx, [.ebx] |
||
827 | jz @f |
||
828 | dbgstr 'ERROR!!! ebx changed' |
||
829 | @@: |
||
830 | cmp esi, [.esi] |
||
831 | jz @f |
||
832 | dbgstr 'ERROR!!! esi changed' |
||
833 | @@: |
||
834 | cmp edi, [.edi] |
||
835 | jz @f |
||
836 | dbgstr 'ERROR!!! edi changed' |
||
837 | @@: |
||
838 | cmp ebp, [.ebp] |
||
839 | jz @f |
||
840 | dbgstr 'ERROR!!! ebp changed' |
||
841 | @@: |
||
842 | ret |
||
843 | endp |
||
844 | end if |