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