Rev 4850 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 4850 | Rev 5363 | ||
---|---|---|---|
1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
2 | ;; ;; |
2 | ;; ;; |
3 | ;; Copyright (C) KolibriOS team 2013-2014. All rights reserved. ;; |
3 | ;; Copyright (C) KolibriOS team 2013-2015. All rights reserved. ;; |
4 | ;; Distributed under terms of the GNU General Public License ;; |
4 | ;; Distributed under terms of the GNU General Public License ;; |
5 | ;; ;; |
5 | ;; ;; |
6 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
6 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
7 | 7 | ||
8 | $Revision: 4850 $ |
8 | $Revision: 5363 $ |
9 | 9 | ||
10 | 10 | ||
11 | ; Functions for USB pipe manipulation: opening/closing, sending data etc. |
11 | ; Functions for USB pipe manipulation: opening/closing, sending data etc. |
12 | ; |
12 | ; |
13 | USB_STDCALL_VERIFY = 1 |
13 | USB_STDCALL_VERIFY = 1 |
14 | macro stdcall_verify [arg] |
14 | macro stdcall_verify [arg] |
15 | { |
15 | { |
16 | common |
16 | common |
17 | if USB_STDCALL_VERIFY |
17 | if USB_STDCALL_VERIFY |
18 | pushad |
18 | pushad |
19 | stdcall arg |
19 | stdcall arg |
20 | call verify_regs |
20 | call verify_regs |
21 | popad |
21 | popad |
22 | else |
22 | else |
23 | stdcall arg |
23 | stdcall arg |
24 | end if |
24 | end if |
25 | } |
25 | } |
26 | if USB_STDCALL_VERIFY |
26 | if USB_STDCALL_VERIFY |
27 | STDCALL_VERIFY_EXTRA = 20h |
27 | STDCALL_VERIFY_EXTRA = 20h |
28 | else |
28 | else |
29 | STDCALL_VERIFY_EXTRA = 0 |
29 | STDCALL_VERIFY_EXTRA = 0 |
30 | end if |
30 | end if |
31 | 31 | ||
32 | ; Initialization of usb_static_ep structure, |
32 | ; Initialization of usb_static_ep structure, |
33 | ; called from controller-specific initialization; edi -> usb_static_ep |
33 | ; called from controller-specific initialization; edi -> usb_static_ep |
34 | proc usb_init_static_endpoint |
34 | proc usb_init_static_endpoint |
35 | mov [edi+usb_static_ep.NextVirt], edi |
35 | mov [edi+usb_static_ep.NextVirt], edi |
36 | mov [edi+usb_static_ep.PrevVirt], edi |
36 | mov [edi+usb_static_ep.PrevVirt], edi |
37 | ret |
37 | ret |
38 | endp |
38 | endp |
39 | 39 | ||
40 | ; Part of API for drivers, see documentation for USBOpenPipe. |
40 | ; Part of API for drivers, see documentation for USBOpenPipe. |
41 | proc usb_open_pipe stdcall uses ebx esi edi,\ |
41 | proc usb_open_pipe stdcall uses ebx esi edi,\ |
42 | config_pipe:dword, endpoint:dword, maxpacket:dword, type:dword, interval:dword |
42 | config_pipe:dword, endpoint:dword, maxpacket:dword, type:dword, interval:dword |
43 | locals |
43 | locals |
44 | tt_vars rd 24 ; should be enough for ehci_select_tt_interrupt_list |
44 | tt_vars rd 24 ; should be enough for ehci_select_tt_interrupt_list |
45 | targetsmask dd ? ; S-Mask for USB2 |
45 | targetsmask dd ? ; S-Mask for USB2 |
46 | bandwidth dd ? |
46 | bandwidth dd ? |
47 | target dd ? |
47 | target dd ? |
48 | endl |
48 | endl |
49 | ; 1. Verify type of pipe: it must be one of *_PIPE constants. |
49 | ; 1. Verify type of pipe: it must be one of *_PIPE constants. |
50 | ; Isochronous pipes are not supported yet. |
50 | ; Isochronous pipes are not supported yet. |
51 | mov eax, [type] |
51 | mov eax, [type] |
52 | cmp eax, INTERRUPT_PIPE |
52 | cmp eax, INTERRUPT_PIPE |
53 | ja .badtype |
53 | ja .badtype |
54 | cmp al, ISOCHRONOUS_PIPE |
54 | cmp al, ISOCHRONOUS_PIPE |
55 | jnz .goodtype |
55 | jnz .goodtype |
56 | .badtype: |
56 | .badtype: |
57 | dbgstr 'unsupported type of USB pipe' |
57 | dbgstr 'unsupported type of USB pipe' |
58 | jmp .return0 |
58 | jmp .return0 |
59 | .goodtype: |
59 | .goodtype: |
60 | ; 2. Allocate memory for pipe and transfer queue. |
60 | ; 2. Allocate memory for pipe and transfer queue. |
61 | ; Empty transfer queue consists of one inactive TD. |
61 | ; Empty transfer queue consists of one inactive TD. |
62 | mov ebx, [config_pipe] |
62 | mov ebx, [config_pipe] |
63 | mov esi, [ebx+usb_pipe.Controller] |
63 | mov esi, [ebx+usb_pipe.Controller] |
64 | mov edx, [esi+usb_controller.HardwareFunc] |
64 | mov edx, [esi+usb_controller.HardwareFunc] |
65 | call [edx+usb_hardware_func.AllocPipe] |
65 | call [edx+usb_hardware_func.AllocPipe] |
66 | test eax, eax |
66 | test eax, eax |
67 | jz .nothing |
67 | jz .nothing |
68 | mov edi, eax |
68 | mov edi, eax |
69 | mov edx, [esi+usb_controller.HardwareFunc] |
69 | mov edx, [esi+usb_controller.HardwareFunc] |
70 | call [edx+usb_hardware_func.AllocTD] |
70 | call [edx+usb_hardware_func.AllocTD] |
71 | test eax, eax |
71 | test eax, eax |
72 | jz .free_and_return0 |
72 | jz .free_and_return0 |
73 | ; 3. Initialize transfer queue: pointer to transfer descriptor, |
73 | ; 3. Initialize transfer queue: pointer to transfer descriptor, |
74 | ; pointers in transfer descriptor, queue lock. |
74 | ; pointers in transfer descriptor, queue lock. |
75 | mov [edi+usb_pipe.LastTD], eax |
75 | mov [edi+usb_pipe.LastTD], eax |
76 | mov [eax+usb_gtd.NextVirt], eax |
76 | mov [eax+usb_gtd.NextVirt], eax |
77 | mov [eax+usb_gtd.PrevVirt], eax |
77 | mov [eax+usb_gtd.PrevVirt], eax |
78 | mov [eax+usb_gtd.Pipe], edi |
78 | mov [eax+usb_gtd.Pipe], edi |
79 | lea ecx, [edi+usb_pipe.Lock] |
79 | lea ecx, [edi+usb_pipe.Lock] |
80 | call mutex_init |
80 | call mutex_init |
81 | ; 4. Initialize software part of pipe structure, except device-related fields. |
81 | ; 4. Initialize software part of pipe structure, except device-related fields. |
82 | mov al, byte [type] |
82 | mov al, byte [type] |
83 | mov [edi+usb_pipe.Type], al |
83 | mov [edi+usb_pipe.Type], al |
84 | xor eax, eax |
84 | xor eax, eax |
85 | mov [edi+usb_pipe.Flags], al |
85 | mov [edi+usb_pipe.Flags], al |
86 | mov [edi+usb_pipe.DeviceData], eax |
86 | mov [edi+usb_pipe.DeviceData], eax |
87 | mov [edi+usb_pipe.Controller], esi |
87 | mov [edi+usb_pipe.Controller], esi |
88 | or [edi+usb_pipe.NextWait], -1 |
88 | or [edi+usb_pipe.NextWait], -1 |
89 | ; 5. Initialize device-related fields: |
89 | ; 5. Initialize device-related fields: |
90 | ; for zero endpoint, set .NextSibling = .PrevSibling = this; |
90 | ; for zero endpoint, set .NextSibling = .PrevSibling = this; |
91 | ; for other endpoins, copy device data, take the lock guarding pipe list |
91 | ; for other endpoins, copy device data, take the lock guarding pipe list |
92 | ; for the device and verify that disconnect processing has not yet started |
92 | ; for the device and verify that disconnect processing has not yet started |
93 | ; for the device. (Since disconnect processing also takes that lock, |
93 | ; for the device. (Since disconnect processing also takes that lock, |
94 | ; either it has completed or it will not start until we release the lock.) |
94 | ; either it has completed or it will not start until we release the lock.) |
95 | ; Note: usb_device_disconnected should not see the new pipe until |
95 | ; Note: usb_device_disconnected should not see the new pipe until |
96 | ; initialization is complete, so that lock will be held during next steps |
96 | ; initialization is complete, so that lock will be held during next steps |
97 | ; (disconnect processing should either not see it at all, or see fully |
97 | ; (disconnect processing should either not see it at all, or see fully |
98 | ; initialized pipe). |
98 | ; initialized pipe). |
99 | cmp [endpoint], eax |
99 | cmp [endpoint], eax |
100 | jz .zero_endpoint |
100 | jz .zero_endpoint |
101 | mov ecx, [ebx+usb_pipe.DeviceData] |
101 | mov ecx, [ebx+usb_pipe.DeviceData] |
102 | mov [edi+usb_pipe.DeviceData], ecx |
102 | mov [edi+usb_pipe.DeviceData], ecx |
103 | call mutex_lock |
103 | call mutex_lock |
104 | test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED |
104 | test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED |
105 | jz .common |
105 | jz .common |
106 | .fail: |
106 | .fail: |
107 | ; If disconnect processing has completed, unlock the mutex, free memory |
107 | ; If disconnect processing has completed, unlock the mutex, free memory |
108 | ; allocated in step 2 and return zero. |
108 | ; allocated in step 2 and return zero. |
109 | call mutex_unlock |
109 | call mutex_unlock |
110 | mov edx, [esi+usb_controller.HardwareFunc] |
110 | mov edx, [esi+usb_controller.HardwareFunc] |
111 | stdcall [edx+usb_hardware_func.FreeTD], [edi+usb_pipe.LastTD] |
111 | stdcall [edx+usb_hardware_func.FreeTD], [edi+usb_pipe.LastTD] |
112 | .free_and_return0: |
112 | .free_and_return0: |
113 | mov edx, [esi+usb_controller.HardwareFunc] |
113 | mov edx, [esi+usb_controller.HardwareFunc] |
114 | stdcall [edx+usb_hardware_func.FreePipe], edi |
114 | stdcall [edx+usb_hardware_func.FreePipe], edi |
115 | .return0: |
115 | .return0: |
116 | xor eax, eax |
116 | xor eax, eax |
117 | jmp .nothing |
117 | jmp .nothing |
118 | .zero_endpoint: |
118 | .zero_endpoint: |
119 | mov [edi+usb_pipe.NextSibling], edi |
119 | mov [edi+usb_pipe.NextSibling], edi |
120 | mov [edi+usb_pipe.PrevSibling], edi |
120 | mov [edi+usb_pipe.PrevSibling], edi |
121 | .common: |
121 | .common: |
122 | ; 6. Initialize hardware part of pipe structure. |
122 | ; 6. Initialize hardware part of pipe structure. |
123 | ; 6a. Acquire the corresponding mutex. |
123 | ; 6a. Acquire the corresponding mutex. |
124 | lea ecx, [esi+usb_controller.ControlLock] |
124 | lea ecx, [esi+usb_controller.ControlLock] |
125 | cmp [type], BULK_PIPE |
125 | cmp [type], BULK_PIPE |
126 | jb @f ; control pipe |
126 | jb @f ; control pipe |
127 | lea ecx, [esi+usb_controller.BulkLock] |
127 | lea ecx, [esi+usb_controller.BulkLock] |
128 | jz @f ; bulk pipe |
128 | jz @f ; bulk pipe |
129 | lea ecx, [esi+usb_controller.PeriodicLock] |
129 | lea ecx, [esi+usb_controller.PeriodicLock] |
130 | @@: |
130 | @@: |
131 | call mutex_lock |
131 | call mutex_lock |
132 | ; 6b. Let the controller-specific code do its job. |
132 | ; 6b. Let the controller-specific code do its job. |
133 | push ecx |
133 | push ecx |
134 | mov edx, [esi+usb_controller.HardwareFunc] |
134 | mov edx, [esi+usb_controller.HardwareFunc] |
135 | mov eax, [edi+usb_pipe.LastTD] |
135 | mov eax, [edi+usb_pipe.LastTD] |
136 | mov ecx, [config_pipe] |
136 | mov ecx, [config_pipe] |
137 | call [edx+usb_hardware_func.InitPipe] |
137 | call [edx+usb_hardware_func.InitPipe] |
138 | pop ecx |
138 | pop ecx |
139 | ; 6c. Release the mutex. |
139 | ; 6c. Release the mutex. |
140 | push eax |
140 | push eax |
141 | call mutex_unlock |
141 | call mutex_unlock |
142 | pop eax |
142 | pop eax |
143 | ; 6d. If controller-specific code indicates failure, |
143 | ; 6d. If controller-specific code indicates failure, |
144 | ; release the lock taken in step 5, free memory allocated in step 2 |
144 | ; release the lock taken in step 5, free memory allocated in step 2 |
145 | ; and return zero. |
145 | ; and return zero. |
146 | test eax, eax |
146 | test eax, eax |
147 | jz .fail |
147 | jz .fail |
148 | ; 7. The pipe is initialized. If this is not the first pipe for the device, |
148 | ; 7. The pipe is initialized. If this is not the first pipe for the device, |
149 | ; insert it to the tail of pipe list for the device, |
149 | ; insert it to the tail of pipe list for the device, |
150 | ; increment number of pipes, |
150 | ; increment number of pipes, |
151 | ; release the lock taken at step 5. |
151 | ; release the lock taken at step 5. |
152 | mov ecx, [edi+usb_pipe.DeviceData] |
152 | mov ecx, [edi+usb_pipe.DeviceData] |
153 | test ecx, ecx |
153 | test ecx, ecx |
154 | jz @f |
154 | jz @f |
155 | mov eax, [ebx+usb_pipe.PrevSibling] |
155 | mov eax, [ebx+usb_pipe.PrevSibling] |
156 | mov [edi+usb_pipe.NextSibling], ebx |
156 | mov [edi+usb_pipe.NextSibling], ebx |
157 | mov [edi+usb_pipe.PrevSibling], eax |
157 | mov [edi+usb_pipe.PrevSibling], eax |
158 | mov [ebx+usb_pipe.PrevSibling], edi |
158 | mov [ebx+usb_pipe.PrevSibling], edi |
159 | mov [eax+usb_pipe.NextSibling], edi |
159 | mov [eax+usb_pipe.NextSibling], edi |
160 | inc [ecx+usb_device_data.NumPipes] |
160 | inc [ecx+usb_device_data.NumPipes] |
161 | call mutex_unlock |
161 | call mutex_unlock |
162 | @@: |
162 | @@: |
163 | ; 8. Return pointer to usb_pipe. |
163 | ; 8. Return pointer to usb_pipe. |
164 | mov eax, edi |
164 | mov eax, edi |
165 | .nothing: |
165 | .nothing: |
166 | ret |
166 | ret |
167 | endp |
167 | endp |
168 | 168 | ||
169 | ; This procedure is called several times during initial device configuration, |
169 | ; This procedure is called several times during initial device configuration, |
170 | ; when usb_device_data structure is reallocated. |
170 | ; when usb_device_data structure is reallocated. |
171 | ; It (re)initializes all pointers in usb_device_data. |
171 | ; It (re)initializes all pointers in usb_device_data. |
172 | ; ebx -> usb_pipe |
172 | ; ebx -> usb_pipe |
173 | proc usb_reinit_pipe_list |
173 | proc usb_reinit_pipe_list |
174 | push eax |
174 | push eax |
175 | ; 1. (Re)initialize the lock guarding pipe list. |
175 | ; 1. (Re)initialize the lock guarding pipe list. |
176 | mov ecx, [ebx+usb_pipe.DeviceData] |
176 | mov ecx, [ebx+usb_pipe.DeviceData] |
177 | call mutex_init |
177 | call mutex_init |
178 | ; 2. Initialize list of opened pipes: two entries, the head and ebx. |
178 | ; 2. Initialize list of opened pipes: two entries, the head and ebx. |
179 | add ecx, usb_device_data.OpenedPipeList - usb_pipe.NextSibling |
179 | add ecx, usb_device_data.OpenedPipeList - usb_pipe.NextSibling |
180 | mov [ecx+usb_pipe.NextSibling], ebx |
180 | mov [ecx+usb_pipe.NextSibling], ebx |
181 | mov [ecx+usb_pipe.PrevSibling], ebx |
181 | mov [ecx+usb_pipe.PrevSibling], ebx |
182 | mov [ebx+usb_pipe.NextSibling], ecx |
182 | mov [ebx+usb_pipe.NextSibling], ecx |
183 | mov [ebx+usb_pipe.PrevSibling], ecx |
183 | mov [ebx+usb_pipe.PrevSibling], ecx |
184 | ; 3. Initialize list of closed pipes: empty list, only the head is present. |
184 | ; 3. Initialize list of closed pipes: empty list, only the head is present. |
185 | add ecx, usb_device_data.ClosedPipeList - usb_device_data.OpenedPipeList |
185 | add ecx, usb_device_data.ClosedPipeList - usb_device_data.OpenedPipeList |
186 | mov [ecx+usb_pipe.NextSibling], ecx |
186 | mov [ecx+usb_pipe.NextSibling], ecx |
187 | mov [ecx+usb_pipe.PrevSibling], ecx |
187 | mov [ecx+usb_pipe.PrevSibling], ecx |
188 | pop eax |
188 | pop eax |
189 | ret |
189 | ret |
190 | endp |
190 | endp |
191 | 191 | ||
192 | ; Part of API for drivers, see documentation for USBClosePipe. |
192 | ; Part of API for drivers, see documentation for USBClosePipe. |
193 | proc usb_close_pipe |
193 | proc usb_close_pipe |
194 | push ebx esi ; save used registers to be stdcall |
194 | push ebx esi ; save used registers to be stdcall |
195 | virtual at esp |
195 | virtual at esp |
196 | rd 2 ; saved registers |
196 | rd 2 ; saved registers |
197 | dd ? ; return address |
197 | dd ? ; return address |
198 | .pipe dd ? |
198 | .pipe dd ? |
199 | end virtual |
199 | end virtual |
200 | ; 1. Lock the pipe list for the device. |
200 | ; 1. Lock the pipe list for the device. |
201 | mov ebx, [.pipe] |
201 | mov ebx, [.pipe] |
202 | mov esi, [ebx+usb_pipe.Controller] |
202 | mov esi, [ebx+usb_pipe.Controller] |
203 | mov ecx, [ebx+usb_pipe.DeviceData] |
203 | mov ecx, [ebx+usb_pipe.DeviceData] |
204 | call mutex_lock |
204 | call mutex_lock |
205 | ; 2. Set the flag "the driver has abandoned this pipe, free it at any time". |
205 | ; 2. Set the flag "the driver has abandoned this pipe, free it at any time". |
206 | lea ecx, [ebx+usb_pipe.Lock] |
206 | lea ecx, [ebx+usb_pipe.Lock] |
207 | call mutex_lock |
207 | call mutex_lock |
208 | or [ebx+usb_pipe.Flags], USB_FLAG_CAN_FREE |
208 | or [ebx+usb_pipe.Flags], USB_FLAG_CAN_FREE |
209 | call mutex_unlock |
209 | call mutex_unlock |
210 | ; 3. Call the worker function. |
210 | ; 3. Call the worker function. |
211 | call usb_close_pipe_nolock |
211 | call usb_close_pipe_nolock |
212 | ; 4. Unlock the pipe list for the device. |
212 | ; 4. Unlock the pipe list for the device. |
213 | mov ecx, [ebx+usb_pipe.DeviceData] |
213 | mov ecx, [ebx+usb_pipe.DeviceData] |
214 | call mutex_unlock |
214 | call mutex_unlock |
215 | ; 5. Wakeup the USB thread so that it can proceed with releasing that pipe. |
215 | ; 5. Wakeup the USB thread so that it can proceed with releasing that pipe. |
216 | push edi |
216 | push edi |
217 | call usb_wakeup |
217 | call usb_wakeup |
218 | pop edi |
218 | pop edi |
219 | ; 6. Return. |
219 | ; 6. Return. |
220 | pop esi ebx ; restore used registers to be stdcall |
220 | pop esi ebx ; restore used registers to be stdcall |
221 | retn 4 |
221 | retn 4 |
222 | endp |
222 | endp |
223 | 223 | ||
224 | ; Worker function for pipe closing. Called by usb_close_pipe API and |
224 | ; Worker function for pipe closing. Called by usb_close_pipe API and |
225 | ; from disconnect processing. |
225 | ; from disconnect processing. |
226 | ; The lock guarding pipe list for the device should be held by the caller. |
226 | ; The lock guarding pipe list for the device should be held by the caller. |
227 | ; ebx -> usb_pipe, esi -> usb_controller |
227 | ; ebx -> usb_pipe, esi -> usb_controller |
228 | proc usb_close_pipe_nolock |
228 | proc usb_close_pipe_nolock |
229 | ; 1. Set the flag "pipe is closed, ignore new transfers". |
229 | ; 1. Set the flag "pipe is closed, ignore new transfers". |
230 | ; If it was already set, do nothing. |
230 | ; If it was already set, do nothing. |
231 | lea ecx, [ebx+usb_pipe.Lock] |
231 | lea ecx, [ebx+usb_pipe.Lock] |
232 | call mutex_lock |
232 | call mutex_lock |
233 | bts dword [ebx+usb_pipe.Flags], USB_FLAG_CLOSED_BIT |
233 | bts dword [ebx+usb_pipe.Flags], USB_FLAG_CLOSED_BIT |
234 | jc .closed |
234 | jc .closed |
235 | call mutex_unlock |
235 | call mutex_unlock |
236 | ; 2. Remove the pipe from the list of opened pipes. |
236 | ; 2. Remove the pipe from the list of opened pipes. |
237 | mov eax, [ebx+usb_pipe.NextSibling] |
237 | mov eax, [ebx+usb_pipe.NextSibling] |
238 | mov edx, [ebx+usb_pipe.PrevSibling] |
238 | mov edx, [ebx+usb_pipe.PrevSibling] |
239 | mov [eax+usb_pipe.PrevSibling], edx |
239 | mov [eax+usb_pipe.PrevSibling], edx |
240 | mov [edx+usb_pipe.NextSibling], eax |
240 | mov [edx+usb_pipe.NextSibling], eax |
241 | ; 3. Unlink the pipe from hardware structures. |
241 | ; 3. Unlink the pipe from hardware structures. |
242 | ; 3a. Acquire the corresponding lock. |
242 | ; 3a. Acquire the corresponding lock. |
243 | lea edx, [esi+usb_controller.WaitPipeListAsync] |
243 | lea edx, [esi+usb_controller.WaitPipeListAsync] |
244 | lea ecx, [esi+usb_controller.ControlLock] |
244 | lea ecx, [esi+usb_controller.ControlLock] |
245 | cmp [ebx+usb_pipe.Type], BULK_PIPE |
245 | cmp [ebx+usb_pipe.Type], BULK_PIPE |
246 | jb @f ; control pipe |
246 | jb @f ; control pipe |
247 | lea ecx, [esi+usb_controller.BulkLock] |
247 | lea ecx, [esi+usb_controller.BulkLock] |
248 | jz @f ; bulk pipe |
248 | jz @f ; bulk pipe |
249 | add edx, usb_controller.WaitPipeListPeriodic - usb_controller.WaitPipeListAsync |
249 | add edx, usb_controller.WaitPipeListPeriodic - usb_controller.WaitPipeListAsync |
250 | lea ecx, [esi+usb_controller.PeriodicLock] |
250 | lea ecx, [esi+usb_controller.PeriodicLock] |
251 | @@: |
251 | @@: |
252 | push edx |
252 | push edx |
253 | call mutex_lock |
253 | call mutex_lock |
254 | push ecx |
254 | push ecx |
255 | ; 3b. Let the controller-specific code do its job. |
255 | ; 3b. Let the controller-specific code do its job. |
256 | test [ebx+usb_pipe.Flags], USB_FLAG_DISABLED |
256 | test [ebx+usb_pipe.Flags], USB_FLAG_DISABLED |
257 | jnz @f |
257 | jnz @f |
258 | mov eax, [esi+usb_controller.HardwareFunc] |
258 | mov eax, [esi+usb_controller.HardwareFunc] |
259 | call [eax+usb_hardware_func.DisablePipe] |
259 | call [eax+usb_hardware_func.DisablePipe] |
260 | @@: |
260 | @@: |
261 | mov eax, [esi+usb_controller.HardwareFunc] |
261 | mov eax, [esi+usb_controller.HardwareFunc] |
262 | call [eax+usb_hardware_func.UnlinkPipe] |
262 | call [eax+usb_hardware_func.UnlinkPipe] |
263 | mov edx, [ebx+usb_pipe.NextVirt] |
263 | mov edx, [ebx+usb_pipe.NextVirt] |
264 | mov eax, [ebx+usb_pipe.PrevVirt] |
264 | mov eax, [ebx+usb_pipe.PrevVirt] |
265 | mov [edx+usb_pipe.PrevVirt], eax |
265 | mov [edx+usb_pipe.PrevVirt], eax |
266 | mov [eax+usb_pipe.NextVirt], edx |
266 | mov [eax+usb_pipe.NextVirt], edx |
267 | ; 3c. Release the corresponding lock. |
267 | ; 3c. Release the corresponding lock. |
268 | pop ecx |
268 | pop ecx |
269 | call mutex_unlock |
269 | call mutex_unlock |
270 | ; 4. Put the pipe into wait queue. |
270 | ; 4. Put the pipe into wait queue. |
271 | pop edx |
271 | pop edx |
272 | cmp [ebx+usb_pipe.NextWait], -1 |
272 | cmp [ebx+usb_pipe.NextWait], -1 |
273 | jz .insert_new |
273 | jz .insert_new |
274 | or [ebx+usb_pipe.Flags], USB_FLAG_EXTRA_WAIT |
274 | or [ebx+usb_pipe.Flags], USB_FLAG_EXTRA_WAIT |
275 | jmp .inserted |
275 | jmp .inserted |
276 | .insert_new: |
276 | .insert_new: |
277 | mov eax, [edx] |
277 | mov eax, [edx] |
278 | mov [ebx+usb_pipe.NextWait], eax |
278 | mov [ebx+usb_pipe.NextWait], eax |
279 | mov [edx], ebx |
279 | mov [edx], ebx |
280 | .inserted: |
280 | .inserted: |
281 | ; 5. Return. |
281 | ; 5. Return. |
282 | ret |
282 | ret |
283 | .closed: |
283 | .closed: |
284 | call mutex_unlock |
284 | call mutex_unlock |
285 | xor eax, eax |
285 | xor eax, eax |
286 | ret |
286 | ret |
287 | endp |
287 | endp |
288 | 288 | ||
289 | ; This procedure is called when all transfers are aborted |
289 | ; This procedure is called when all transfers are aborted |
290 | ; either due to call to usb_abort_pipe or due to pipe closing. |
290 | ; either due to call to usb_abort_pipe or due to pipe closing. |
291 | ; It notifies all callbacks and frees all transfer descriptors. |
291 | ; It notifies all callbacks and frees all transfer descriptors. |
292 | ; ebx -> usb_pipe, esi -> usb_controller, edi -> usb_hardware_func |
292 | ; ebx -> usb_pipe, esi -> usb_controller, edi -> usb_hardware_func |
293 | ; three stack parameters: status code for callback functions |
293 | ; three stack parameters: status code for callback functions |
294 | ; and descriptors where to start and stop. |
294 | ; and descriptors where to start and stop. |
295 | proc usb_pipe_aborted |
295 | proc usb_pipe_aborted |
296 | virtual at esp |
296 | virtual at esp |
297 | dd ? ; return address |
297 | dd ? ; return address |
298 | .status dd ? ; USB_STATUS_CLOSED or USB_STATUS_CANCELLED |
298 | .status dd ? ; USB_STATUS_CLOSED or USB_STATUS_CANCELLED |
299 | .first_td dd ? |
299 | .first_td dd ? |
300 | .last_td dd ? |
300 | .last_td dd ? |
301 | end virtual |
301 | end virtual |
302 | ; Loop over all transfers, calling the driver with the given status |
302 | ; Loop over all transfers, calling the driver with the given status |
303 | ; and freeing all descriptors except the last one. |
303 | ; and freeing all descriptors except the last one. |
304 | .loop: |
304 | .loop: |
305 | mov edx, [.first_td] |
305 | mov edx, [.first_td] |
306 | cmp edx, [.last_td] |
306 | cmp edx, [.last_td] |
307 | jz .done |
307 | jz .done |
308 | mov ecx, [edx+usb_gtd.Callback] |
308 | mov ecx, [edx+usb_gtd.Callback] |
309 | test ecx, ecx |
309 | test ecx, ecx |
310 | jz .no_callback |
310 | jz .no_callback |
311 | stdcall_verify ecx, ebx, [.status+12+STDCALL_VERIFY_EXTRA], \ |
311 | stdcall_verify ecx, ebx, [.status+12+STDCALL_VERIFY_EXTRA], \ |
312 | [edx+usb_gtd.Buffer], 0, [edx+usb_gtd.UserData] |
312 | [edx+usb_gtd.Buffer], 0, [edx+usb_gtd.UserData] |
313 | mov edx, [.first_td] |
313 | mov edx, [.first_td] |
314 | .no_callback: |
314 | .no_callback: |
315 | mov eax, [edx+usb_gtd.NextVirt] |
315 | mov eax, [edx+usb_gtd.NextVirt] |
316 | mov [.first_td], eax |
316 | mov [.first_td], eax |
317 | stdcall [edi+usb_hardware_func.FreeTD], edx |
317 | stdcall [edi+usb_hardware_func.FreeTD], edx |
318 | jmp .loop |
318 | jmp .loop |
319 | .done: |
319 | .done: |
320 | ret 12 |
320 | ret 12 |
321 | endp |
321 | endp |
322 | 322 | ||
323 | ; This procedure is called when a pipe with USB_FLAG_CLOSED is removed from the |
323 | ; This procedure is called when a pipe with USB_FLAG_CLOSED is removed from the |
324 | ; corresponding wait list. It means that the hardware has fully forgot about it. |
324 | ; corresponding wait list. It means that the hardware has fully forgot about it. |
325 | ; ebx -> usb_pipe, esi -> usb_controller |
325 | ; ebx -> usb_pipe, esi -> usb_controller |
326 | proc usb_pipe_closed |
326 | proc usb_pipe_closed |
327 | push edi |
327 | push edi |
328 | mov edi, [esi+usb_controller.HardwareFunc] |
328 | mov edi, [esi+usb_controller.HardwareFunc] |
329 | ; 1. Notify all registered callbacks with status USB_STATUS_CLOSED, if any, |
329 | ; 1. Notify all registered callbacks with status USB_STATUS_CLOSED, if any, |
330 | ; and free all transfer descriptors, including the last one. |
330 | ; and free all transfer descriptors, including the last one. |
331 | lea ecx, [ebx+usb_pipe.Lock] |
331 | lea ecx, [ebx+usb_pipe.Lock] |
332 | call mutex_lock |
332 | call mutex_lock |
333 | mov edx, [ebx+usb_pipe.LastTD] |
333 | mov edx, [ebx+usb_pipe.LastTD] |
334 | test edx, edx |
334 | test edx, edx |
335 | jz .no_transfer |
335 | jz .no_transfer |
336 | mov eax, [edx+usb_gtd.NextVirt] |
336 | mov eax, [edx+usb_gtd.NextVirt] |
337 | push edx |
337 | push edx |
338 | push eax |
338 | push eax |
339 | call mutex_unlock |
339 | call mutex_unlock |
340 | push USB_STATUS_CLOSED |
340 | push USB_STATUS_CLOSED |
341 | call usb_pipe_aborted |
341 | call usb_pipe_aborted |
342 | ; It is safe to free LastTD here: |
342 | ; It is safe to free LastTD here: |
343 | ; usb_*_transfer_async do not enqueue new transfers if USB_FLAG_CLOSED is set. |
343 | ; usb_*_transfer_async do not enqueue new transfers if USB_FLAG_CLOSED is set. |
344 | stdcall [edi+usb_hardware_func.FreeTD], [ebx+usb_pipe.LastTD] |
344 | stdcall [edi+usb_hardware_func.FreeTD], [ebx+usb_pipe.LastTD] |
345 | jmp @f |
345 | jmp @f |
346 | .no_transfer: |
346 | .no_transfer: |
347 | call mutex_unlock |
347 | call mutex_unlock |
348 | @@: |
348 | @@: |
349 | ; 2. Decrement number of pipes for the device. |
349 | ; 2. Decrement number of pipes for the device. |
350 | ; If this pipe is the last pipe, go to 5. |
350 | ; If this pipe is the last pipe, go to 5. |
351 | mov ecx, [ebx+usb_pipe.DeviceData] |
351 | mov ecx, [ebx+usb_pipe.DeviceData] |
352 | call mutex_lock |
352 | call mutex_lock |
353 | dec [ecx+usb_device_data.NumPipes] |
353 | dec [ecx+usb_device_data.NumPipes] |
354 | jz .last_pipe |
354 | jz .last_pipe |
355 | call mutex_unlock |
355 | call mutex_unlock |
356 | ; 3. If the flag "the driver has abandoned this pipe" is set, |
356 | ; 3. If the flag "the driver has abandoned this pipe" is set, |
357 | ; free memory and return. |
357 | ; free memory and return. |
358 | test [ebx+usb_pipe.Flags], USB_FLAG_CAN_FREE |
358 | test [ebx+usb_pipe.Flags], USB_FLAG_CAN_FREE |
359 | jz .nofree |
359 | jz .nofree |
360 | stdcall [edi+usb_hardware_func.FreePipe], ebx |
360 | stdcall [edi+usb_hardware_func.FreePipe], ebx |
361 | pop edi |
361 | pop edi |
362 | ret |
362 | ret |
363 | ; 4. Otherwise, add it to the list of closed pipes and return. |
363 | ; 4. Otherwise, add it to the list of closed pipes and return. |
364 | .nofree: |
364 | .nofree: |
365 | add ecx, usb_device_data.ClosedPipeList - usb_pipe.NextSibling |
365 | add ecx, usb_device_data.ClosedPipeList - usb_pipe.NextSibling |
366 | mov edx, [ecx+usb_pipe.PrevSibling] |
366 | mov edx, [ecx+usb_pipe.PrevSibling] |
367 | mov [ebx+usb_pipe.NextSibling], ecx |
367 | mov [ebx+usb_pipe.NextSibling], ecx |
368 | mov [ebx+usb_pipe.PrevSibling], edx |
368 | mov [ebx+usb_pipe.PrevSibling], edx |
369 | mov [ecx+usb_pipe.PrevSibling], ebx |
369 | mov [ecx+usb_pipe.PrevSibling], ebx |
370 | mov [edx+usb_pipe.NextSibling], ebx |
370 | mov [edx+usb_pipe.NextSibling], ebx |
371 | pop edi |
371 | pop edi |
372 | ret |
372 | ret |
373 | .last_pipe: |
373 | .last_pipe: |
374 | ; That was the last pipe for the device. |
374 | ; That was the last pipe for the device. |
375 | ; 5. Notify device driver(s) about disconnect. |
375 | ; 5. Notify device driver(s) about disconnect. |
376 | call mutex_unlock |
376 | call mutex_unlock |
377 | mov eax, [ecx+usb_device_data.NumInterfaces] |
377 | mov eax, [ecx+usb_device_data.NumInterfaces] |
378 | test eax, eax |
378 | test eax, eax |
379 | jz .notify_done |
379 | jz .notify_done |
380 | add ecx, [ecx+usb_device_data.Interfaces] |
380 | add ecx, [ecx+usb_device_data.Interfaces] |
381 | .notify_loop: |
381 | .notify_loop: |
382 | mov edx, [ecx+usb_interface_data.DriverFunc] |
382 | mov edx, [ecx+usb_interface_data.DriverFunc] |
383 | test edx, edx |
383 | test edx, edx |
384 | jz @f |
384 | jz @f |
385 | mov edx, [edx+USBSRV.usb_func] |
385 | mov edx, [edx+USBSRV.usb_func] |
386 | cmp [edx+USBFUNC.strucsize], USBFUNC.device_disconnect + 4 |
386 | cmp [edx+USBFUNC.strucsize], USBFUNC.device_disconnect + 4 |
387 | jb @f |
387 | jb @f |
388 | mov edx, [edx+USBFUNC.device_disconnect] |
388 | mov edx, [edx+USBFUNC.device_disconnect] |
389 | test edx, edx |
389 | test edx, edx |
390 | jz @f |
390 | jz @f |
391 | push eax ecx |
391 | push eax ecx |
392 | stdcall_verify edx, [ecx+usb_interface_data.DriverData] |
392 | stdcall_verify edx, [ecx+usb_interface_data.DriverData] |
393 | pop ecx eax |
393 | pop ecx eax |
394 | @@: |
394 | @@: |
395 | add ecx, sizeof.usb_interface_data |
395 | add ecx, sizeof.usb_interface_data |
396 | dec eax |
396 | dec eax |
397 | jnz .notify_loop |
397 | jnz .notify_loop |
398 | .notify_done: |
398 | .notify_done: |
399 | ; 6. Kill the timer, if active. |
399 | ; 6. Kill the timer, if active. |
400 | ; (Usually not; possible if device is disconnected |
400 | ; (Usually not; possible if device is disconnected |
401 | ; while processing SET_ADDRESS request). |
401 | ; while processing SET_ADDRESS request). |
402 | mov eax, [ebx+usb_pipe.DeviceData] |
402 | mov eax, [ebx+usb_pipe.DeviceData] |
403 | cmp [eax+usb_device_data.Timer], 0 |
403 | cmp [eax+usb_device_data.Timer], 0 |
404 | jz @f |
404 | jz @f |
405 | stdcall cancel_timer_hs, [eax+usb_device_data.Timer] |
405 | stdcall cancel_timer_hs, [eax+usb_device_data.Timer] |
406 | mov [eax+usb_device_data.Timer], 0 |
406 | mov [eax+usb_device_data.Timer], 0 |
407 | @@: |
407 | @@: |
408 | ; 7. Bus address, if assigned, can now be reused. |
408 | ; 7. Bus address, if assigned, can now be reused. |
409 | call [edi+usb_hardware_func.GetDeviceAddress] |
409 | call [edi+usb_hardware_func.GetDeviceAddress] |
410 | test eax, eax |
410 | test eax, eax |
411 | jz @f |
411 | jz @f |
412 | bts [esi+usb_controller.ExistingAddresses], eax |
412 | bts [esi+usb_controller.ExistingAddresses], eax |
413 | @@: |
413 | @@: |
414 | dbgstr 'USB device disconnected' |
414 | dbgstr 'USB device disconnected' |
415 | ; 8. All drivers have returned from disconnect callback, |
415 | ; 8. All drivers have returned from disconnect callback, |
416 | ; so all drivers should not use any device-related pipes. |
416 | ; so all drivers should not use any device-related pipes. |
417 | ; Free the remaining pipes. |
417 | ; Free the remaining pipes. |
418 | mov eax, [ebx+usb_pipe.DeviceData] |
418 | mov eax, [ebx+usb_pipe.DeviceData] |
419 | add eax, usb_device_data.ClosedPipeList - usb_pipe.NextSibling |
419 | add eax, usb_device_data.ClosedPipeList - usb_pipe.NextSibling |
420 | push eax |
420 | push eax |
421 | mov eax, [eax+usb_pipe.NextSibling] |
421 | mov eax, [eax+usb_pipe.NextSibling] |
422 | .free_loop: |
422 | .free_loop: |
423 | cmp eax, [esp] |
423 | cmp eax, [esp] |
424 | jz .free_done |
424 | jz .free_done |
425 | push [eax+usb_pipe.NextSibling] |
425 | push [eax+usb_pipe.NextSibling] |
426 | stdcall [edi+usb_hardware_func.FreePipe], eax |
426 | stdcall [edi+usb_hardware_func.FreePipe], eax |
427 | pop eax |
427 | pop eax |
428 | jmp .free_loop |
428 | jmp .free_loop |
429 | .free_done: |
429 | .free_done: |
430 | stdcall [edi+usb_hardware_func.FreePipe], ebx |
430 | stdcall [edi+usb_hardware_func.FreePipe], ebx |
431 | pop eax |
431 | pop eax |
432 | ; 9. Free the usb_device_data structure. |
432 | ; 9. Free the usb_device_data structure. |
433 | sub eax, usb_device_data.ClosedPipeList - usb_pipe.NextSibling |
433 | sub eax, usb_device_data.ClosedPipeList - usb_pipe.NextSibling |
434 | call free |
434 | call free |
435 | ; 10. Return. |
435 | ; 10. Return. |
436 | .nothing: |
436 | .nothing: |
437 | pop edi |
437 | pop edi |
438 | ret |
438 | ret |
439 | endp |
439 | endp |
440 | 440 | ||
441 | ; This procedure is called when a pipe with USB_FLAG_DISABLED is removed from the |
441 | ; This procedure is called when a pipe with USB_FLAG_DISABLED is removed from the |
442 | ; corresponding wait list. It means that the hardware has fully forgot about it. |
442 | ; corresponding wait list. It means that the hardware has fully forgot about it. |
443 | ; ebx -> usb_pipe, esi -> usb_controller |
443 | ; ebx -> usb_pipe, esi -> usb_controller |
444 | proc usb_pipe_disabled |
444 | proc usb_pipe_disabled |
445 | push edi |
445 | push edi |
446 | mov edi, [esi+usb_controller.HardwareFunc] |
446 | mov edi, [esi+usb_controller.HardwareFunc] |
447 | ; 1. Acquire pipe lock. |
447 | ; 1. Acquire pipe lock. |
448 | lea ecx, [ebx+usb_pipe.Lock] |
448 | lea ecx, [ebx+usb_pipe.Lock] |
449 | call mutex_lock |
449 | call mutex_lock |
450 | ; 2. Clear USB_FLAG_DISABLED in pipe state. |
450 | ; 2. Clear USB_FLAG_DISABLED in pipe state. |
451 | and [ebx+usb_pipe.Flags], not USB_FLAG_DISABLED |
451 | and [ebx+usb_pipe.Flags], not USB_FLAG_DISABLED |
452 | ; 3. Sanity check: ignore uninitialized pipes. |
452 | ; 3. Sanity check: ignore uninitialized pipes. |
453 | cmp [ebx+usb_pipe.LastTD], 0 |
453 | cmp [ebx+usb_pipe.LastTD], 0 |
454 | jz .no_transfer |
454 | jz .no_transfer |
455 | ; 4. Acquire the first and last to-be-cancelled transfer descriptor, |
455 | ; 4. Acquire the first and last to-be-cancelled transfer descriptor, |
456 | ; save them in stack for the step 6, |
456 | ; save them in stack for the step 6, |
457 | ; ask the controller driver to enable the pipe for hardware, |
457 | ; ask the controller driver to enable the pipe for hardware, |
458 | ; removing transfers between first and last to-be-cancelled descriptors. |
458 | ; removing transfers between first and last to-be-cancelled descriptors. |
459 | lea ecx, [esi+usb_controller.ControlLock] |
459 | lea ecx, [esi+usb_controller.ControlLock] |
460 | cmp [ebx+usb_pipe.Type], BULK_PIPE |
460 | cmp [ebx+usb_pipe.Type], BULK_PIPE |
461 | jb @f ; control pipe |
461 | jb @f ; control pipe |
462 | lea ecx, [esi+usb_controller.BulkLock] |
462 | lea ecx, [esi+usb_controller.BulkLock] |
463 | jz @f ; bulk pipe |
463 | jz @f ; bulk pipe |
464 | lea ecx, [esi+usb_controller.PeriodicLock] |
464 | lea ecx, [esi+usb_controller.PeriodicLock] |
465 | @@: |
465 | @@: |
466 | call mutex_lock |
466 | call mutex_lock |
467 | mov eax, [ebx+usb_pipe.BaseList] |
467 | mov eax, [ebx+usb_pipe.BaseList] |
468 | mov edx, [eax+usb_pipe.NextVirt] |
468 | mov edx, [eax+usb_pipe.NextVirt] |
469 | mov [ebx+usb_pipe.NextVirt], edx |
469 | mov [ebx+usb_pipe.NextVirt], edx |
470 | mov [ebx+usb_pipe.PrevVirt], eax |
470 | mov [ebx+usb_pipe.PrevVirt], eax |
471 | mov [edx+usb_pipe.PrevVirt], ebx |
471 | mov [edx+usb_pipe.PrevVirt], ebx |
472 | mov [eax+usb_pipe.NextVirt], ebx |
472 | mov [eax+usb_pipe.NextVirt], ebx |
473 | mov eax, [ebx+usb_pipe.LastTD] |
473 | mov eax, [ebx+usb_pipe.LastTD] |
474 | mov edx, [eax+usb_gtd.NextVirt] |
474 | mov edx, [eax+usb_gtd.NextVirt] |
475 | mov [eax+usb_gtd.NextVirt], eax |
475 | mov [eax+usb_gtd.NextVirt], eax |
476 | mov [eax+usb_gtd.PrevVirt], eax |
476 | mov [eax+usb_gtd.PrevVirt], eax |
477 | push eax |
477 | push eax |
478 | push edx |
478 | push edx |
479 | push ecx |
479 | push ecx |
480 | call [edi+usb_hardware_func.EnablePipe] |
480 | call [edi+usb_hardware_func.EnablePipe] |
481 | pop ecx |
481 | pop ecx |
482 | call mutex_unlock |
482 | call mutex_unlock |
483 | ; 5. Release pipe lock acquired at step 1. |
483 | ; 5. Release pipe lock acquired at step 1. |
484 | ; Callbacks called at step 6 can insert new transfers, |
484 | ; Callbacks called at step 6 can insert new transfers, |
485 | ; so we cannot call usb_pipe_aborted while holding pipe lock. |
485 | ; so we cannot call usb_pipe_aborted while holding pipe lock. |
486 | lea ecx, [ebx+usb_pipe.Lock] |
486 | lea ecx, [ebx+usb_pipe.Lock] |
487 | call mutex_unlock |
487 | call mutex_unlock |
488 | ; 6. Notify all registered callbacks with status USB_STATUS_CANCELLED, if any. |
488 | ; 6. Notify all registered callbacks with status USB_STATUS_CANCELLED, if any. |
489 | ; Two arguments describing transfers range were pushed at step 4. |
489 | ; Two arguments describing transfers range were pushed at step 4. |
490 | push USB_STATUS_CANCELLED |
490 | push USB_STATUS_CANCELLED |
491 | call usb_pipe_aborted |
491 | call usb_pipe_aborted |
492 | pop edi |
492 | pop edi |
493 | ret |
493 | ret |
494 | .no_transfer: |
494 | .no_transfer: |
495 | call mutex_unlock |
495 | call mutex_unlock |
496 | pop edi |
496 | pop edi |
497 | ret |
497 | ret |
498 | endp |
498 | endp |
499 | 499 | ||
500 | ; Part of API for drivers, see documentation for USBNormalTransferAsync. |
500 | ; Part of API for drivers, see documentation for USBNormalTransferAsync. |
501 | proc usb_normal_transfer_async stdcall uses ebx edi,\ |
501 | proc usb_normal_transfer_async stdcall uses ebx edi,\ |
502 | pipe:dword, buffer:dword, size:dword, callback:dword, calldata:dword, flags:dword |
502 | pipe:dword, buffer:dword, size:dword, callback:dword, calldata:dword, flags:dword |
503 | ; 1. Sanity check: callback must be nonzero. |
503 | ; 1. Sanity check: callback must be nonzero. |
504 | ; (It is important for other parts of code.) |
504 | ; (It is important for other parts of code.) |
505 | xor eax, eax |
505 | xor eax, eax |
506 | cmp [callback], eax |
506 | cmp [callback], eax |
507 | jz .nothing |
507 | jz .nothing |
508 | ; 2. Lock the transfer queue. |
508 | ; 2. Lock the transfer queue. |
509 | mov ebx, [pipe] |
509 | mov ebx, [pipe] |
510 | lea ecx, [ebx+usb_pipe.Lock] |
510 | lea ecx, [ebx+usb_pipe.Lock] |
511 | call mutex_lock |
511 | call mutex_lock |
512 | ; 3. If the pipe has already been closed (presumably due to device disconnect), |
512 | ; 3. If the pipe has already been closed (presumably due to device disconnect), |
513 | ; release the lock taken in step 2 and return zero. |
513 | ; release the lock taken in step 2 and return zero. |
514 | xor eax, eax |
514 | xor eax, eax |
515 | test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED |
515 | test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED |
516 | jnz .unlock |
516 | jnz .unlock |
517 | ; 4. Allocate and initialize TDs for the transfer. |
517 | ; 4. Allocate and initialize TDs for the transfer. |
518 | mov edx, [ebx+usb_pipe.Controller] |
518 | mov edx, [ebx+usb_pipe.Controller] |
519 | mov edi, [edx+usb_controller.HardwareFunc] |
519 | mov edi, [edx+usb_controller.HardwareFunc] |
520 | stdcall [edi+usb_hardware_func.AllocTransfer], [buffer], [size], [flags], [ebx+usb_pipe.LastTD], 0 |
520 | stdcall [edi+usb_hardware_func.AllocTransfer], [buffer], [size], [flags], [ebx+usb_pipe.LastTD], 0 |
521 | ; If failed, release the lock taken in step 2 and return zero. |
521 | ; If failed, release the lock taken in step 2 and return zero. |
522 | test eax, eax |
522 | test eax, eax |
523 | jz .unlock |
523 | jz .unlock |
524 | ; 5. Store callback and its parameters in the last descriptor for this transfer. |
524 | ; 5. Store callback and its parameters in the last descriptor for this transfer. |
525 | mov ecx, [eax+usb_gtd.PrevVirt] |
525 | mov ecx, [eax+usb_gtd.PrevVirt] |
526 | mov edx, [callback] |
526 | mov edx, [callback] |
527 | mov [ecx+usb_gtd.Callback], edx |
527 | mov [ecx+usb_gtd.Callback], edx |
528 | mov edx, [calldata] |
528 | mov edx, [calldata] |
529 | mov [ecx+usb_gtd.UserData], edx |
529 | mov [ecx+usb_gtd.UserData], edx |
530 | mov edx, [buffer] |
530 | mov edx, [buffer] |
531 | mov [ecx+usb_gtd.Buffer], edx |
531 | mov [ecx+usb_gtd.Buffer], edx |
532 | ; 6. Advance LastTD pointer and activate transfer. |
532 | ; 6. Advance LastTD pointer and activate transfer. |
533 | push [ebx+usb_pipe.LastTD] |
533 | push [ebx+usb_pipe.LastTD] |
534 | mov [ebx+usb_pipe.LastTD], eax |
534 | mov [ebx+usb_pipe.LastTD], eax |
535 | call [edi+usb_hardware_func.InsertTransfer] |
535 | call [edi+usb_hardware_func.InsertTransfer] |
536 | pop eax |
536 | pop eax |
537 | ; 7. Release the lock taken in step 2 and |
537 | ; 7. Release the lock taken in step 2 and |
538 | ; return pointer to the first descriptor for the new transfer. |
538 | ; return pointer to the first descriptor for the new transfer. |
539 | .unlock: |
539 | .unlock: |
540 | push eax |
540 | push eax |
541 | lea ecx, [ebx+usb_pipe.Lock] |
541 | lea ecx, [ebx+usb_pipe.Lock] |
542 | call mutex_unlock |
542 | call mutex_unlock |
543 | pop eax |
543 | pop eax |
544 | .nothing: |
544 | .nothing: |
545 | ret |
545 | ret |
546 | endp |
546 | endp |
547 | 547 | ||
548 | ; Part of API for drivers, see documentation for USBControlTransferAsync. |
548 | ; Part of API for drivers, see documentation for USBControlTransferAsync. |
549 | proc usb_control_async stdcall uses ebx edi,\ |
549 | proc usb_control_async stdcall uses ebx edi,\ |
550 | pipe:dword, config:dword, buffer:dword, size:dword, callback:dword, calldata:dword, flags:dword |
550 | pipe:dword, config:dword, buffer:dword, size:dword, callback:dword, calldata:dword, flags:dword |
551 | locals |
551 | locals |
552 | last_td dd ? |
552 | last_td dd ? |
553 | endl |
553 | endl |
554 | ; 1. Sanity check: callback must be nonzero. |
554 | ; 1. Sanity check: callback must be nonzero. |
555 | ; (It is important for other parts of code.) |
555 | ; (It is important for other parts of code.) |
556 | cmp [callback], 0 |
556 | cmp [callback], 0 |
557 | jz .return0 |
557 | jz .return0 |
558 | ; 2. Lock the transfer queue. |
558 | ; 2. Lock the transfer queue. |
559 | mov ebx, [pipe] |
559 | mov ebx, [pipe] |
560 | lea ecx, [ebx+usb_pipe.Lock] |
560 | lea ecx, [ebx+usb_pipe.Lock] |
561 | call mutex_lock |
561 | call mutex_lock |
562 | ; 3. If the pipe has already been closed (presumably due to device disconnect), |
562 | ; 3. If the pipe has already been closed (presumably due to device disconnect), |
563 | ; release the lock taken in step 2 and return zero. |
563 | ; release the lock taken in step 2 and return zero. |
564 | test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED |
564 | test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED |
565 | jnz .unlock_return0 |
565 | jnz .unlock_return0 |
566 | ; A control transfer contains two or three stages: |
566 | ; A control transfer contains two or three stages: |
567 | ; Setup stage, optional Data stage, Status stage. |
567 | ; Setup stage, optional Data stage, Status stage. |
568 | ; 4. Allocate and initialize TDs for the Setup stage. |
568 | ; 4. Allocate and initialize TDs for the Setup stage. |
569 | ; Payload is 8 bytes from [config]. |
569 | ; Payload is 8 bytes from [config]. |
570 | mov edx, [ebx+usb_pipe.Controller] |
570 | mov edx, [ebx+usb_pipe.Controller] |
571 | mov edi, [edx+usb_controller.HardwareFunc] |
571 | mov edi, [edx+usb_controller.HardwareFunc] |
572 | stdcall [edi+usb_hardware_func.AllocTransfer], [config], 8, 0, [ebx+usb_pipe.LastTD], (2 shl 2) + 0 |
572 | stdcall [edi+usb_hardware_func.AllocTransfer], [config], 8, 0, [ebx+usb_pipe.LastTD], (2 shl 2) + 0 |
573 | ; short transfer is an error, direction is DATA0, token is SETUP |
573 | ; short transfer is an error, direction is DATA0, token is SETUP |
574 | mov [last_td], eax |
574 | mov [last_td], eax |
575 | test eax, eax |
575 | test eax, eax |
576 | jz .fail |
576 | jz .fail |
577 | ; 5. Allocate and initialize TDs for the Data stage, if [size] is nonzero. |
577 | ; 5. Allocate and initialize TDs for the Data stage, if [size] is nonzero. |
578 | ; Payload is [size] bytes from [buffer]. |
578 | ; Payload is [size] bytes from [buffer]. |
579 | mov edx, [config] |
579 | mov edx, [config] |
580 | mov ecx, (3 shl 2) + 1 ; DATA1, token is OUT |
580 | mov ecx, (3 shl 2) + 1 ; DATA1, token is OUT |
581 | cmp byte [edx], 0 |
581 | cmp byte [edx], 0 |
582 | jns @f |
582 | jns @f |
583 | cmp [size], 0 |
583 | cmp [size], 0 |
584 | jz @f |
584 | jz @f |
585 | inc ecx ; token is IN |
585 | inc ecx ; token is IN |
586 | @@: |
586 | @@: |
587 | cmp [size], 0 |
587 | cmp [size], 0 |
588 | jz .nodata |
588 | jz .nodata |
589 | push ecx |
589 | push ecx |
590 | stdcall [edi+usb_hardware_func.AllocTransfer], [buffer], [size], [flags], eax, ecx |
590 | stdcall [edi+usb_hardware_func.AllocTransfer], [buffer], [size], [flags], eax, ecx |
591 | pop ecx |
591 | pop ecx |
592 | test eax, eax |
592 | test eax, eax |
593 | jz .fail |
593 | jz .fail |
594 | mov [last_td], eax |
594 | mov [last_td], eax |
595 | .nodata: |
595 | .nodata: |
596 | ; 6. Allocate and initialize TDs for the Status stage. |
596 | ; 6. Allocate and initialize TDs for the Status stage. |
597 | ; No payload. |
597 | ; No payload. |
598 | xor ecx, 3 ; IN becomes OUT, OUT becomes IN |
598 | xor ecx, 3 ; IN becomes OUT, OUT becomes IN |
599 | stdcall [edi+usb_hardware_func.AllocTransfer], 0, 0, 0, eax, ecx |
599 | stdcall [edi+usb_hardware_func.AllocTransfer], 0, 0, 0, eax, ecx |
600 | test eax, eax |
600 | test eax, eax |
601 | jz .fail |
601 | jz .fail |
602 | ; 7. Store callback and its parameters in the last descriptor for this transfer. |
602 | ; 7. Store callback and its parameters in the last descriptor for this transfer. |
603 | mov ecx, [eax+usb_gtd.PrevVirt] |
603 | mov ecx, [eax+usb_gtd.PrevVirt] |
604 | mov edx, [callback] |
604 | mov edx, [callback] |
605 | mov [ecx+usb_gtd.Callback], edx |
605 | mov [ecx+usb_gtd.Callback], edx |
606 | mov edx, [calldata] |
606 | mov edx, [calldata] |
607 | mov [ecx+usb_gtd.UserData], edx |
607 | mov [ecx+usb_gtd.UserData], edx |
608 | mov edx, [buffer] |
608 | mov edx, [buffer] |
609 | mov [ecx+usb_gtd.Buffer], edx |
609 | mov [ecx+usb_gtd.Buffer], edx |
610 | ; 8. Advance LastTD pointer and activate transfer. |
610 | ; 8. Advance LastTD pointer and activate transfer. |
611 | push [ebx+usb_pipe.LastTD] |
611 | push [ebx+usb_pipe.LastTD] |
612 | mov [ebx+usb_pipe.LastTD], eax |
612 | mov [ebx+usb_pipe.LastTD], eax |
613 | call [edi+usb_hardware_func.InsertTransfer] |
613 | call [edi+usb_hardware_func.InsertTransfer] |
614 | ; 9. Release the lock taken in step 2 and |
614 | ; 9. Release the lock taken in step 2 and |
615 | ; return pointer to the first descriptor for the new transfer. |
615 | ; return pointer to the first descriptor for the new transfer. |
616 | lea ecx, [ebx+usb_pipe.Lock] |
616 | lea ecx, [ebx+usb_pipe.Lock] |
617 | call mutex_unlock |
617 | call mutex_unlock |
618 | pop eax |
618 | pop eax |
619 | ret |
619 | ret |
620 | .fail: |
620 | .fail: |
621 | mov eax, [last_td] |
621 | mov eax, [last_td] |
622 | test eax, eax |
622 | test eax, eax |
623 | jz .unlock_return0 |
623 | jz .unlock_return0 |
624 | stdcall usb_undo_tds, [ebx+usb_pipe.LastTD] |
624 | stdcall usb_undo_tds, [ebx+usb_pipe.LastTD] |
625 | .unlock_return0: |
625 | .unlock_return0: |
626 | lea ecx, [ebx+usb_pipe.Lock] |
626 | lea ecx, [ebx+usb_pipe.Lock] |
627 | call mutex_unlock |
627 | call mutex_unlock |
628 | .return0: |
628 | .return0: |
629 | xor eax, eax |
629 | xor eax, eax |
630 | ret |
630 | ret |
631 | endp |
631 | endp |
632 | 632 | ||
633 | ; Part of API for drivers, see documentation for USBAbortPipe. |
633 | ; Part of API for drivers, see documentation for USBAbortPipe. |
634 | proc usb_abort_pipe |
634 | proc usb_abort_pipe |
635 | push ebx esi ; save used registers to be stdcall |
635 | push ebx esi ; save used registers to be stdcall |
636 | virtual at esp |
636 | virtual at esp |
637 | rd 2 ; saved registers |
637 | rd 2 ; saved registers |
638 | dd ? ; return address |
638 | dd ? ; return address |
639 | .pipe dd ? |
639 | .pipe dd ? |
640 | end virtual |
640 | end virtual |
641 | mov ebx, [.pipe] |
641 | mov ebx, [.pipe] |
642 | ; 1. Acquire pipe lock. |
642 | ; 1. Acquire pipe lock. |
643 | lea ecx, [ebx+usb_pipe.Lock] |
643 | lea ecx, [ebx+usb_pipe.Lock] |
644 | call mutex_lock |
644 | call mutex_lock |
645 | ; 2. If the pipe is already closed or abort is in progress, |
645 | ; 2. If the pipe is already closed or abort is in progress, |
646 | ; just release pipe lock and return. |
646 | ; just release pipe lock and return. |
647 | test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED + USB_FLAG_DISABLED |
647 | test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED + USB_FLAG_DISABLED |
648 | jnz .nothing |
648 | jnz .nothing |
649 | ; 3. Mark the pipe as aborting. |
649 | ; 3. Mark the pipe as aborting. |
650 | or [ebx+usb_pipe.Flags], USB_FLAG_DISABLED |
650 | or [ebx+usb_pipe.Flags], USB_FLAG_DISABLED |
651 | ; 4. We cannot do anything except adding new transfers concurrently with hardware. |
651 | ; 4. We cannot do anything except adding new transfers concurrently with hardware. |
652 | ; Ask the controller driver to (temporarily) remove the pipe from hardware queue. |
652 | ; Ask the controller driver to (temporarily) remove the pipe from hardware queue. |
653 | mov esi, [ebx+usb_pipe.Controller] |
653 | mov esi, [ebx+usb_pipe.Controller] |
654 | ; 4a. Acquire queue lock. |
654 | ; 4a. Acquire queue lock. |
655 | lea ecx, [esi+usb_controller.ControlLock] |
655 | lea ecx, [esi+usb_controller.ControlLock] |
656 | cmp [ebx+usb_pipe.Type], BULK_PIPE |
656 | cmp [ebx+usb_pipe.Type], BULK_PIPE |
657 | jb @f ; control pipe |
657 | jb @f ; control pipe |
658 | lea ecx, [esi+usb_controller.BulkLock] |
658 | lea ecx, [esi+usb_controller.BulkLock] |
659 | jz @f ; bulk pipe |
659 | jz @f ; bulk pipe |
660 | lea ecx, [esi+usb_controller.PeriodicLock] |
660 | lea ecx, [esi+usb_controller.PeriodicLock] |
661 | @@: |
661 | @@: |
662 | call mutex_lock |
662 | call mutex_lock |
663 | push ecx |
663 | push ecx |
664 | ; 4b. Call the driver. |
664 | ; 4b. Call the driver. |
665 | mov eax, [esi+usb_controller.HardwareFunc] |
665 | mov eax, [esi+usb_controller.HardwareFunc] |
666 | call [eax+usb_hardware_func.DisablePipe] |
666 | call [eax+usb_hardware_func.DisablePipe] |
667 | ; 4c. Remove the pipe from software list. |
667 | ; 4c. Remove the pipe from software list. |
668 | mov eax, [ebx+usb_pipe.NextVirt] |
668 | mov eax, [ebx+usb_pipe.NextVirt] |
669 | mov edx, [ebx+usb_pipe.PrevVirt] |
669 | mov edx, [ebx+usb_pipe.PrevVirt] |
670 | mov [eax+usb_pipe.PrevVirt], edx |
670 | mov [eax+usb_pipe.PrevVirt], edx |
671 | mov [edx+usb_pipe.NextVirt], eax |
671 | mov [edx+usb_pipe.NextVirt], eax |
672 | ; 4c. Register the pipe in corresponding wait list. |
672 | ; 4c. Register the pipe in corresponding wait list. |
673 | test [ebx+usb_pipe.Type], 1 |
673 | test [ebx+usb_pipe.Type], 1 |
674 | jz .control_bulk |
674 | jz .control_bulk |
675 | call usb_subscribe_periodic |
675 | call usb_subscribe_periodic |
676 | jmp @f |
676 | jmp @f |
677 | .control_bulk: |
677 | .control_bulk: |
678 | call usb_subscribe_control |
678 | call usb_subscribe_control |
679 | @@: |
679 | @@: |
680 | ; 4d. Release queue lock. |
680 | ; 4d. Release queue lock. |
681 | pop ecx |
681 | pop ecx |
682 | call mutex_unlock |
682 | call mutex_unlock |
683 | ; 4e. Notify the USB thread about new work. |
683 | ; 4e. Notify the USB thread about new work. |
684 | push ebx esi edi |
684 | push ebx esi edi |
685 | call usb_wakeup |
685 | call usb_wakeup |
686 | pop edi esi ebx |
686 | pop edi esi ebx |
687 | ; That's all for now. To be continued in usb_pipe_disabled. |
687 | ; That's all for now. To be continued in usb_pipe_disabled. |
688 | ; 5. Release pipe lock acquired at step 1 and return. |
688 | ; 5. Release pipe lock acquired at step 1 and return. |
689 | .nothing: |
689 | .nothing: |
690 | lea ecx, [ebx+usb_pipe.Lock] |
690 | lea ecx, [ebx+usb_pipe.Lock] |
691 | call mutex_unlock |
691 | call mutex_unlock |
692 | pop esi ebx |
692 | pop esi ebx |
693 | ret 4 |
693 | ret 4 |
694 | endp |
694 | endp |
695 | 695 | ||
696 | ; Part of API for drivers, see documentation for USBGetParam. |
696 | ; Part of API for drivers, see documentation for USBGetParam. |
697 | proc usb_get_param |
697 | proc usb_get_param |
698 | virtual at esp |
698 | virtual at esp |
699 | dd ? ; return address |
699 | dd ? ; return address |
700 | .pipe dd ? |
700 | .pipe dd ? |
701 | .param dd ? |
701 | .param dd ? |
702 | end virtual |
702 | end virtual |
703 | mov edx, [.param] |
703 | mov edx, [.param] |
704 | mov ecx, [.pipe] |
704 | mov ecx, [.pipe] |
705 | mov eax, [ecx+usb_pipe.DeviceData] |
705 | mov eax, [ecx+usb_pipe.DeviceData] |
706 | test edx, edx |
706 | test edx, edx |
707 | jz .get_device_descriptor |
707 | jz .get_device_descriptor |
708 | dec edx |
708 | dec edx |
709 | jz .get_config_descriptor |
709 | jz .get_config_descriptor |
710 | dec edx |
710 | dec edx |
711 | jz .get_speed |
711 | jz .get_speed |
712 | or eax, -1 |
712 | or eax, -1 |
713 | ret 8 |
713 | ret 8 |
714 | .get_device_descriptor: |
714 | .get_device_descriptor: |
715 | add eax, usb_device_data.DeviceDescriptor |
715 | add eax, usb_device_data.DeviceDescriptor |
716 | ret 8 |
716 | ret 8 |
717 | .get_config_descriptor: |
717 | .get_config_descriptor: |
718 | movzx ecx, [eax+usb_device_data.DeviceDescrSize] |
718 | movzx ecx, [eax+usb_device_data.DeviceDescrSize] |
719 | lea eax, [eax+ecx+usb_device_data.DeviceDescriptor] |
719 | lea eax, [eax+ecx+usb_device_data.DeviceDescriptor] |
720 | ret 8 |
720 | ret 8 |
721 | .get_speed: |
721 | .get_speed: |
722 | movzx eax, [eax+usb_device_data.Speed] |
722 | movzx eax, [eax+usb_device_data.Speed] |
723 | ret 8 |
723 | ret 8 |
724 | endp |
724 | endp |
725 | 725 | ||
726 | ; Initialize software part of usb_gtd. Called from controller-specific code |
726 | ; Initialize software part of usb_gtd. Called from controller-specific code |
727 | ; somewhere in AllocTransfer with eax -> next (inactive) usb_gtd, |
727 | ; somewhere in AllocTransfer with eax -> next (inactive) usb_gtd, |
728 | ; ebx -> usb_pipe, ebp frame from call to AllocTransfer with [.td] -> |
728 | ; ebx -> usb_pipe, ebp frame from call to AllocTransfer with [.td] -> |
729 | ; current (initializing) usb_gtd. |
729 | ; current (initializing) usb_gtd. |
730 | ; Returns ecx = [.td]. |
730 | ; Returns ecx = [.td]. |
731 | proc usb_init_transfer |
731 | proc usb_init_transfer |
732 | virtual at ebp-4 |
732 | virtual at ebp-4 |
733 | .Size dd ? |
733 | .Size dd ? |
734 | rd 2 |
734 | rd 2 |
735 | .Buffer dd ? |
735 | .Buffer dd ? |
736 | dd ? |
736 | dd ? |
737 | .Flags dd ? |
737 | .Flags dd ? |
738 | .td dd ? |
738 | .td dd ? |
739 | end virtual |
739 | end virtual |
740 | mov [eax+usb_gtd.Pipe], ebx |
740 | mov [eax+usb_gtd.Pipe], ebx |
741 | mov ecx, [.td] |
741 | mov ecx, [.td] |
742 | mov [eax+usb_gtd.PrevVirt], ecx |
742 | mov [eax+usb_gtd.PrevVirt], ecx |
743 | mov edx, [ecx+usb_gtd.NextVirt] |
743 | mov edx, [ecx+usb_gtd.NextVirt] |
744 | mov [ecx+usb_gtd.NextVirt], eax |
744 | mov [ecx+usb_gtd.NextVirt], eax |
745 | mov [eax+usb_gtd.NextVirt], edx |
745 | mov [eax+usb_gtd.NextVirt], edx |
746 | mov [edx+usb_gtd.PrevVirt], eax |
746 | mov [edx+usb_gtd.PrevVirt], eax |
747 | mov edx, [.Size] |
747 | mov edx, [.Size] |
748 | mov [ecx+usb_gtd.Length], edx |
748 | mov [ecx+usb_gtd.Length], edx |
749 | xor edx, edx |
749 | xor edx, edx |
750 | mov [ecx+usb_gtd.Callback], edx |
750 | mov [ecx+usb_gtd.Callback], edx |
751 | mov [ecx+usb_gtd.UserData], edx |
751 | mov [ecx+usb_gtd.UserData], edx |
752 | ret |
752 | ret |
753 | endp |
753 | endp |
754 | 754 | ||
755 | ; Free all TDs for the current transfer if something has failed |
755 | ; Free all TDs for the current transfer if something has failed |
756 | ; during initialization (e.g. no memory for the next TD). |
756 | ; during initialization (e.g. no memory for the next TD). |
757 | ; Stdcall with one stack argument = first TD for the transfer |
757 | ; Stdcall with one stack argument = first TD for the transfer |
758 | ; and eax = last initialized TD for the transfer. |
758 | ; and eax = last initialized TD for the transfer. |
759 | proc usb_undo_tds |
759 | proc usb_undo_tds |
760 | push [eax+usb_gtd.NextVirt] |
760 | push [eax+usb_gtd.NextVirt] |
761 | @@: |
761 | @@: |
762 | cmp eax, [esp+8] |
762 | cmp eax, [esp+8] |
763 | jz @f |
763 | jz @f |
764 | push [eax+usb_gtd.PrevVirt] |
764 | push [eax+usb_gtd.PrevVirt] |
765 | stdcall [edi+usb_hardware_func.FreeTD], eax |
765 | stdcall [edi+usb_hardware_func.FreeTD], eax |
766 | pop eax |
766 | pop eax |
767 | jmp @b |
767 | jmp @b |
768 | @@: |
768 | @@: |
769 | pop ecx |
769 | pop ecx |
770 | mov [eax+usb_gtd.NextVirt], ecx |
770 | mov [eax+usb_gtd.NextVirt], ecx |
771 | mov [ecx+usb_gtd.PrevVirt], eax |
771 | mov [ecx+usb_gtd.PrevVirt], eax |
772 | ret 4 |
772 | ret 4 |
773 | endp |
773 | endp |
774 | 774 | ||
775 | ; Helper procedure for handling short packets in controller-specific code. |
775 | ; Helper procedure for handling short packets in controller-specific code. |
776 | ; Returns with CF cleared if this is the final packet in some stage: |
776 | ; Returns with CF cleared if this is the final packet in some stage: |
777 | ; for control transfers that means one of Data and Status stages, |
777 | ; for control transfers that means one of Data and Status stages, |
778 | ; for other transfers - the final packet in the only stage. |
778 | ; for other transfers - the final packet in the only stage. |
779 | proc usb_is_final_packet |
779 | proc usb_is_final_packet |
780 | cmp [ebx+usb_gtd.Callback], 0 |
780 | cmp [ebx+usb_gtd.Callback], 0 |
781 | jnz .nothing |
781 | jnz .nothing |
782 | mov eax, [ebx+usb_gtd.NextVirt] |
782 | mov eax, [ebx+usb_gtd.NextVirt] |
783 | cmp [eax+usb_gtd.Callback], 0 |
783 | cmp [eax+usb_gtd.Callback], 0 |
784 | jz .stc |
784 | jz .stc |
785 | mov eax, [ebx+usb_gtd.Pipe] |
785 | mov eax, [ebx+usb_gtd.Pipe] |
786 | cmp [eax+usb_pipe.Type], CONTROL_PIPE |
786 | cmp [eax+usb_pipe.Type], CONTROL_PIPE |
787 | jz .nothing |
787 | jz .nothing |
788 | .stc: |
788 | .stc: |
789 | stc |
789 | stc |
790 | .nothing: |
790 | .nothing: |
791 | ret |
791 | ret |
792 | endp |
792 | endp |
793 | 793 | ||
794 | ; Helper procedure for controller-specific code: |
794 | ; Helper procedure for controller-specific code: |
795 | ; removes one TD from the transfer queue, ebx -> usb_gtd to remove. |
795 | ; removes one TD from the transfer queue, ebx -> usb_gtd to remove. |
796 | proc usb_unlink_td |
796 | proc usb_unlink_td |
797 | mov ecx, [ebx+usb_gtd.Pipe] |
797 | mov ecx, [ebx+usb_gtd.Pipe] |
798 | add ecx, usb_pipe.Lock |
798 | add ecx, usb_pipe.Lock |
799 | call mutex_lock |
799 | call mutex_lock |
800 | mov eax, [ebx+usb_gtd.PrevVirt] |
800 | mov eax, [ebx+usb_gtd.PrevVirt] |
801 | mov edx, [ebx+usb_gtd.NextVirt] |
801 | mov edx, [ebx+usb_gtd.NextVirt] |
802 | mov [edx+usb_gtd.PrevVirt], eax |
802 | mov [edx+usb_gtd.PrevVirt], eax |
803 | mov [eax+usb_gtd.NextVirt], edx |
803 | mov [eax+usb_gtd.NextVirt], edx |
804 | call mutex_unlock |
804 | call mutex_unlock |
805 | ret |
805 | ret |
806 | endp |
806 | endp |
807 | 807 | ||
808 | ; One part of transfer is completed, run the associated callback |
808 | ; One part of transfer is completed, run the associated callback |
809 | ; or update total length in the next part of transfer. |
809 | ; or update total length in the next part of transfer. |
810 | ; in: ebx -> usb_gtd, ecx = status, edx = length |
810 | ; in: ebx -> usb_gtd, ecx = status, edx = length |
811 | proc usb_process_gtd |
811 | proc usb_process_gtd |
812 | ; 1. Test whether it is the last descriptor in the transfer |
812 | ; 1. Test whether it is the last descriptor in the transfer |
813 | ; <=> it has an associated callback. |
813 | ; <=> it has an associated callback. |
814 | mov eax, [ebx+usb_gtd.Callback] |
814 | mov eax, [ebx+usb_gtd.Callback] |
815 | test eax, eax |
815 | test eax, eax |
816 | jz .nocallback |
816 | jz .nocallback |
817 | ; 2. It has an associated callback; call it with corresponding parameters. |
817 | ; 2. It has an associated callback; call it with corresponding parameters. |
818 | stdcall_verify eax, [ebx+usb_gtd.Pipe], ecx, \ |
818 | stdcall_verify eax, [ebx+usb_gtd.Pipe], ecx, \ |
819 | [ebx+usb_gtd.Buffer], edx, [ebx+usb_gtd.UserData] |
819 | [ebx+usb_gtd.Buffer], edx, [ebx+usb_gtd.UserData] |
820 | ret |
820 | ret |
821 | .nocallback: |
821 | .nocallback: |
822 | ; 3. It is an intermediate descriptor. Add its length to the length |
822 | ; 3. It is an intermediate descriptor. Add its length to the length |
823 | ; in the following descriptor. |
823 | ; in the following descriptor. |
824 | mov eax, [ebx+usb_gtd.NextVirt] |
824 | mov eax, [ebx+usb_gtd.NextVirt] |
825 | add [eax+usb_gtd.Length], edx |
825 | add [eax+usb_gtd.Length], edx |
826 | ret |
826 | ret |
827 | endp |
827 | endp |
828 | 828 | ||
829 | if USB_STDCALL_VERIFY |
829 | if USB_STDCALL_VERIFY |
830 | proc verify_regs |
830 | proc verify_regs |
831 | virtual at esp |
831 | virtual at esp |
832 | dd ? ; return address |
832 | dd ? ; return address |
833 | .edi dd ? |
833 | .edi dd ? |
834 | .esi dd ? |
834 | .esi dd ? |
835 | .ebp dd ? |
835 | .ebp dd ? |
836 | .esp dd ? |
836 | .esp dd ? |
837 | .ebx dd ? |
837 | .ebx dd ? |
838 | .edx dd ? |
838 | .edx dd ? |
839 | .ecx dd ? |
839 | .ecx dd ? |
840 | .eax dd ? |
840 | .eax dd ? |
841 | end virtual |
841 | end virtual |
842 | cmp ebx, [.ebx] |
842 | cmp ebx, [.ebx] |
843 | jz @f |
843 | jz @f |
844 | dbgstr 'ERROR!!! ebx changed' |
844 | dbgstr 'ERROR!!! ebx changed' |
845 | @@: |
845 | @@: |
846 | cmp esi, [.esi] |
846 | cmp esi, [.esi] |
847 | jz @f |
847 | jz @f |
848 | dbgstr 'ERROR!!! esi changed' |
848 | dbgstr 'ERROR!!! esi changed' |
849 | @@: |
849 | @@: |
850 | cmp edi, [.edi] |
850 | cmp edi, [.edi] |
851 | jz @f |
851 | jz @f |
852 | dbgstr 'ERROR!!! edi changed' |
852 | dbgstr 'ERROR!!! edi changed' |
853 | @@: |
853 | @@: |
854 | cmp ebp, [.ebp] |
854 | cmp ebp, [.ebp] |
855 | jz @f |
855 | jz @f |
856 | dbgstr 'ERROR!!! ebp changed' |
856 | dbgstr 'ERROR!!! ebp changed' |
857 | @@: |
857 | @@: |
858 | ret |
858 | ret |
859 | endp |
859 | endp |
860 | end if=> |
860 | end if=> |