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