Rev 3653 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 3653 | Rev 3656 | ||
---|---|---|---|
1 | ; Code for EHCI controllers. |
1 | ; Code for EHCI controllers. |
2 | ; Note: it should be moved to an external driver, |
2 | ; Note: it should be moved to an external driver, |
3 | ; it was convenient to have this code compiled into the kernel during initial |
3 | ; it was convenient to have this code compiled into the kernel during initial |
4 | ; development, but there are no reasons to keep it here. |
4 | ; development, but there are no reasons to keep it here. |
5 | 5 | ||
6 | ; ============================================================================= |
6 | ; ============================================================================= |
7 | ; ================================= Constants ================================= |
7 | ; ================================= Constants ================================= |
8 | ; ============================================================================= |
8 | ; ============================================================================= |
9 | ; EHCI register declarations. |
9 | ; EHCI register declarations. |
10 | ; Part 1. Capability registers. |
10 | ; Part 1. Capability registers. |
11 | ; Base is MMIO from the PCI space. |
11 | ; Base is MMIO from the PCI space. |
12 | EhciCapLengthReg = 0 |
12 | EhciCapLengthReg = 0 |
13 | EhciVersionReg = 2 |
13 | EhciVersionReg = 2 |
14 | EhciStructParamsReg = 4 |
14 | EhciStructParamsReg = 4 |
15 | EhciCapParamsReg = 8 |
15 | EhciCapParamsReg = 8 |
16 | EhciPortRouteReg = 0Ch |
16 | EhciPortRouteReg = 0Ch |
17 | ; Part 2. Operational registers. |
17 | ; Part 2. Operational registers. |
18 | ; Base is (base for part 1) + (value of EhciCapLengthReg). |
18 | ; Base is (base for part 1) + (value of EhciCapLengthReg). |
19 | EhciCommandReg = 0 |
19 | EhciCommandReg = 0 |
20 | EhciStatusReg = 4 |
20 | EhciStatusReg = 4 |
21 | EhciInterruptReg = 8 |
21 | EhciInterruptReg = 8 |
22 | EhciFrameIndexReg = 0Ch |
22 | EhciFrameIndexReg = 0Ch |
23 | EhciCtrlDataSegReg = 10h |
23 | EhciCtrlDataSegReg = 10h |
24 | EhciPeriodicListReg = 14h |
24 | EhciPeriodicListReg = 14h |
25 | EhciAsyncListReg = 18h |
25 | EhciAsyncListReg = 18h |
26 | EhciConfigFlagReg = 40h |
26 | EhciConfigFlagReg = 40h |
27 | EhciPortsReg = 44h |
27 | EhciPortsReg = 44h |
28 | 28 | ||
29 | ; Possible values of ehci_pipe.NextQH.Type bitfield. |
29 | ; Possible values of ehci_pipe.NextQH.Type bitfield. |
30 | EHCI_TYPE_ITD = 0 ; isochronous transfer descriptor |
30 | EHCI_TYPE_ITD = 0 ; isochronous transfer descriptor |
31 | EHCI_TYPE_QH = 1 ; queue head |
31 | EHCI_TYPE_QH = 1 ; queue head |
32 | EHCI_TYPE_SITD = 2 ; split-transaction isochronous TD |
32 | EHCI_TYPE_SITD = 2 ; split-transaction isochronous TD |
33 | EHCI_TYPE_FSTN = 3 ; frame span traversal node |
33 | EHCI_TYPE_FSTN = 3 ; frame span traversal node |
34 | 34 | ||
35 | ; ============================================================================= |
35 | ; ============================================================================= |
36 | ; ================================ Structures ================================= |
36 | ; ================================ Structures ================================= |
37 | ; ============================================================================= |
37 | ; ============================================================================= |
38 | 38 | ||
39 | ; Hardware part of EHCI general transfer descriptor. |
39 | ; Hardware part of EHCI general transfer descriptor. |
40 | struct ehci_hardware_td |
40 | struct ehci_hardware_td |
41 | NextTD dd ? |
41 | NextTD dd ? |
42 | ; Bit 0 is Terminate bit, 1 = there is no next TD. |
42 | ; Bit 0 is Terminate bit, 1 = there is no next TD. |
43 | ; Bits 1-4 must be zero. |
43 | ; Bits 1-4 must be zero. |
44 | ; With masked 5 lower bits, this is the physical address of the next TD, if any. |
44 | ; With masked 5 lower bits, this is the physical address of the next TD, if any. |
45 | AlternateNextTD dd ? |
45 | AlternateNextTD dd ? |
46 | ; Similar to NextTD, used if the transfer terminates with a short packet. |
46 | ; Similar to NextTD, used if the transfer terminates with a short packet. |
47 | Token dd ? |
47 | Token dd ? |
48 | ; 1. Lower byte is Status field: |
48 | ; 1. Lower byte is Status field: |
49 | ; bit 0 = ping state for USB2 endpoints, ERR handshake signal for USB1 endpoints |
49 | ; bit 0 = ping state for USB2 endpoints, ERR handshake signal for USB1 endpoints |
50 | ; bit 1 = split transaction state, meaningless for USB2 endpoints |
50 | ; bit 1 = split transaction state, meaningless for USB2 endpoints |
51 | ; bit 2 = missed micro-frame |
51 | ; bit 2 = missed micro-frame |
52 | ; bit 3 = transaction error |
52 | ; bit 3 = transaction error |
53 | ; bit 4 = babble detected |
53 | ; bit 4 = babble detected |
54 | ; bit 5 = data buffer error |
54 | ; bit 5 = data buffer error |
55 | ; bit 6 = halted |
55 | ; bit 6 = halted |
56 | ; bit 7 = active |
56 | ; bit 7 = active |
57 | ; 2. Next two bits (bits 8-9) are PID code, 0 = OUT, 1 = IN, 2 = SETUP. |
57 | ; 2. Next two bits (bits 8-9) are PID code, 0 = OUT, 1 = IN, 2 = SETUP. |
58 | ; 3. Next two bits (bits 10-11) is ErrorCounter. Initialized as 3, decremented |
58 | ; 3. Next two bits (bits 10-11) is ErrorCounter. Initialized as 3, decremented |
59 | ; on each error; if it goes to zero, transaction is stopped. |
59 | ; on each error; if it goes to zero, transaction is stopped. |
60 | ; 4. Next 3 bits (bits 12-14) are CurrentPage field. |
60 | ; 4. Next 3 bits (bits 12-14) are CurrentPage field. |
61 | ; 5. Next bit (bit 15) is InterruptOnComplete bit. |
61 | ; 5. Next bit (bit 15) is InterruptOnComplete bit. |
62 | ; 6. Next 15 bits (bits 16-30) are TransferLength field, |
62 | ; 6. Next 15 bits (bits 16-30) are TransferLength field, |
63 | ; number of bytes to transfer. |
63 | ; number of bytes to transfer. |
64 | ; 7. Upper bit (bit 31) is DataToggle bit. |
64 | ; 7. Upper bit (bit 31) is DataToggle bit. |
65 | BufferPointers rd 5 |
65 | BufferPointers rd 5 |
66 | ; The buffer to be transferred can be spanned on up to 5 physical pages. |
66 | ; The buffer to be transferred can be spanned on up to 5 physical pages. |
67 | ; The first item of this array is the physical address of the first byte in |
67 | ; The first item of this array is the physical address of the first byte in |
68 | ; the buffer, other items are physical addresses of next pages. Lower 12 bits |
68 | ; the buffer, other items are physical addresses of next pages. Lower 12 bits |
69 | ; in other items must be set to zero; ehci_pipe.Overlay reuses some of them. |
69 | ; in other items must be set to zero; ehci_pipe.Overlay reuses some of them. |
70 | BufferPointersHigh rd 5 |
70 | BufferPointersHigh rd 5 |
71 | ; Upper dwords of BufferPointers for controllers with 64-bit memory access. |
71 | ; Upper dwords of BufferPointers for controllers with 64-bit memory access. |
72 | ; Always zero. |
72 | ; Always zero. |
73 | ends |
73 | ends |
74 | 74 | ||
75 | ; EHCI general transfer descriptor. |
75 | ; EHCI general transfer descriptor. |
76 | ; * The structure describes transfers to be performed on Control, Bulk or |
76 | ; * The structure describes transfers to be performed on Control, Bulk or |
77 | ; Interrupt endpoints. |
77 | ; Interrupt endpoints. |
78 | ; * The structure includes two parts, the hardware part and the software part. |
78 | ; * The structure includes two parts, the hardware part and the software part. |
79 | ; * The hardware part consists of first 52 bytes and corresponds to |
79 | ; * The hardware part consists of first 52 bytes and corresponds to |
80 | ; the Queue Element Transfer Descriptor from EHCI specification. |
80 | ; the Queue Element Transfer Descriptor from EHCI specification. |
81 | ; * The hardware requires 32-bytes alignment of the hardware part, so |
81 | ; * The hardware requires 32-bytes alignment of the hardware part, so |
82 | ; the entire descriptor must be 32-bytes aligned. Since the allocator |
82 | ; the entire descriptor must be 32-bytes aligned. Since the allocator |
83 | ; (usb_allocate_common) allocates memory sequentially from page start |
83 | ; (usb_allocate_common) allocates memory sequentially from page start |
84 | ; (aligned on 0x1000 bytes), block size for the allocator must be divisible |
84 | ; (aligned on 0x1000 bytes), block size for the allocator must be divisible |
85 | ; by 32; ehci_alloc_td ensures this. |
85 | ; by 32; ehci_alloc_td ensures this. |
86 | ; * The hardware also requires that the hardware part must not cross page |
86 | ; * The hardware also requires that the hardware part must not cross page |
87 | ; boundary; the allocator satisfies this automatically. |
87 | ; boundary; the allocator satisfies this automatically. |
88 | struct ehci_gtd ehci_hardware_td |
88 | struct ehci_gtd ehci_hardware_td |
89 | Flags dd ? |
89 | Flags dd ? |
90 | ; Copy of flags from the call to usb_*_transfer_async. |
90 | ; Copy of flags from the call to usb_*_transfer_async. |
91 | ends |
91 | ends |
92 | 92 | ||
93 | ; EHCI-specific part of a pipe descriptor. |
93 | ; EHCI-specific part of a pipe descriptor. |
94 | ; * This structure corresponds to the Queue Head from the EHCI specification. |
94 | ; * This structure corresponds to the Queue Head from the EHCI specification. |
95 | ; * The hardware requires 32-bytes alignment of the hardware part. |
95 | ; * The hardware requires 32-bytes alignment of the hardware part. |
96 | ; Since the allocator (usb_allocate_common) allocates memory sequentially |
96 | ; Since the allocator (usb_allocate_common) allocates memory sequentially |
97 | ; from page start (aligned on 0x1000 bytes), block size for the allocator |
97 | ; from page start (aligned on 0x1000 bytes), block size for the allocator |
98 | ; must be divisible by 32; ehci_alloc_pipe ensures this. |
98 | ; must be divisible by 32; ehci_alloc_pipe ensures this. |
99 | ; * The hardware requires also that the hardware part must not cross page |
99 | ; * The hardware requires also that the hardware part must not cross page |
100 | ; boundary; the allocator satisfies this automatically. |
100 | ; boundary; the allocator satisfies this automatically. |
101 | struct ehci_pipe |
101 | struct ehci_pipe |
102 | NextQH dd ? |
102 | NextQH dd ? |
103 | ; 1. First bit (bit 0) is Terminate bit, 1 = there is no next QH. |
103 | ; 1. First bit (bit 0) is Terminate bit, 1 = there is no next QH. |
104 | ; 2. Next two bits (bits 1-2) are Type field of the next QH, |
104 | ; 2. Next two bits (bits 1-2) are Type field of the next QH, |
105 | ; one of EHCI_TYPE_* constants. |
105 | ; one of EHCI_TYPE_* constants. |
106 | ; 3. Next two bits (bits 3-4) are reserved, must be zero. |
106 | ; 3. Next two bits (bits 3-4) are reserved, must be zero. |
107 | ; 4. With masked 5 lower bits, this is the physical address of the next object |
107 | ; 4. With masked 5 lower bits, this is the physical address of the next object |
108 | ; to be processed, usually next QH. |
108 | ; to be processed, usually next QH. |
109 | Token dd ? |
109 | Token dd ? |
110 | ; 1. Lower 7 bits are DeviceAddress field. This is the address of the |
110 | ; 1. Lower 7 bits are DeviceAddress field. This is the address of the |
111 | ; target device on the USB bus. |
111 | ; target device on the USB bus. |
112 | ; 2. Next bit (bit 7) is Inactivate-on-next-transaction bit. Can be nonzero |
112 | ; 2. Next bit (bit 7) is Inactivate-on-next-transaction bit. Can be nonzero |
113 | ; only for interrupt/isochronous USB1 endpoints. |
113 | ; only for interrupt/isochronous USB1 endpoints. |
114 | ; 3. Next 4 bits (bits 8-11) are Endpoint field. This is the target endpoint |
114 | ; 3. Next 4 bits (bits 8-11) are Endpoint field. This is the target endpoint |
115 | ; number. |
115 | ; number. |
116 | ; 4. Next 2 bits (bits 12-13) are EndpointSpeed field, one of EHCI_SPEED_*. |
116 | ; 4. Next 2 bits (bits 12-13) are EndpointSpeed field, one of EHCI_SPEED_*. |
117 | ; 5. Next bit (bit 14) is DataToggleControl bit, |
117 | ; 5. Next bit (bit 14) is DataToggleControl bit, |
118 | ; 0 = use DataToggle bit from QH, 1 = from TD. |
118 | ; 0 = use DataToggle bit from QH, 1 = from TD. |
119 | ; 6. Next bit (bit 15) is Head-of-reclamation-list. The head of Control list |
119 | ; 6. Next bit (bit 15) is Head-of-reclamation-list. The head of Control list |
120 | ; has 1 here, all other QHs have zero. |
120 | ; has 1 here, all other QHs have zero. |
121 | ; 7. Next 11 bits (bits 16-26) are MaximumPacketLength field for the target |
121 | ; 7. Next 11 bits (bits 16-26) are MaximumPacketLength field for the target |
122 | ; endpoint. |
122 | ; endpoint. |
123 | ; 8. Next bit (bit 27) is ControlEndpoint bit, must be 1 for USB1 control |
123 | ; 8. Next bit (bit 27) is ControlEndpoint bit, must be 1 for USB1 control |
124 | ; endpoints and 0 for all others. |
124 | ; endpoints and 0 for all others. |
125 | ; 9. Upper 4 bits (bits 28-31) are NakCountReload field. |
125 | ; 9. Upper 4 bits (bits 28-31) are NakCountReload field. |
126 | ; Zero for USB1 endpoints, zero for periodic endpoints. |
126 | ; Zero for USB1 endpoints, zero for periodic endpoints. |
127 | ; For control/bulk USB2 endpoints, the code sets it to 4, |
127 | ; For control/bulk USB2 endpoints, the code sets it to 4, |
128 | ; which is rather arbitrary. |
128 | ; which is rather arbitrary. |
129 | Flags dd ? |
129 | Flags dd ? |
130 | ; 1. Lower byte is S-mask, each bit corresponds to one microframe per frame; |
130 | ; 1. Lower byte is S-mask, each bit corresponds to one microframe per frame; |
131 | ; bit is set <=> enable transactions in this microframe. |
131 | ; bit is set <=> enable transactions in this microframe. |
132 | ; 2. Next byte is C-mask, each bit corresponds to one microframe per frame; |
132 | ; 2. Next byte is C-mask, each bit corresponds to one microframe per frame; |
133 | ; bit is set <=> enable complete-split transactions in this microframe. |
133 | ; bit is set <=> enable complete-split transactions in this microframe. |
134 | ; Meaningful only for USB1 endpoints. |
134 | ; Meaningful only for USB1 endpoints. |
135 | ; 3. Next 14 bits give address of the target device as hub:port, bits 16-22 |
135 | ; 3. Next 14 bits give address of the target device as hub:port, bits 16-22 |
136 | ; are the USB address of the hub, bits 23-29 are the port number. |
136 | ; are the USB address of the hub, bits 23-29 are the port number. |
137 | ; Meaningful only for USB1 endpoints. |
137 | ; Meaningful only for USB1 endpoints. |
138 | ; 4. Upper 2 bits define number of consequetive transactions per micro-frame |
138 | ; 4. Upper 2 bits define number of consequetive transactions per micro-frame |
139 | ; which host is allowed to permit for this endpoint. |
139 | ; which host is allowed to permit for this endpoint. |
140 | ; For control/bulk endpoints, it must be 1. |
140 | ; For control/bulk endpoints, it must be 1. |
141 | ; For periodic endpoints, the value is taken from the endpoint descriptor. |
141 | ; For periodic endpoints, the value is taken from the endpoint descriptor. |
142 | HeadTD dd ? |
142 | HeadTD dd ? |
143 | ; The physical address of the first TD for this pipe. |
143 | ; The physical address of the first TD for this pipe. |
144 | ; Lower 5 bits must be zero. |
144 | ; Lower 5 bits must be zero. |
145 | Overlay ehci_hardware_td ? |
145 | Overlay ehci_hardware_td ? |
146 | ; Working area for the current TD, if there is any. |
146 | ; Working area for the current TD, if there is any. |
147 | ; When TD is retired, it is written to that TD and Overlay is loaded |
147 | ; When TD is retired, it is written to that TD and Overlay is loaded |
148 | ; from the new TD, if any. |
148 | ; from the new TD, if any. |
149 | BaseList dd ? |
149 | BaseList dd ? |
150 | ; Pointer to head of the corresponding pipe list. |
150 | ; Pointer to head of the corresponding pipe list. |
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 | ; The hardware requires 32-bytes alignment of this structure. |
154 | ; The hardware requires 32-bytes alignment of this structure. |
155 | ; All instances of this structure are located sequentially in ehci_controller, |
155 | ; All instances of this structure are located sequentially in ehci_controller, |
156 | ; ehci_controller is page-aligned, so it is sufficient to make this structure |
156 | ; ehci_controller is page-aligned, so it is sufficient to make this structure |
157 | ; 32-bytes aligned and verify that the first instance is 32-bytes aligned |
157 | ; 32-bytes aligned and verify that the first instance is 32-bytes aligned |
158 | ; inside ehci_controller. |
158 | ; inside ehci_controller. |
159 | ; The hardware also requires that 44h bytes (size of 64-bit Queue Head |
159 | ; The hardware also requires that 44h bytes (size of 64-bit Queue Head |
160 | ; Descriptor) starting at the beginning of this structure must not cross page |
160 | ; Descriptor) starting at the beginning of this structure must not cross page |
161 | ; boundary. If not, most hardware still behaves correctly (in fact, the last |
161 | ; boundary. If not, most hardware still behaves correctly (in fact, the last |
162 | ; dword can have any value and this structure is never written), but on some |
162 | ; dword can have any value and this structure is never written), but on some |
163 | ; hardware some things just break in mysterious ways. |
163 | ; hardware some things just break in mysterious ways. |
164 | struct ehci_static_ep |
164 | struct ehci_static_ep |
165 | ; Hardware fields are the same as in ehci_pipe. |
165 | ; Hardware fields are the same as in ehci_pipe. |
166 | ; Only NextQH and Overlay.Token are actually used. |
166 | ; Only NextQH and Overlay.Token are actually used. |
167 | ; NB: some emulators ignore Token.Halted bit (probably assuming that it is set |
167 | ; NB: some emulators ignore Token.Halted bit (probably assuming that it is set |
168 | ; only when device fails and emulation never fails) and always follow |
168 | ; only when device fails and emulation never fails) and always follow |
169 | ; [Alternate]NextTD when they see that OverlayToken.Active bit is zero; |
169 | ; [Alternate]NextTD when they see that OverlayToken.Active bit is zero; |
170 | ; so it is important to also set [Alternate]NextTD to 1. |
170 | ; so it is important to also set [Alternate]NextTD to 1. |
171 | NextQH dd ? |
171 | NextQH dd ? |
172 | Token dd ? |
172 | Token dd ? |
173 | Flags dd ? |
173 | Flags dd ? |
174 | HeadTD dd ? |
174 | HeadTD dd ? |
175 | NextTD dd ? |
175 | NextTD dd ? |
176 | AlternateNextTD dd ? |
176 | AlternateNextTD dd ? |
177 | OverlayToken dd ? |
177 | OverlayToken dd ? |
178 | NextList dd ? |
178 | NextList dd ? |
179 | SoftwarePart rd sizeof.usb_static_ep/4 |
179 | SoftwarePart rd sizeof.usb_static_ep/4 |
180 | Bandwidths rw 8 |
180 | Bandwidths rw 8 |
181 | dd ? |
181 | dd ? |
182 | ends |
182 | ends |
183 | 183 | ||
184 | if sizeof.ehci_static_ep mod 32 |
184 | if sizeof.ehci_static_ep mod 32 |
185 | .err ehci_static_ep must be 32-bytes aligned |
185 | .err ehci_static_ep must be 32-bytes aligned |
186 | end if |
186 | end if |
187 | 187 | ||
188 | if ehci_static_ep.OverlayToken <> ehci_pipe.Overlay.Token |
188 | if ehci_static_ep.OverlayToken <> ehci_pipe.Overlay.Token |
189 | .err ehci_static_ep.OverlayToken misplaced |
189 | .err ehci_static_ep.OverlayToken misplaced |
190 | end if |
190 | end if |
191 | 191 | ||
192 | ; EHCI-specific part of controller data. |
192 | ; EHCI-specific part of controller data. |
193 | ; * The structure includes two parts, the hardware part and the software part. |
193 | ; * The structure includes two parts, the hardware part and the software part. |
194 | ; * The hardware part consists of first 4096 bytes and corresponds to |
194 | ; * The hardware part consists of first 4096 bytes and corresponds to |
195 | ; the Periodic Frame List from the EHCI specification. |
195 | ; the Periodic Frame List from the EHCI specification. |
196 | ; * The hardware requires page-alignment of the hardware part, so |
196 | ; * The hardware requires page-alignment of the hardware part, so |
197 | ; the entire descriptor must be page-aligned. |
197 | ; the entire descriptor must be page-aligned. |
198 | ; This structure is allocated with kernel_alloc (see usb_init_controller), |
198 | ; This structure is allocated with kernel_alloc (see usb_init_controller), |
199 | ; this gives page-aligned data. |
199 | ; this gives page-aligned data. |
200 | ; * The controller is described by both ehci_controller and usb_controller |
200 | ; * The controller is described by both ehci_controller and usb_controller |
201 | ; structures, for each controller there is one ehci_controller and one |
201 | ; structures, for each controller there is one ehci_controller and one |
202 | ; usb_controller structure. These structures are located sequentially |
202 | ; usb_controller structure. These structures are located sequentially |
203 | ; in the memory: beginning from some page start, there is ehci_controller |
203 | ; in the memory: beginning from some page start, there is ehci_controller |
204 | ; structure - this enforces hardware alignment requirements - and then |
204 | ; structure - this enforces hardware alignment requirements - and then |
205 | ; usb_controller structure. |
205 | ; usb_controller structure. |
206 | ; * The code keeps pointer to usb_controller structure. The ehci_controller |
206 | ; * The code keeps pointer to usb_controller structure. The ehci_controller |
207 | ; structure is addressed as [ptr + ehci_controller.field - sizeof.ehci_controller]. |
207 | ; structure is addressed as [ptr + ehci_controller.field - sizeof.ehci_controller]. |
208 | struct ehci_controller |
208 | struct ehci_controller |
209 | ; ------------------------------ hardware fields ------------------------------ |
209 | ; ------------------------------ hardware fields ------------------------------ |
210 | FrameList rd 1024 |
210 | FrameList rd 1024 |
211 | ; Entry n corresponds to the head of the frame list to be executed in |
211 | ; Entry n corresponds to the head of the frame list to be executed in |
212 | ; the frames n,n+1024,n+2048,n+3072,... |
212 | ; the frames n,n+1024,n+2048,n+3072,... |
213 | ; The first bit of each entry is Terminate bit, 1 = the frame is empty. |
213 | ; The first bit of each entry is Terminate bit, 1 = the frame is empty. |
214 | ; Bits 1-2 are Type field, one of EHCI_TYPE_* constants. |
214 | ; Bits 1-2 are Type field, one of EHCI_TYPE_* constants. |
215 | ; Bits 3-4 must be zero. |
215 | ; Bits 3-4 must be zero. |
216 | ; With masked 5 lower bits, the entry is a physical address of the first QH/TD |
216 | ; With masked 5 lower bits, the entry is a physical address of the first QH/TD |
217 | ; to be executed. |
217 | ; to be executed. |
218 | ; ------------------------------ software fields ------------------------------ |
218 | ; ------------------------------ software fields ------------------------------ |
219 | ; Every list has the static head, which is an always halted QH. |
219 | ; Every list has the static head, which is an always halted QH. |
220 | ; The following fields are static heads, one per list: |
220 | ; The following fields are static heads, one per list: |
221 | ; 32+16+8+4+2+1 = 63 for Periodic lists, 1 for Control list and 1 for Bulk list. |
221 | ; 32+16+8+4+2+1 = 63 for Periodic lists, 1 for Control list and 1 for Bulk list. |
222 | IntEDs ehci_static_ep |
222 | IntEDs ehci_static_ep |
223 | rb 62 * sizeof.ehci_static_ep |
223 | rb 62 * sizeof.ehci_static_ep |
224 | ; Beware. |
224 | ; Beware. |
225 | ; Two following strings ensure that 44h bytes at any static head |
225 | ; Two following strings ensure that 44h bytes at any static head |
226 | ; do not cross page boundary. Without that, the code "works on my machine"... |
226 | ; do not cross page boundary. Without that, the code "works on my machine"... |
227 | ; but fails on some hardware in seemingly unrelated ways. |
227 | ; but fails on some hardware in seemingly unrelated ways. |
228 | ; One hardware TD (without any software fields) fit in the rest of the page. |
228 | ; One hardware TD (without any software fields) fit in the rest of the page. |
229 | ehci_controller.ControlDelta = 2000h - (ehci_controller.IntEDs + 63 * sizeof.ehci_static_ep) |
229 | ehci_controller.ControlDelta = 2000h - (ehci_controller.IntEDs + 63 * sizeof.ehci_static_ep) |
230 | StopQueueTD ehci_hardware_td |
230 | StopQueueTD ehci_hardware_td |
231 | ; Used as AlternateNextTD for transfers when short packet is considered |
231 | ; Used as AlternateNextTD for transfers when short packet is considered |
232 | ; as an error; short packet must stop the queue in this case, not advance |
232 | ; as an error; short packet must stop the queue in this case, not advance |
233 | ; to the next transfer. |
233 | ; to the next transfer. |
234 | rb ehci_controller.ControlDelta - sizeof.ehci_hardware_td |
234 | rb ehci_controller.ControlDelta - sizeof.ehci_hardware_td |
235 | ; Padding for page-alignment. |
235 | ; Padding for page-alignment. |
236 | ControlED ehci_static_ep |
236 | ControlED ehci_static_ep |
237 | BulkED ehci_static_ep |
237 | BulkED ehci_static_ep |
238 | MMIOBase1 dd ? |
238 | MMIOBase1 dd ? |
239 | ; Virtual address of memory-mapped area with part 1 of EHCI registers EhciXxxReg. |
239 | ; Virtual address of memory-mapped area with part 1 of EHCI registers EhciXxxReg. |
240 | MMIOBase2 dd ? |
240 | MMIOBase2 dd ? |
241 | ; Pointer inside memory-mapped area MMIOBase1; points to part 2 of EHCI registers. |
241 | ; Pointer inside memory-mapped area MMIOBase1; points to part 2 of EHCI registers. |
242 | StructuralParams dd ? |
242 | StructuralParams dd ? |
243 | ; Copy of EhciStructParamsReg value. |
243 | ; Copy of EhciStructParamsReg value. |
244 | CapabilityParams dd ? |
244 | CapabilityParams dd ? |
245 | ; Copy of EhciCapParamsReg value. |
245 | ; Copy of EhciCapParamsReg value. |
246 | DeferredActions dd ? |
246 | DeferredActions dd ? |
247 | ; Bitmask of events from EhciStatusReg which were observed by the IRQ handler |
247 | ; Bitmask of events from EhciStatusReg which were observed by the IRQ handler |
248 | ; and needs to be processed in the IRQ thread. |
248 | ; and needs to be processed in the IRQ thread. |
249 | ends |
249 | ends |
250 | 250 | ||
251 | if ehci_controller.IntEDs mod 32 |
251 | if ehci_controller.IntEDs mod 32 |
252 | .err Static endpoint descriptors must be 32-bytes aligned inside ehci_controller |
252 | .err Static endpoint descriptors must be 32-bytes aligned inside ehci_controller |
253 | end if |
253 | end if |
254 | 254 | ||
255 | ; Description of #HCI-specific data and functions for |
255 | ; Description of #HCI-specific data and functions for |
256 | ; controller-independent code. |
256 | ; controller-independent code. |
257 | ; Implements the structure usb_hardware_func from hccommon.inc for EHCI. |
257 | ; Implements the structure usb_hardware_func from hccommon.inc for EHCI. |
258 | iglobal |
258 | iglobal |
259 | align 4 |
259 | align 4 |
260 | ehci_hardware_func: |
260 | ehci_hardware_func: |
261 | dd 'EHCI' |
261 | dd 'EHCI' |
262 | dd sizeof.ehci_controller |
262 | dd sizeof.ehci_controller |
263 | dd ehci_init |
263 | dd ehci_init |
264 | dd ehci_process_deferred |
264 | dd ehci_process_deferred |
265 | dd ehci_set_device_address |
265 | dd ehci_set_device_address |
266 | dd ehci_get_device_address |
266 | dd ehci_get_device_address |
267 | dd ehci_port_disable |
267 | dd ehci_port_disable |
268 | dd ehci_new_port.reset |
268 | dd ehci_new_port.reset |
269 | dd ehci_set_endpoint_packet_size |
269 | dd ehci_set_endpoint_packet_size |
270 | dd ehci_alloc_pipe |
270 | dd ehci_alloc_pipe |
271 | dd ehci_free_pipe |
271 | dd ehci_free_pipe |
272 | dd ehci_init_pipe |
272 | dd ehci_init_pipe |
273 | dd ehci_unlink_pipe |
273 | dd ehci_unlink_pipe |
274 | dd ehci_alloc_td |
274 | dd ehci_alloc_td |
275 | dd ehci_free_td |
275 | dd ehci_free_td |
276 | dd ehci_alloc_transfer |
276 | dd ehci_alloc_transfer |
277 | dd ehci_insert_transfer |
277 | dd ehci_insert_transfer |
278 | dd ehci_new_device |
278 | dd ehci_new_device |
279 | endg |
279 | endg |
280 | 280 | ||
281 | ; ============================================================================= |
281 | ; ============================================================================= |
282 | ; =================================== Code ==================================== |
282 | ; =================================== Code ==================================== |
283 | ; ============================================================================= |
283 | ; ============================================================================= |
284 | 284 | ||
285 | ; Controller-specific initialization function. |
285 | ; Controller-specific initialization function. |
286 | ; Called from usb_init_controller. Initializes the hardware and |
286 | ; Called from usb_init_controller. Initializes the hardware and |
287 | ; EHCI-specific parts of software structures. |
287 | ; EHCI-specific parts of software structures. |
288 | ; eax = pointer to ehci_controller to be initialized |
288 | ; eax = pointer to ehci_controller to be initialized |
289 | ; [ebp-4] = pcidevice |
289 | ; [ebp-4] = pcidevice |
290 | proc ehci_init |
290 | proc ehci_init |
291 | ; inherit some variables from the parent (usb_init_controller) |
291 | ; inherit some variables from the parent (usb_init_controller) |
292 | .devfn equ ebp - 4 |
292 | .devfn equ ebp - 4 |
293 | .bus equ ebp - 3 |
293 | .bus equ ebp - 3 |
294 | ; 1. Store pointer to ehci_controller for further use. |
294 | ; 1. Store pointer to ehci_controller for further use. |
295 | push eax |
295 | push eax |
296 | mov edi, eax |
296 | mov edi, eax |
297 | mov esi, eax |
297 | mov esi, eax |
298 | ; 2. Initialize ehci_controller.FrameList. |
298 | ; 2. Initialize ehci_controller.FrameList. |
299 | ; Note that FrameList is located in the beginning of ehci_controller, |
299 | ; Note that FrameList is located in the beginning of ehci_controller, |
300 | ; so esi and edi now point to ehci_controller.FrameList. |
300 | ; so esi and edi now point to ehci_controller.FrameList. |
301 | ; First 32 entries of FrameList contain physical addresses |
301 | ; First 32 entries of FrameList contain physical addresses |
302 | ; of first 32 Periodic static heads, further entries duplicate these. |
302 | ; of first 32 Periodic static heads, further entries duplicate these. |
303 | ; See the description of structures for full info. |
303 | ; See the description of structures for full info. |
304 | ; 2a. Get physical address of first static head. |
304 | ; 2a. Get physical address of first static head. |
305 | ; Note that 1) it is located in the beginning of a page |
305 | ; Note that 1) it is located in the beginning of a page |
306 | ; and 2) first 32 static heads fit in the same page, |
306 | ; and 2) first 32 static heads fit in the same page, |
307 | ; so one call to get_phys_addr without correction of lower 12 bits |
307 | ; so one call to get_phys_addr without correction of lower 12 bits |
308 | ; is sufficient. |
308 | ; is sufficient. |
309 | if (ehci_controller.IntEDs / 0x1000) <> ((ehci_controller.IntEDs + 32 * sizeof.ehci_static_ep) / 0x1000) |
309 | if (ehci_controller.IntEDs / 0x1000) <> ((ehci_controller.IntEDs + 32 * sizeof.ehci_static_ep) / 0x1000) |
310 | .err assertion failed |
310 | .err assertion failed |
311 | end if |
311 | end if |
312 | if (ehci_controller.IntEDs mod 0x1000) <> 0 |
312 | if (ehci_controller.IntEDs mod 0x1000) <> 0 |
313 | .err assertion failed |
313 | .err assertion failed |
314 | end if |
314 | end if |
315 | add eax, ehci_controller.IntEDs |
315 | add eax, ehci_controller.IntEDs |
316 | call get_phys_addr |
316 | call get_phys_addr |
317 | ; 2b. Fill first 32 entries. |
317 | ; 2b. Fill first 32 entries. |
318 | inc eax |
318 | inc eax |
319 | inc eax ; set Type to EHCI_TYPE_QH |
319 | inc eax ; set Type to EHCI_TYPE_QH |
320 | movi ecx, 32 |
320 | movi ecx, 32 |
321 | mov edx, ecx |
321 | mov edx, ecx |
322 | @@: |
322 | @@: |
323 | stosd |
323 | stosd |
324 | add eax, sizeof.ehci_static_ep |
324 | add eax, sizeof.ehci_static_ep |
325 | loop @b |
325 | loop @b |
326 | ; 2c. Fill the rest entries. |
326 | ; 2c. Fill the rest entries. |
327 | mov ecx, 1024 - 32 |
327 | mov ecx, 1024 - 32 |
328 | rep movsd |
328 | rep movsd |
329 | ; 3. Initialize static heads ehci_controller.*ED. |
329 | ; 3. Initialize static heads ehci_controller.*ED. |
330 | ; Use the loop over groups: first group consists of first 32 Periodic |
330 | ; Use the loop over groups: first group consists of first 32 Periodic |
331 | ; descriptors, next group consists of next 16 Periodic descriptors, |
331 | ; descriptors, next group consists of next 16 Periodic descriptors, |
332 | ; ..., last group consists of the last Periodic descriptor. |
332 | ; ..., last group consists of the last Periodic descriptor. |
333 | ; 3a. Prepare for the loop. |
333 | ; 3a. Prepare for the loop. |
334 | ; make esi point to the second group, other registers are already set. |
334 | ; make esi point to the second group, other registers are already set. |
335 | add esi, 32*4 + 32*sizeof.ehci_static_ep |
335 | add esi, 32*4 + 32*sizeof.ehci_static_ep |
336 | ; 3b. Loop over groups. On every iteration: |
336 | ; 3b. Loop over groups. On every iteration: |
337 | ; edx = size of group, edi = pointer to the current group, |
337 | ; edx = size of group, edi = pointer to the current group, |
338 | ; esi = pointer to the next group. |
338 | ; esi = pointer to the next group. |
339 | .init_static_eds: |
339 | .init_static_eds: |
340 | ; 3c. Get the size of next group. |
340 | ; 3c. Get the size of next group. |
341 | shr edx, 1 |
341 | shr edx, 1 |
342 | ; 3d. Exit the loop if there is no next group. |
342 | ; 3d. Exit the loop if there is no next group. |
343 | jz .init_static_eds_done |
343 | jz .init_static_eds_done |
344 | ; 3e. Initialize the first half of the current group. |
344 | ; 3e. Initialize the first half of the current group. |
345 | ; Advance edi to the second half. |
345 | ; Advance edi to the second half. |
346 | push esi |
346 | push esi |
347 | call ehci_init_static_ep_group |
347 | call ehci_init_static_ep_group |
348 | pop esi |
348 | pop esi |
349 | ; 3f. Initialize the second half of the current group |
349 | ; 3f. Initialize the second half of the current group |
350 | ; with the same values. |
350 | ; with the same values. |
351 | ; Advance edi to the next group, esi/eax to the next of the next group. |
351 | ; Advance edi to the next group, esi/eax to the next of the next group. |
352 | call ehci_init_static_ep_group |
352 | call ehci_init_static_ep_group |
353 | jmp .init_static_eds |
353 | jmp .init_static_eds |
354 | .init_static_eds_done: |
354 | .init_static_eds_done: |
355 | ; 3g. Initialize the last static head. |
355 | ; 3g. Initialize the last static head. |
356 | xor esi, esi |
356 | xor esi, esi |
357 | call ehci_init_static_endpoint |
357 | call ehci_init_static_endpoint |
358 | ; While we are here, initialize StopQueueTD. |
358 | ; While we are here, initialize StopQueueTD. |
359 | if (ehci_controller.StopQueueTD <> ehci_controller.IntEDs + 63 * sizeof.ehci_static_ep) |
359 | if (ehci_controller.StopQueueTD <> ehci_controller.IntEDs + 63 * sizeof.ehci_static_ep) |
360 | .err assertion failed |
360 | .err assertion failed |
361 | end if |
361 | end if |
362 | inc [edi+ehci_hardware_td.NextTD] ; 0 -> 1 |
362 | inc [edi+ehci_hardware_td.NextTD] ; 0 -> 1 |
363 | inc [edi+ehci_hardware_td.AlternateNextTD] ; 0 -> 1 |
363 | inc [edi+ehci_hardware_td.AlternateNextTD] ; 0 -> 1 |
364 | ; leave other fields as zero, including Active bit |
364 | ; leave other fields as zero, including Active bit |
365 | ; 3i. Initialize the head of Control list. |
365 | ; 3i. Initialize the head of Control list. |
366 | add edi, ehci_controller.ControlDelta |
366 | add edi, ehci_controller.ControlDelta |
367 | lea esi, [edi+sizeof.ehci_static_ep] |
367 | lea esi, [edi+sizeof.ehci_static_ep] |
368 | call ehci_init_static_endpoint |
368 | call ehci_init_static_endpoint |
369 | or byte [edi-sizeof.ehci_static_ep+ehci_static_ep.Token+1], 80h |
369 | or byte [edi-sizeof.ehci_static_ep+ehci_static_ep.Token+1], 80h |
370 | ; 3j. Initialize the head of Bulk list. |
370 | ; 3j. Initialize the head of Bulk list. |
371 | sub esi, sizeof.ehci_static_ep |
371 | sub esi, sizeof.ehci_static_ep |
372 | call ehci_init_static_endpoint |
372 | call ehci_init_static_endpoint |
373 | ; 4. Create a virtual memory area to talk with the controller. |
373 | ; 4. Create a virtual memory area to talk with the controller. |
374 | ; 4a. Enable memory & bus master access. |
374 | ; 4a. Enable memory & bus master access. |
375 | mov ch, [.bus] |
375 | mov ch, [.bus] |
376 | mov cl, 1 |
376 | mov cl, 1 |
377 | mov eax, ecx |
377 | mov eax, ecx |
378 | mov bh, [.devfn] |
378 | mov bh, [.devfn] |
379 | mov bl, 4 |
379 | mov bl, 4 |
380 | call pci_read_reg |
380 | call pci_read_reg |
381 | or al, 6 |
381 | or al, 6 |
382 | xchg eax, ecx |
382 | xchg eax, ecx |
383 | call pci_write_reg |
383 | call pci_write_reg |
384 | ; 4b. Read memory base address. |
384 | ; 4b. Read memory base address. |
385 | mov ah, [.bus] |
385 | mov ah, [.bus] |
386 | mov al, 2 |
386 | mov al, 2 |
387 | mov bl, 10h |
387 | mov bl, 10h |
388 | call pci_read_reg |
388 | call pci_read_reg |
389 | ; DEBUGF 1,'K : phys MMIO %x\n',eax |
389 | ; DEBUGF 1,'K : phys MMIO %x\n',eax |
390 | and al, not 0Fh |
390 | and al, not 0Fh |
391 | ; 4c. Create mapping for physical memory. 200h bytes are always sufficient. |
391 | ; 4c. Create mapping for physical memory. 200h bytes are always sufficient. |
392 | stdcall map_io_mem, eax, 200h, PG_SW+PG_NOCACHE |
392 | stdcall map_io_mem, eax, 200h, PG_SW+PG_NOCACHE |
393 | test eax, eax |
393 | test eax, eax |
394 | jz .fail |
394 | jz .fail |
395 | ; DEBUGF 1,'K : MMIO %x\n',eax |
395 | ; DEBUGF 1,'K : MMIO %x\n',eax |
396 | if ehci_controller.MMIOBase1 <> ehci_controller.BulkED + sizeof.ehci_static_ep |
396 | if ehci_controller.MMIOBase1 <> ehci_controller.BulkED + sizeof.ehci_static_ep |
397 | .err assertion failed |
397 | .err assertion failed |
398 | end if |
398 | end if |
399 | stosd ; fill ehci_controller.MMIOBase1 |
399 | stosd ; fill ehci_controller.MMIOBase1 |
400 | movzx ecx, byte [eax+EhciCapLengthReg] |
400 | movzx ecx, byte [eax+EhciCapLengthReg] |
401 | mov edx, [eax+EhciCapParamsReg] |
401 | mov edx, [eax+EhciCapParamsReg] |
402 | mov ebx, [eax+EhciStructParamsReg] |
402 | mov ebx, [eax+EhciStructParamsReg] |
403 | add eax, ecx |
403 | add eax, ecx |
404 | if ehci_controller.MMIOBase2 <> ehci_controller.MMIOBase1 + 4 |
404 | if ehci_controller.MMIOBase2 <> ehci_controller.MMIOBase1 + 4 |
405 | .err assertion failed |
405 | .err assertion failed |
406 | end if |
406 | end if |
407 | stosd ; fill ehci_controller.MMIOBase2 |
407 | stosd ; fill ehci_controller.MMIOBase2 |
408 | if ehci_controller.StructuralParams <> ehci_controller.MMIOBase2 + 4 |
408 | if ehci_controller.StructuralParams <> ehci_controller.MMIOBase2 + 4 |
409 | .err assertion failed |
409 | .err assertion failed |
410 | end if |
410 | end if |
411 | if ehci_controller.CapabilityParams <> ehci_controller.StructuralParams + 4 |
411 | if ehci_controller.CapabilityParams <> ehci_controller.StructuralParams + 4 |
412 | .err assertion failed |
412 | .err assertion failed |
413 | end if |
413 | end if |
414 | mov [edi], ebx ; fill ehci_controller.StructuralParams |
414 | mov [edi], ebx ; fill ehci_controller.StructuralParams |
415 | mov [edi+4], edx ; fill ehci_controller.CapabilityParams |
415 | mov [edi+4], edx ; fill ehci_controller.CapabilityParams |
416 | DEBUGF 1,'K : HCSPARAMS=%x, HCCPARAMS=%x\n',ebx,edx |
416 | DEBUGF 1,'K : HCSPARAMS=%x, HCCPARAMS=%x\n',ebx,edx |
417 | and ebx, 15 |
417 | and ebx, 15 |
418 | mov [edi+usb_controller.NumPorts+sizeof.ehci_controller-ehci_controller.StructuralParams], ebx |
418 | mov [edi+usb_controller.NumPorts+sizeof.ehci_controller-ehci_controller.StructuralParams], ebx |
419 | mov edi, eax |
419 | mov edi, eax |
420 | ; now edi = MMIOBase2 |
420 | ; now edi = MMIOBase2 |
421 | ; 6. Transfer the controller to a known state. |
421 | ; 6. Transfer the controller to a known state. |
422 | ; 6b. Stop the controller if it is running. |
422 | ; 6b. Stop the controller if it is running. |
423 | movi ecx, 10 |
423 | movi ecx, 10 |
424 | test dword [edi+EhciStatusReg], 1 shl 12 |
424 | test dword [edi+EhciStatusReg], 1 shl 12 |
425 | jnz .stopped |
425 | jnz .stopped |
426 | and dword [edi+EhciCommandReg], not 1 |
426 | and dword [edi+EhciCommandReg], not 1 |
427 | @@: |
427 | @@: |
428 | movi esi, 1 |
428 | movi esi, 1 |
429 | call delay_ms |
429 | call delay_ms |
430 | test dword [edi+EhciStatusReg], 1 shl 12 |
430 | test dword [edi+EhciStatusReg], 1 shl 12 |
431 | jnz .stopped |
431 | jnz .stopped |
432 | loop @b |
432 | loop @b |
433 | dbgstr 'Failed to stop EHCI controller' |
433 | dbgstr 'Failed to stop EHCI controller' |
434 | jmp .fail_unmap |
434 | jmp .fail_unmap |
435 | .stopped: |
435 | .stopped: |
436 | ; 6c. Reset the controller. Wait up to 50 ms checking status every 1 ms. |
436 | ; 6c. Reset the controller. Wait up to 50 ms checking status every 1 ms. |
437 | or dword [edi+EhciCommandReg], 2 |
437 | or dword [edi+EhciCommandReg], 2 |
438 | movi ecx, 50 |
438 | movi ecx, 50 |
439 | @@: |
439 | @@: |
440 | movi esi, 1 |
440 | movi esi, 1 |
441 | call delay_ms |
441 | call delay_ms |
442 | test dword [edi+EhciCommandReg], 2 |
442 | test dword [edi+EhciCommandReg], 2 |
443 | jz .reset_ok |
443 | jz .reset_ok |
444 | loop @b |
444 | loop @b |
445 | dbgstr 'Failed to reset EHCI controller' |
445 | dbgstr 'Failed to reset EHCI controller' |
446 | jmp .fail_unmap |
446 | jmp .fail_unmap |
447 | .reset_ok: |
447 | .reset_ok: |
448 | ; 7. Configure the controller. |
448 | ; 7. Configure the controller. |
449 | pop esi ; restore the pointer saved at step 1 |
449 | pop esi ; restore the pointer saved at step 1 |
450 | add esi, sizeof.ehci_controller |
450 | add esi, sizeof.ehci_controller |
451 | ; 7a. If the controller is 64-bit, say to it that all structures are located |
451 | ; 7a. If the controller is 64-bit, say to it that all structures are located |
452 | ; in first 4G. |
452 | ; in first 4G. |
453 | test byte [esi+ehci_controller.CapabilityParams-sizeof.ehci_controller], 1 |
453 | test byte [esi+ehci_controller.CapabilityParams-sizeof.ehci_controller], 1 |
454 | jz @f |
454 | jz @f |
455 | mov dword [edi+EhciCtrlDataSegReg], 0 |
455 | mov dword [edi+EhciCtrlDataSegReg], 0 |
456 | @@: |
456 | @@: |
457 | ; 7b. Hook interrupt and enable appropriate interrupt sources. |
457 | ; 7b. Hook interrupt and enable appropriate interrupt sources. |
458 | mov ah, [.bus] |
458 | mov ah, [.bus] |
459 | mov al, 0 |
459 | mov al, 0 |
460 | mov bh, [.devfn] |
460 | mov bh, [.devfn] |
461 | mov bl, 3Ch |
461 | mov bl, 3Ch |
462 | call pci_read_reg |
462 | call pci_read_reg |
463 | ; al = IRQ |
463 | ; al = IRQ |
464 | DEBUGF 1,'K : attaching to IRQ %x\n',al |
464 | DEBUGF 1,'K : attaching to IRQ %x\n',al |
465 | movzx eax, al |
465 | movzx eax, al |
466 | stdcall attach_int_handler, eax, ehci_irq, esi |
466 | stdcall attach_int_handler, eax, ehci_irq, esi |
467 | ; mov dword [edi+EhciStatusReg], 111111b ; clear status |
467 | ; mov dword [edi+EhciStatusReg], 111111b ; clear status |
468 | ; disable Frame List Rollover interrupt, enable all other sources |
468 | ; disable Frame List Rollover interrupt, enable all other sources |
469 | mov dword [edi+EhciInterruptReg], 110111b |
469 | mov dword [edi+EhciInterruptReg], 110111b |
470 | ; 7c. Inform the controller of the address of periodic lists head. |
470 | ; 7c. Inform the controller of the address of periodic lists head. |
471 | lea eax, [esi-sizeof.ehci_controller] |
471 | lea eax, [esi-sizeof.ehci_controller] |
472 | call get_phys_addr |
472 | call get_phys_addr |
473 | mov dword [edi+EhciPeriodicListReg], eax |
473 | mov dword [edi+EhciPeriodicListReg], eax |
474 | ; 7d. Inform the controller of the address of asynchronous lists head. |
474 | ; 7d. Inform the controller of the address of asynchronous lists head. |
475 | lea eax, [esi+ehci_controller.ControlED-sizeof.ehci_controller] |
475 | lea eax, [esi+ehci_controller.ControlED-sizeof.ehci_controller] |
476 | call get_phys_addr |
476 | call get_phys_addr |
477 | mov dword [edi+EhciAsyncListReg], eax |
477 | mov dword [edi+EhciAsyncListReg], eax |
478 | ; 7e. Configure operational details and run the controller. |
478 | ; 7e. Configure operational details and run the controller. |
479 | mov dword [edi+EhciCommandReg], \ |
479 | mov dword [edi+EhciCommandReg], \ |
480 | (1 shl 16) + \ ; interrupt threshold = 1 microframe = 0.125ms |
480 | (1 shl 16) + \ ; interrupt threshold = 1 microframe = 0.125ms |
481 | (0 shl 11) + \ ; disable Async Park Mode |
481 | (0 shl 11) + \ ; disable Async Park Mode |
482 | (0 shl 8) + \ ; zero Async Park Mode Count |
482 | (0 shl 8) + \ ; zero Async Park Mode Count |
483 | (1 shl 5) + \ ; Async Schedule Enable |
483 | (1 shl 5) + \ ; Async Schedule Enable |
484 | (1 shl 4) + \ ; Periodic Schedule Enable |
484 | (1 shl 4) + \ ; Periodic Schedule Enable |
485 | (0 shl 2) + \ ; 1024 elements in FrameList |
485 | (0 shl 2) + \ ; 1024 elements in FrameList |
486 | 1 ; Run |
486 | 1 ; Run |
487 | ; 7f. Route all ports to this controller, not companion controllers. |
487 | ; 7f. Route all ports to this controller, not companion controllers. |
488 | mov dword [edi+EhciConfigFlagReg], 1 |
488 | mov dword [edi+EhciConfigFlagReg], 1 |
489 | DEBUGF 1,'K : EHCI controller at %x:%x with %d ports initialized\n',[.bus]:2,[.devfn]:2,[esi+usb_controller.NumPorts] |
489 | DEBUGF 1,'K : EHCI controller at %x:%x with %d ports initialized\n',[.bus]:2,[.devfn]:2,[esi+usb_controller.NumPorts] |
490 | ; 8. Apply port power, if needed, and disable all ports. |
490 | ; 8. Apply port power, if needed, and disable all ports. |
491 | xor ecx, ecx |
491 | xor ecx, ecx |
492 | @@: |
492 | @@: |
493 | mov dword [edi+EhciPortsReg+ecx*4], 1000h ; Port Power enabled, all other bits disabled |
493 | mov dword [edi+EhciPortsReg+ecx*4], 1000h ; Port Power enabled, all other bits disabled |
494 | inc ecx |
494 | inc ecx |
495 | cmp ecx, [esi+usb_controller.NumPorts] |
495 | cmp ecx, [esi+usb_controller.NumPorts] |
496 | jb @b |
496 | jb @b |
497 | test byte [esi+ehci_controller.StructuralParams-sizeof.ehci_controller], 10h |
497 | test byte [esi+ehci_controller.StructuralParams-sizeof.ehci_controller], 10h |
498 | jz @f |
498 | jz @f |
499 | push esi |
499 | push esi |
500 | movi esi, 20 |
500 | movi esi, 20 |
501 | call delay_ms |
501 | call delay_ms |
502 | pop esi |
502 | pop esi |
503 | @@: |
503 | @@: |
504 | ; 9. Return pointer to usb_controller. |
504 | ; 9. Return pointer to usb_controller. |
505 | xchg eax, esi |
505 | xchg eax, esi |
506 | ret |
506 | ret |
507 | ; On error, pop the pointer saved at step 1 and return zero. |
507 | ; On error, pop the pointer saved at step 1 and return zero. |
508 | ; Note that the main code branch restores the stack at step 7 and never fails |
508 | ; Note that the main code branch restores the stack at step 7 and never fails |
509 | ; after step 7. |
509 | ; after step 7. |
510 | .fail_unmap: |
510 | .fail_unmap: |
511 | pop eax |
511 | pop eax |
512 | push eax |
512 | push eax |
513 | stdcall free_kernel_space, [eax+ehci_controller.MMIOBase1] |
513 | stdcall free_kernel_space, [eax+ehci_controller.MMIOBase1] |
514 | .fail: |
514 | .fail: |
515 | pop ecx |
515 | pop ecx |
516 | xor eax, eax |
516 | xor eax, eax |
517 | ret |
517 | ret |
518 | endp |
518 | endp |
519 | 519 | ||
520 | ; Helper procedure for step 3 of ehci_init, see comments there. |
520 | ; Helper procedure for step 3 of ehci_init, see comments there. |
521 | ; Initializes the static head of one list. |
521 | ; Initializes the static head of one list. |
522 | ; esi = pointer to the "next" list, edi = pointer to head to initialize. |
522 | ; esi = pointer to the "next" list, edi = pointer to head to initialize. |
523 | ; Advances edi to the next head, keeps esi. |
523 | ; Advances edi to the next head, keeps esi. |
524 | proc ehci_init_static_endpoint |
524 | proc ehci_init_static_endpoint |
525 | xor eax, eax |
525 | xor eax, eax |
526 | inc eax ; set Terminate bit |
526 | inc eax ; set Terminate bit |
527 | mov [edi+ehci_static_ep.NextTD], eax |
527 | mov [edi+ehci_static_ep.NextTD], eax |
528 | mov [edi+ehci_static_ep.AlternateNextTD], eax |
528 | mov [edi+ehci_static_ep.AlternateNextTD], eax |
529 | test esi, esi |
529 | test esi, esi |
530 | jz @f |
530 | jz @f |
531 | mov eax, esi |
531 | mov eax, esi |
532 | call get_phys_addr |
532 | call get_phys_addr |
533 | inc eax |
533 | inc eax |
534 | inc eax ; set Type to EHCI_TYPE_QH |
534 | inc eax ; set Type to EHCI_TYPE_QH |
535 | @@: |
535 | @@: |
536 | mov [edi+ehci_static_ep.NextQH], eax |
536 | mov [edi+ehci_static_ep.NextQH], eax |
537 | mov [edi+ehci_static_ep.NextList], esi |
537 | mov [edi+ehci_static_ep.NextList], esi |
538 | mov byte [edi+ehci_static_ep.OverlayToken], 1 shl 6 ; halted |
538 | mov byte [edi+ehci_static_ep.OverlayToken], 1 shl 6 ; halted |
539 | add edi, ehci_static_ep.SoftwarePart |
539 | add edi, ehci_static_ep.SoftwarePart |
540 | call usb_init_static_endpoint |
540 | call usb_init_static_endpoint |
541 | add edi, sizeof.ehci_static_ep - ehci_static_ep.SoftwarePart |
541 | add edi, sizeof.ehci_static_ep - ehci_static_ep.SoftwarePart |
542 | ret |
542 | ret |
543 | endp |
543 | endp |
544 | 544 | ||
545 | ; Helper procedure for step 3 of ehci_init, see comments there. |
545 | ; Helper procedure for step 3 of ehci_init, see comments there. |
546 | ; Initializes one half of group of static heads. |
546 | ; Initializes one half of group of static heads. |
547 | ; edx = size of the next group = half of size of the group, |
547 | ; edx = size of the next group = half of size of the group, |
548 | ; edi = pointer to the group, esi = pointer to the next group. |
548 | ; edi = pointer to the group, esi = pointer to the next group. |
549 | ; Advances esi, edi to next group, keeps edx. |
549 | ; Advances esi, edi to next group, keeps edx. |
550 | proc ehci_init_static_ep_group |
550 | proc ehci_init_static_ep_group |
551 | push edx |
551 | push edx |
552 | @@: |
552 | @@: |
553 | call ehci_init_static_endpoint |
553 | call ehci_init_static_endpoint |
554 | add esi, sizeof.ehci_static_ep |
554 | add esi, sizeof.ehci_static_ep |
555 | dec edx |
555 | dec edx |
556 | jnz @b |
556 | jnz @b |
557 | pop edx |
557 | pop edx |
558 | ret |
558 | ret |
559 | endp |
559 | endp |
560 | 560 | ||
561 | ; Controller-specific pre-initialization function: take ownership from BIOS. |
561 | ; Controller-specific pre-initialization function: take ownership from BIOS. |
562 | ; Some BIOSes, although not all of them, use USB controllers themselves |
562 | ; Some BIOSes, although not all of them, use USB controllers themselves |
563 | ; to support USB flash drives. In this case, |
563 | ; to support USB flash drives. In this case, |
564 | ; we must notify the BIOS that we don't need that emulation and know how to |
564 | ; we must notify the BIOS that we don't need that emulation and know how to |
565 | ; deal with USB devices. |
565 | ; deal with USB devices. |
566 | proc ehci_kickoff_bios |
566 | proc ehci_kickoff_bios |
567 | ; 1. Get the physical address of MMIO registers. |
567 | ; 1. Get the physical address of MMIO registers. |
568 | mov ah, [esi+PCIDEV.bus] |
568 | mov ah, [esi+PCIDEV.bus] |
569 | mov bh, [esi+PCIDEV.devfn] |
569 | mov bh, [esi+PCIDEV.devfn] |
570 | mov al, 2 |
570 | mov al, 2 |
571 | mov bl, 10h |
571 | mov bl, 10h |
572 | call pci_read_reg |
572 | call pci_read_reg |
573 | and al, not 0Fh |
573 | and al, not 0Fh |
574 | ; 2. Create mapping for physical memory. 200h bytes are always sufficient. |
574 | ; 2. Create mapping for physical memory. 200h bytes are always sufficient. |
575 | stdcall map_io_mem, eax, 200h, PG_SW+PG_NOCACHE |
575 | stdcall map_io_mem, eax, 200h, PG_SW+PG_NOCACHE |
576 | test eax, eax |
576 | test eax, eax |
577 | jz .nothing |
577 | jz .nothing |
578 | push eax ; push argument for step 8 |
578 | push eax ; push argument for step 8 |
579 | ; 3. Some BIOSes enable controller interrupts as a result of giving |
579 | ; 3. Some BIOSes enable controller interrupts as a result of giving |
580 | ; controller away. At this point the system knows nothing about how to serve |
580 | ; controller away. At this point the system knows nothing about how to serve |
581 | ; EHCI interrupts, so such an interrupt will send the system into an infinite |
581 | ; EHCI interrupts, so such an interrupt will send the system into an infinite |
582 | ; loop handling the same IRQ again and again. Thus, we need to block EHCI |
582 | ; loop handling the same IRQ again and again. Thus, we need to block EHCI |
583 | ; interrupts. We can't do this at the controller level until step 5, |
583 | ; interrupts. We can't do this at the controller level until step 5, |
584 | ; because the controller is currently owned by BIOS, so we block all hardware |
584 | ; because the controller is currently owned by BIOS, so we block all hardware |
585 | ; interrupts on this processor until step 5. |
585 | ; interrupts on this processor until step 5. |
586 | pushf |
586 | pushf |
587 | cli |
587 | cli |
588 | ; 4. Take the ownership over the controller. |
588 | ; 4. Take the ownership over the controller. |
589 | ; 4a. Locate take-ownership capability in the PCI configuration space. |
589 | ; 4a. Locate take-ownership capability in the PCI configuration space. |
590 | ; Limit the loop with 100h iterations; since the entire configuration space is |
590 | ; Limit the loop with 100h iterations; since the entire configuration space is |
591 | ; 100h bytes long, hitting this number of iterations means that something is |
591 | ; 100h bytes long, hitting this number of iterations means that something is |
592 | ; corrupted. |
592 | ; corrupted. |
593 | ; Use a value from MMIO as a starting point. |
593 | ; Use a value from MMIO as a starting point. |
594 | mov edx, [eax+EhciCapParamsReg] |
594 | mov edx, [eax+EhciCapParamsReg] |
595 | DEBUGF 1,'K : edx=%x\n',edx |
595 | DEBUGF 1,'K : edx=%x\n',edx |
596 | movzx edi, byte [eax+EhciCapLengthReg] |
596 | movzx edi, byte [eax+EhciCapLengthReg] |
597 | add edi, eax |
597 | add edi, eax |
598 | push 0 |
598 | push 0 |
599 | mov bl, dh ; get Extended Capabilities Pointer |
599 | mov bl, dh ; get Extended Capabilities Pointer |
600 | test bl, bl |
600 | test bl, bl |
601 | jz .has_ownership2 |
601 | jz .has_ownership2 |
602 | cmp bl, 40h |
602 | cmp bl, 40h |
603 | jb .no_capability |
603 | jb .no_capability |
604 | .look_bios_handoff: |
604 | .look_bios_handoff: |
605 | test bl, 3 |
605 | test bl, 3 |
606 | jnz .no_capability |
606 | jnz .no_capability |
607 | ; In each iteration, read the current dword, |
607 | ; In each iteration, read the current dword, |
608 | mov ah, [esi+PCIDEV.bus] |
608 | mov ah, [esi+PCIDEV.bus] |
609 | mov al, 2 |
609 | mov al, 2 |
610 | mov bh, [esi+PCIDEV.devfn] |
610 | mov bh, [esi+PCIDEV.devfn] |
611 | call pci_read_reg |
611 | call pci_read_reg |
612 | ; check, whether the capability ID is take-ownership ID = 1, |
612 | ; check, whether the capability ID is take-ownership ID = 1, |
613 | cmp al, 1 |
613 | cmp al, 1 |
614 | jz .found_bios_handoff |
614 | jz .found_bios_handoff |
615 | ; if not, advance to next-capability link and continue loop. |
615 | ; if not, advance to next-capability link and continue loop. |
616 | dec byte [esp] |
616 | dec byte [esp] |
617 | jz .no_capability |
617 | jz .no_capability |
618 | mov bl, ah |
618 | mov bl, ah |
619 | cmp bl, 40h |
619 | cmp bl, 40h |
620 | jae .look_bios_handoff |
620 | jae .look_bios_handoff |
621 | .no_capability: |
621 | .no_capability: |
622 | dbgstr 'warning: cannot locate take-ownership capability' |
622 | dbgstr 'warning: cannot locate take-ownership capability' |
623 | jmp .has_ownership2 |
623 | jmp .has_ownership2 |
624 | .found_bios_handoff: |
624 | .found_bios_handoff: |
625 | ; 4b. Check whether BIOS has ownership. |
625 | ; 4b. Check whether BIOS has ownership. |
626 | ; Some BIOSes release ownership before loading OS, but forget to unwatch for |
626 | ; Some BIOSes release ownership before loading OS, but forget to unwatch for |
627 | ; change-ownership requests; they cannot handle ownership request, so |
627 | ; change-ownership requests; they cannot handle ownership request, so |
628 | ; such a request sends the system into infinite loop of handling the same SMI |
628 | ; such a request sends the system into infinite loop of handling the same SMI |
629 | ; over and over. Avoid this. |
629 | ; over and over. Avoid this. |
630 | inc ebx |
630 | inc ebx |
631 | inc ebx |
631 | inc ebx |
632 | test eax, 0x10000 |
632 | test eax, 0x10000 |
633 | jz .has_ownership |
633 | jz .has_ownership |
634 | ; 4c. Request ownership. |
634 | ; 4c. Request ownership. |
635 | inc ebx |
635 | inc ebx |
636 | mov cl, 1 |
636 | mov cl, 1 |
637 | mov ah, [esi+PCIDEV.bus] |
637 | mov ah, [esi+PCIDEV.bus] |
638 | mov al, 0 |
638 | mov al, 0 |
639 | call pci_write_reg |
639 | call pci_write_reg |
640 | ; 4d. Some BIOSes set ownership flag, but forget to watch for change-ownership |
640 | ; 4d. Some BIOSes set ownership flag, but forget to watch for change-ownership |
641 | ; requests; if so, there is no sense in waiting. |
641 | ; requests; if so, there is no sense in waiting. |
642 | inc ebx |
642 | inc ebx |
643 | mov ah, [esi+PCIDEV.bus] |
643 | mov ah, [esi+PCIDEV.bus] |
644 | mov al, 2 |
644 | mov al, 2 |
645 | call pci_read_reg |
645 | call pci_read_reg |
646 | dec ebx |
646 | dec ebx |
647 | dec ebx |
647 | dec ebx |
648 | test ah, 20h |
648 | test ah, 20h |
649 | jz .force_ownership |
649 | jz .force_ownership |
650 | ; 4e. Wait for result no more than 1 s, checking for status every 1 ms. |
650 | ; 4e. Wait for result no more than 1 s, checking for status every 1 ms. |
651 | ; If successful, go to 5. |
651 | ; If successful, go to 5. |
652 | mov dword [esp], 1000 |
652 | mov dword [esp], 1000 |
653 | @@: |
653 | @@: |
654 | mov ah, [esi+PCIDEV.bus] |
654 | mov ah, [esi+PCIDEV.bus] |
655 | mov al, 0 |
655 | mov al, 0 |
656 | call pci_read_reg |
656 | call pci_read_reg |
657 | test al, 1 |
657 | test al, 1 |
658 | jz .has_ownership |
658 | jz .has_ownership |
659 | push esi |
659 | push esi |
660 | movi esi, 1 |
660 | movi esi, 1 |
661 | call delay_ms |
661 | call delay_ms |
662 | pop esi |
662 | pop esi |
663 | dec dword [esp] |
663 | dec dword [esp] |
664 | jnz @b |
664 | jnz @b |
665 | dbgstr 'warning: taking EHCI ownership from BIOS timeout' |
665 | dbgstr 'warning: taking EHCI ownership from BIOS timeout' |
666 | .force_ownership: |
666 | .force_ownership: |
667 | ; 4f. BIOS has not responded within the timeout. |
667 | ; 4f. BIOS has not responded within the timeout. |
668 | ; Let's just clear BIOS ownership flag and hope that everything will be ok. |
668 | ; Let's just clear BIOS ownership flag and hope that everything will be ok. |
669 | mov ah, [esi+PCIDEV.bus] |
669 | mov ah, [esi+PCIDEV.bus] |
670 | mov al, 0 |
670 | mov al, 0 |
671 | mov cl, 0 |
671 | mov cl, 0 |
672 | call pci_write_reg |
672 | call pci_write_reg |
673 | .has_ownership: |
673 | .has_ownership: |
674 | ; 5. Just in case clear all SMI event sources except change-ownership. |
674 | ; 5. Just in case clear all SMI event sources except change-ownership. |
675 | dbgstr 'has_ownership' |
675 | dbgstr 'has_ownership' |
676 | inc ebx |
676 | inc ebx |
677 | inc ebx |
677 | inc ebx |
678 | mov ah, [esi+PCIDEV.bus] |
678 | mov ah, [esi+PCIDEV.bus] |
679 | mov al, 2 |
679 | mov al, 2 |
680 | mov ecx, eax |
680 | mov ecx, eax |
681 | call pci_read_reg |
681 | call pci_read_reg |
682 | and ax, 2000h |
682 | and ax, 2000h |
683 | xchg eax, ecx |
683 | xchg eax, ecx |
684 | call pci_write_reg |
684 | call pci_write_reg |
685 | .has_ownership2: |
685 | .has_ownership2: |
686 | pop ecx |
686 | pop ecx |
687 | ; 6. Disable all controller interrupts until the system will be ready to |
687 | ; 6. Disable all controller interrupts until the system will be ready to |
688 | ; process them. |
688 | ; process them. |
689 | mov dword [edi+EhciInterruptReg], 0 |
689 | mov dword [edi+EhciInterruptReg], 0 |
690 | ; 7. Now we can unblock interrupts in the processor. |
690 | ; 7. Now we can unblock interrupts in the processor. |
691 | popf |
691 | popf |
692 | ; 8. Release memory mapping created in step 2 and return. |
692 | ; 8. Release memory mapping created in step 2 and return. |
693 | call free_kernel_space |
693 | call free_kernel_space |
694 | .nothing: |
694 | .nothing: |
695 | ret |
695 | ret |
696 | endp |
696 | endp |
697 | 697 | ||
698 | ; IRQ handler for EHCI controllers. |
698 | ; IRQ handler for EHCI controllers. |
699 | ehci_irq.noint: |
699 | ehci_irq.noint: |
700 | spin_unlock_irqrestore [esi+usb_controller.WaitSpinlock] |
700 | spin_unlock_irqrestore [esi+usb_controller.WaitSpinlock] |
701 | ; Not our interrupt: restore registers and return zero. |
701 | ; Not our interrupt: restore registers and return zero. |
702 | xor eax, eax |
702 | xor eax, eax |
703 | pop edi esi ebx |
703 | pop edi esi ebx |
704 | ret |
704 | ret |
705 | 705 | ||
706 | proc ehci_irq |
706 | proc ehci_irq |
707 | push ebx esi edi ; save registers to be cdecl |
707 | push ebx esi edi ; save registers to be cdecl |
708 | virtual at esp |
708 | virtual at esp |
709 | rd 3 ; saved registers |
709 | rd 3 ; saved registers |
710 | dd ? ; return address |
710 | dd ? ; return address |
711 | .controller dd ? |
711 | .controller dd ? |
712 | end virtual |
712 | end virtual |
713 | ; 1. ebx will hold whether some deferred processing is needed, |
713 | ; 1. ebx will hold whether some deferred processing is needed, |
714 | ; that cannot be done from the interrupt handler. Initialize to zero. |
714 | ; that cannot be done from the interrupt handler. Initialize to zero. |
715 | xor ebx, ebx |
715 | xor ebx, ebx |
716 | ; 2. Get the mask of events which should be processed. |
716 | ; 2. Get the mask of events which should be processed. |
717 | mov esi, [.controller] |
717 | mov esi, [.controller] |
718 | mov edi, [esi+ehci_controller.MMIOBase2-sizeof.ehci_controller] |
718 | mov edi, [esi+ehci_controller.MMIOBase2-sizeof.ehci_controller] |
719 | spin_lock_irqsave [esi+usb_controller.WaitSpinlock] |
719 | spin_lock_irqsave [esi+usb_controller.WaitSpinlock] |
720 | mov eax, [edi+EhciStatusReg] |
720 | mov eax, [edi+EhciStatusReg] |
721 | ; DEBUGF 1,'K : [%d] EHCI status %x\n',[timer_ticks],eax |
721 | ; DEBUGF 1,'K : [%d] EHCI status %x\n',[timer_ticks],eax |
722 | ; 3. Check whether that interrupt has been generated by our controller. |
722 | ; 3. Check whether that interrupt has been generated by our controller. |
723 | ; (One IRQ can be shared by several devices.) |
723 | ; (One IRQ can be shared by several devices.) |
724 | and eax, [edi+EhciInterruptReg] |
724 | and eax, [edi+EhciInterruptReg] |
725 | jz .noint |
725 | jz .noint |
726 | ; 4. Clear the events we know of. |
726 | ; 4. Clear the events we know of. |
727 | ; Note that this should be done before processing of events: |
727 | ; Note that this should be done before processing of events: |
728 | ; new events could arise while we are processing those, this way we won't lose |
728 | ; new events could arise while we are processing those, this way we won't lose |
729 | ; them (the controller would generate another interrupt after completion |
729 | ; them (the controller would generate another interrupt after completion |
730 | ; of this one). |
730 | ; of this one). |
731 | ; DEBUGF 1,'K : EHCI interrupt: status = %x\n',eax |
731 | ; DEBUGF 1,'K : EHCI interrupt: status = %x\n',eax |
732 | mov [edi+EhciStatusReg], eax |
732 | mov [edi+EhciStatusReg], eax |
733 | ; 5. Sanity check. |
733 | ; 5. Sanity check. |
734 | test al, 10h |
734 | test al, 10h |
735 | jz @f |
735 | jz @f |
736 | DEBUGF 1,'K : something terrible happened with EHCI %x (%x)\n',esi,al |
736 | DEBUGF 1,'K : something terrible happened with EHCI %x (%x)\n',esi,al |
737 | @@: |
737 | @@: |
738 | ; We can't do too much from an interrupt handler. Inform the processing thread |
738 | ; We can't do too much from an interrupt handler. Inform the processing thread |
739 | ; that it should perform appropriate actions. |
739 | ; that it should perform appropriate actions. |
740 | or [esi+ehci_controller.DeferredActions-sizeof.ehci_controller], eax |
740 | or [esi+ehci_controller.DeferredActions-sizeof.ehci_controller], eax |
741 | spin_unlock_irqrestore [esi+usb_controller.WaitSpinlock] |
741 | spin_unlock_irqrestore [esi+usb_controller.WaitSpinlock] |
742 | inc ebx |
742 | inc ebx |
743 | call usb_wakeup_if_needed |
743 | call usb_wakeup_if_needed |
744 | ; 6. Interrupt processed; return non-zero. |
744 | ; 6. Interrupt processed; return non-zero. |
745 | mov al, 1 |
745 | mov al, 1 |
746 | pop edi esi ebx ; restore used registers to be cdecl |
746 | pop edi esi ebx ; restore used registers to be cdecl |
747 | ret |
747 | ret |
748 | endp |
748 | endp |
749 | 749 | ||
750 | ; This procedure is called from usb_set_address_callback |
750 | ; This procedure is called from usb_set_address_callback |
751 | ; and stores USB device address in the ehci_pipe structure. |
751 | ; and stores USB device address in the ehci_pipe structure. |
752 | ; in: esi -> usb_controller, ebx -> usb_pipe, cl = address |
752 | ; in: esi -> usb_controller, ebx -> usb_pipe, cl = address |
753 | proc ehci_set_device_address |
753 | proc ehci_set_device_address |
754 | mov byte [ebx+ehci_pipe.Token-sizeof.ehci_pipe], cl |
754 | mov byte [ebx+ehci_pipe.Token-sizeof.ehci_pipe], cl |
755 | call usb_subscribe_control |
755 | call usb_subscribe_control |
756 | ret |
756 | ret |
757 | endp |
757 | endp |
758 | 758 | ||
759 | ; This procedure returns USB device address from the ehci_pipe structure. |
759 | ; This procedure returns USB device address from the ehci_pipe structure. |
760 | ; in: esi -> usb_controller, ebx -> usb_pipe |
760 | ; in: esi -> usb_controller, ebx -> usb_pipe |
761 | ; out: eax = endpoint address |
761 | ; out: eax = endpoint address |
762 | proc ehci_get_device_address |
762 | proc ehci_get_device_address |
763 | mov eax, [ebx+ehci_pipe.Token-sizeof.ehci_pipe] |
763 | mov eax, [ebx+ehci_pipe.Token-sizeof.ehci_pipe] |
764 | and eax, 7Fh |
764 | and eax, 7Fh |
765 | ret |
765 | ret |
766 | endp |
766 | endp |
767 | 767 | ||
768 | ; This procedure is called from usb_set_address_callback |
768 | ; This procedure is called from usb_set_address_callback |
769 | ; if the device does not accept SET_ADDRESS command and needs |
769 | ; if the device does not accept SET_ADDRESS command and needs |
770 | ; to be disabled at the port level. |
770 | ; to be disabled at the port level. |
771 | ; in: esi -> usb_controller, ecx = port (zero-based) |
771 | ; in: esi -> usb_controller, ecx = port (zero-based) |
772 | proc ehci_port_disable |
772 | proc ehci_port_disable |
773 | mov eax, [esi+ehci_controller.MMIOBase2-sizeof.ehci_controller] |
773 | mov eax, [esi+ehci_controller.MMIOBase2-sizeof.ehci_controller] |
774 | and dword [eax+EhciPortsReg+ecx*4], not (4 or 2Ah) |
774 | and dword [eax+EhciPortsReg+ecx*4], not (4 or 2Ah) |
775 | ret |
775 | ret |
776 | endp |
776 | endp |
777 | 777 | ||
778 | ; This procedure is called from usb_get_descr8_callback when |
778 | ; This procedure is called from usb_get_descr8_callback when |
779 | ; the packet size for zero endpoint becomes known and |
779 | ; the packet size for zero endpoint becomes known and |
780 | ; stores the packet size in ehci_pipe structure. |
780 | ; stores the packet size in ehci_pipe structure. |
781 | ; in: esi -> usb_controller, ebx -> usb_pipe, ecx = packet size |
781 | ; in: esi -> usb_controller, ebx -> usb_pipe, ecx = packet size |
782 | proc ehci_set_endpoint_packet_size |
782 | proc ehci_set_endpoint_packet_size |
783 | mov eax, [ebx+ehci_pipe.Token-sizeof.ehci_pipe] |
783 | mov eax, [ebx+ehci_pipe.Token-sizeof.ehci_pipe] |
784 | and eax, not (0x7FF shl 16) |
784 | and eax, not (0x7FF shl 16) |
785 | shl ecx, 16 |
785 | shl ecx, 16 |
786 | or eax, ecx |
786 | or eax, ecx |
787 | mov [ebx+ehci_pipe.Token-sizeof.ehci_pipe], eax |
787 | mov [ebx+ehci_pipe.Token-sizeof.ehci_pipe], eax |
788 | ; Wait until hardware cache is evicted. |
788 | ; Wait until hardware cache is evicted. |
789 | call usb_subscribe_control |
789 | call usb_subscribe_control |
790 | ret |
790 | ret |
791 | endp |
791 | endp |
792 | 792 | ||
793 | uglobal |
793 | uglobal |
794 | align 4 |
794 | align 4 |
795 | ; Data for memory allocator, see memory.inc. |
795 | ; Data for memory allocator, see memory.inc. |
796 | ehci_ep_first_page dd ? |
796 | ehci_ep_first_page dd ? |
797 | ehci_ep_mutex MUTEX |
797 | ehci_ep_mutex MUTEX |
798 | ehci_gtd_first_page dd ? |
798 | ehci_gtd_first_page dd ? |
799 | ehci_gtd_mutex MUTEX |
799 | ehci_gtd_mutex MUTEX |
800 | endg |
800 | endg |
801 | 801 | ||
802 | ; This procedure allocates memory for pipe. |
802 | ; This procedure allocates memory for pipe. |
803 | ; Both hardware+software parts must be allocated, returns pointer to usb_pipe |
803 | ; Both hardware+software parts must be allocated, returns pointer to usb_pipe |
804 | ; (software part). |
804 | ; (software part). |
805 | proc ehci_alloc_pipe |
805 | proc ehci_alloc_pipe |
806 | push ebx |
806 | push ebx |
807 | mov ebx, ehci_ep_mutex |
807 | mov ebx, ehci_ep_mutex |
808 | stdcall usb_allocate_common, (sizeof.ehci_pipe + sizeof.usb_pipe + 1Fh) and not 1Fh |
808 | stdcall usb_allocate_common, (sizeof.ehci_pipe + sizeof.usb_pipe + 1Fh) and not 1Fh |
809 | test eax, eax |
809 | test eax, eax |
810 | jz @f |
810 | jz @f |
811 | add eax, sizeof.ehci_pipe |
811 | add eax, sizeof.ehci_pipe |
812 | @@: |
812 | @@: |
813 | pop ebx |
813 | pop ebx |
814 | ret |
814 | ret |
815 | endp |
815 | endp |
816 | 816 | ||
817 | ; This procedure frees memory for pipe allocated by ehci_alloc_pipe. |
817 | ; This procedure frees memory for pipe allocated by ehci_alloc_pipe. |
818 | ; void stdcall with one argument = pointer to usb_pipe. |
818 | ; void stdcall with one argument = pointer to usb_pipe. |
819 | proc ehci_free_pipe |
819 | proc ehci_free_pipe |
820 | virtual at esp |
820 | virtual at esp |
821 | dd ? ; return address |
821 | dd ? ; return address |
822 | .ptr dd ? |
822 | .ptr dd ? |
823 | end virtual |
823 | end virtual |
824 | sub [.ptr], sizeof.ehci_pipe |
824 | sub [.ptr], sizeof.ehci_pipe |
825 | jmp usb_free_common |
825 | jmp usb_free_common |
826 | endp |
826 | endp |
827 | 827 | ||
828 | ; This procedure is called from API usb_open_pipe and processes |
828 | ; This procedure is called from API usb_open_pipe and processes |
829 | ; the controller-specific part of this API. See docs. |
829 | ; the controller-specific part of this API. See docs. |
830 | ; in: edi -> usb_pipe for target, ecx -> usb_pipe for config pipe, |
830 | ; in: edi -> usb_pipe for target, ecx -> usb_pipe for config pipe, |
831 | ; esi -> usb_controller, eax -> usb_gtd for the first TD, |
831 | ; esi -> usb_controller, eax -> usb_gtd for the first TD, |
832 | ; [ebp+12] = endpoint, [ebp+16] = maxpacket, [ebp+20] = type |
832 | ; [ebp+12] = endpoint, [ebp+16] = maxpacket, [ebp+20] = type |
833 | proc ehci_init_pipe |
833 | proc ehci_init_pipe |
834 | virtual at ebp+8 |
834 | virtual at ebp+8 |
835 | .config_pipe dd ? |
835 | .config_pipe dd ? |
836 | .endpoint dd ? |
836 | .endpoint dd ? |
837 | .maxpacket dd ? |
837 | .maxpacket dd ? |
838 | .type dd ? |
838 | .type dd ? |
839 | .interval dd ? |
839 | .interval dd ? |
840 | end virtual |
840 | end virtual |
841 | ; 1. Zero all fields in the hardware part. |
841 | ; 1. Zero all fields in the hardware part. |
842 | push eax ecx |
842 | push eax ecx |
843 | sub edi, sizeof.ehci_pipe |
843 | sub edi, sizeof.ehci_pipe |
844 | xor eax, eax |
844 | xor eax, eax |
845 | movi ecx, sizeof.ehci_pipe/4 |
845 | movi ecx, sizeof.ehci_pipe/4 |
846 | rep stosd |
846 | rep stosd |
847 | pop ecx eax |
847 | pop ecx eax |
848 | ; 2. Setup PID in the first TD and make sure that the it is not active. |
848 | ; 2. Setup PID in the first TD and make sure that the it is not active. |
849 | xor edx, edx |
849 | xor edx, edx |
850 | test byte [.endpoint], 80h |
850 | test byte [.endpoint], 80h |
851 | setnz dh |
851 | setnz dh |
852 | mov [eax+ehci_gtd.Token-sizeof.ehci_gtd], edx |
852 | mov [eax+ehci_gtd.Token-sizeof.ehci_gtd], edx |
853 | mov [eax+ehci_gtd.NextTD-sizeof.ehci_gtd], 1 |
853 | mov [eax+ehci_gtd.NextTD-sizeof.ehci_gtd], 1 |
854 | mov [eax+ehci_gtd.AlternateNextTD-sizeof.ehci_gtd], 1 |
854 | mov [eax+ehci_gtd.AlternateNextTD-sizeof.ehci_gtd], 1 |
855 | ; 3. Store physical address of the first TD. |
855 | ; 3. Store physical address of the first TD. |
856 | sub eax, sizeof.ehci_gtd |
856 | sub eax, sizeof.ehci_gtd |
857 | call get_phys_addr |
857 | call get_phys_addr |
858 | mov [edi+ehci_pipe.Overlay.NextTD-sizeof.ehci_pipe], eax |
858 | mov [edi+ehci_pipe.Overlay.NextTD-sizeof.ehci_pipe], eax |
859 | ; 4. Fill ehci_pipe.Flags except for S- and C-masks. |
859 | ; 4. Fill ehci_pipe.Flags except for S- and C-masks. |
860 | ; Copy location from the config pipe. |
860 | ; Copy location from the config pipe. |
861 | mov eax, [ecx+ehci_pipe.Flags-sizeof.ehci_pipe] |
861 | mov eax, [ecx+ehci_pipe.Flags-sizeof.ehci_pipe] |
862 | and eax, 3FFF0000h |
862 | and eax, 3FFF0000h |
863 | ; Use 1 requests per microframe for control/bulk endpoints, |
863 | ; Use 1 requests per microframe for control/bulk endpoints, |
864 | ; use value from the endpoint descriptor for periodic endpoints |
864 | ; use value from the endpoint descriptor for periodic endpoints |
865 | movi edx, 1 |
865 | movi edx, 1 |
866 | test [.type], 1 |
866 | test [.type], 1 |
867 | jz @f |
867 | jz @f |
868 | mov edx, [.maxpacket] |
868 | mov edx, [.maxpacket] |
869 | shr edx, 11 |
869 | shr edx, 11 |
870 | inc edx |
870 | inc edx |
871 | @@: |
871 | @@: |
872 | shl edx, 30 |
872 | shl edx, 30 |
873 | or eax, edx |
873 | or eax, edx |
874 | mov [edi+ehci_pipe.Flags-sizeof.ehci_pipe], eax |
874 | mov [edi+ehci_pipe.Flags-sizeof.ehci_pipe], eax |
875 | ; 5. Fill ehci_pipe.Token. |
875 | ; 5. Fill ehci_pipe.Token. |
876 | mov eax, [ecx+ehci_pipe.Token-sizeof.ehci_pipe] |
876 | mov eax, [ecx+ehci_pipe.Token-sizeof.ehci_pipe] |
877 | ; copy following fields from the config pipe: |
877 | ; copy following fields from the config pipe: |
878 | ; DeviceAddress, EndpointSpeed, ControlEndpoint if new type is control |
878 | ; DeviceAddress, EndpointSpeed, ControlEndpoint if new type is control |
879 | mov ecx, eax |
879 | mov ecx, eax |
880 | and eax, 307Fh |
880 | and eax, 307Fh |
881 | and ecx, 8000000h |
881 | and ecx, 8000000h |
882 | or ecx, 4000h |
882 | or ecx, 4000h |
883 | mov edx, [.endpoint] |
883 | mov edx, [.endpoint] |
884 | and edx, 15 |
884 | and edx, 15 |
885 | shl edx, 8 |
885 | shl edx, 8 |
886 | or eax, edx |
886 | or eax, edx |
887 | mov edx, [.maxpacket] |
887 | mov edx, [.maxpacket] |
888 | shl edx, 16 |
888 | shl edx, 16 |
889 | or eax, edx |
889 | or eax, edx |
890 | ; for control endpoints, use DataToggle from TD, otherwise use DataToggle from QH |
890 | ; for control endpoints, use DataToggle from TD, otherwise use DataToggle from QH |
891 | cmp [.type], CONTROL_PIPE |
891 | cmp [.type], CONTROL_PIPE |
892 | jnz @f |
892 | jnz @f |
893 | or eax, ecx |
893 | or eax, ecx |
894 | @@: |
894 | @@: |
895 | ; for control/bulk USB2 endpoints, set NakCountReload to 4 |
895 | ; for control/bulk USB2 endpoints, set NakCountReload to 4 |
896 | test eax, USB_SPEED_HS shl 12 |
896 | test eax, USB_SPEED_HS shl 12 |
897 | jz .nonak |
897 | jz .nonak |
898 | cmp [.type], CONTROL_PIPE |
898 | cmp [.type], CONTROL_PIPE |
899 | jz @f |
899 | jz @f |
900 | cmp [.type], BULK_PIPE |
900 | cmp [.type], BULK_PIPE |
901 | jnz .nonak |
901 | jnz .nonak |
902 | @@: |
902 | @@: |
903 | or eax, 40000000h |
903 | or eax, 40000000h |
904 | .nonak: |
904 | .nonak: |
905 | mov [edi+ehci_pipe.Token-sizeof.ehci_pipe], eax |
905 | mov [edi+ehci_pipe.Token-sizeof.ehci_pipe], eax |
906 | ; 5. Select the corresponding list and insert to the list. |
906 | ; 5. Select the corresponding list and insert to the list. |
907 | ; 5a. Use Control list for control pipes, Bulk list for bulk pipes. |
907 | ; 5a. Use Control list for control pipes, Bulk list for bulk pipes. |
908 | lea edx, [esi+ehci_controller.ControlED.SoftwarePart-sizeof.ehci_controller] |
908 | lea edx, [esi+ehci_controller.ControlED.SoftwarePart-sizeof.ehci_controller] |
909 | cmp [.type], BULK_PIPE |
909 | cmp [.type], BULK_PIPE |
910 | jb .insert ; control pipe |
910 | jb .insert ; control pipe |
911 | lea edx, [esi+ehci_controller.BulkED.SoftwarePart-sizeof.ehci_controller] |
911 | lea edx, [esi+ehci_controller.BulkED.SoftwarePart-sizeof.ehci_controller] |
912 | jz .insert ; bulk pipe |
912 | jz .insert ; bulk pipe |
913 | .interrupt_pipe: |
913 | .interrupt_pipe: |
914 | ; 5b. For interrupt pipes, let the scheduler select the appropriate list |
914 | ; 5b. For interrupt pipes, let the scheduler select the appropriate list |
915 | ; and the appropriate microframe(s) (which goes to S-mask and C-mask) |
915 | ; and the appropriate microframe(s) (which goes to S-mask and C-mask) |
916 | ; based on the current bandwidth distribution and the requested bandwidth. |
916 | ; based on the current bandwidth distribution and the requested bandwidth. |
917 | ; There are two schedulers, one for high-speed devices, |
917 | ; There are two schedulers, one for high-speed devices, |
918 | ; another for split transactions. |
918 | ; another for split transactions. |
919 | ; This could fail if the requested bandwidth is not available; |
919 | ; This could fail if the requested bandwidth is not available; |
920 | ; if so, return an error. |
920 | ; if so, return an error. |
921 | test word [edi+ehci_pipe.Flags-sizeof.ehci_pipe+2], 3FFFh |
921 | test word [edi+ehci_pipe.Flags-sizeof.ehci_pipe+2], 3FFFh |
922 | jnz .interrupt_fs |
922 | jnz .interrupt_fs |
923 | call ehci_select_hs_interrupt_list |
923 | call ehci_select_hs_interrupt_list |
924 | jmp .interrupt_common |
924 | jmp .interrupt_common |
925 | .interrupt_fs: |
925 | .interrupt_fs: |
926 | call ehci_select_fs_interrupt_list |
926 | call ehci_select_fs_interrupt_list |
927 | .interrupt_common: |
927 | .interrupt_common: |
928 | test edx, edx |
928 | test edx, edx |
929 | jz .return0 |
929 | jz .return0 |
930 | mov word [edi+ehci_pipe.Flags-sizeof.ehci_pipe], ax |
930 | mov word [edi+ehci_pipe.Flags-sizeof.ehci_pipe], ax |
931 | .insert: |
931 | .insert: |
932 | mov [edi+ehci_pipe.BaseList-sizeof.ehci_pipe], edx |
932 | mov [edi+ehci_pipe.BaseList-sizeof.ehci_pipe], edx |
933 | ; Insert to the head of the corresponding list. |
933 | ; Insert to the head of the corresponding list. |
934 | ; Note: inserting to the head guarantees that the list traverse in |
934 | ; Note: inserting to the head guarantees that the list traverse in |
935 | ; ehci_process_updated_schedule, once started, will not interact with new pipes. |
935 | ; ehci_process_updated_schedule, once started, will not interact with new pipes. |
936 | ; However, we still need to ensure that links in the new pipe (edi.NextVirt) |
936 | ; However, we still need to ensure that links in the new pipe (edi.NextVirt) |
937 | ; are initialized before links to the new pipe (edx.NextVirt). |
937 | ; are initialized before links to the new pipe (edx.NextVirt). |
938 | ; 5c. Insert in the list of virtual addresses. |
938 | ; 5c. Insert in the list of virtual addresses. |
939 | mov ecx, [edx+usb_pipe.NextVirt] |
939 | mov ecx, [edx+usb_pipe.NextVirt] |
940 | mov [edi+usb_pipe.NextVirt], ecx |
940 | mov [edi+usb_pipe.NextVirt], ecx |
941 | mov [edi+usb_pipe.PrevVirt], edx |
941 | mov [edi+usb_pipe.PrevVirt], edx |
942 | mov [ecx+usb_pipe.PrevVirt], edi |
942 | mov [ecx+usb_pipe.PrevVirt], edi |
943 | mov [edx+usb_pipe.NextVirt], edi |
943 | mov [edx+usb_pipe.NextVirt], edi |
944 | ; 5d. Insert in the hardware list: copy previous NextQH to the new pipe, |
944 | ; 5d. Insert in the hardware list: copy previous NextQH to the new pipe, |
945 | ; store the physical address of the new pipe to previous NextQH. |
945 | ; store the physical address of the new pipe to previous NextQH. |
946 | mov ecx, [edx+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart] |
946 | mov ecx, [edx+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart] |
947 | mov [edi+ehci_pipe.NextQH-sizeof.ehci_pipe], ecx |
947 | mov [edi+ehci_pipe.NextQH-sizeof.ehci_pipe], ecx |
948 | lea eax, [edi-sizeof.ehci_pipe] |
948 | lea eax, [edi-sizeof.ehci_pipe] |
949 | call get_phys_addr |
949 | call get_phys_addr |
950 | inc eax |
950 | inc eax |
951 | inc eax |
951 | inc eax |
952 | mov [edx+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart], eax |
952 | mov [edx+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart], eax |
953 | ; 6. Return with nonzero eax. |
953 | ; 6. Return with nonzero eax. |
954 | ret |
954 | ret |
955 | .return0: |
955 | .return0: |
956 | xor eax, eax |
956 | xor eax, eax |
957 | ret |
957 | ret |
958 | endp |
958 | endp |
959 | 959 | ||
960 | ; This function is called from ehci_process_deferred when |
960 | ; This function is called from ehci_process_deferred when |
961 | ; a new device was connected at least USB_CONNECT_DELAY ticks |
961 | ; a new device was connected at least USB_CONNECT_DELAY ticks |
962 | ; and therefore is ready to be configured. |
962 | ; and therefore is ready to be configured. |
963 | ; ecx = port, esi -> ehci_controller, edi -> EHCI MMIO |
963 | ; ecx = port, esi -> ehci_controller, edi -> EHCI MMIO |
964 | proc ehci_new_port |
964 | proc ehci_new_port |
965 | ; 1. If the device operates at low-speed, just release it to a companion. |
965 | ; 1. If the device operates at low-speed, just release it to a companion. |
966 | mov eax, [edi+EhciPortsReg+ecx*4] |
966 | mov eax, [edi+EhciPortsReg+ecx*4] |
967 | DEBUGF 1,'K : [%d] EHCI %x port %d state is %x\n',[timer_ticks],esi,ecx,eax |
967 | DEBUGF 1,'K : [%d] EHCI %x port %d state is %x\n',[timer_ticks],esi,ecx,eax |
968 | mov edx, eax |
968 | mov edx, eax |
969 | and ah, 0Ch |
969 | and ah, 0Ch |
970 | cmp ah, 4 |
970 | cmp ah, 4 |
971 | jz .low_speed |
971 | jz .low_speed |
972 | ; 2. Devices operating at full-speed and high-speed must now have ah == 8. |
972 | ; 2. Devices operating at full-speed and high-speed must now have ah == 8. |
973 | ; Some broken hardware asserts both D+ and D- even after initial decoupling; |
973 | ; Some broken hardware asserts both D+ and D- even after initial decoupling; |
974 | ; if so, stop initialization here, no sense in further actions. |
974 | ; if so, stop initialization here, no sense in further actions. |
975 | cmp ah, 0Ch |
975 | cmp ah, 0Ch |
976 | jz .se1 |
976 | jz .se1 |
977 | ; 3. If another port is resetting right now, mark this port as 'reset pending' |
977 | ; 3. If another port is resetting right now, mark this port as 'reset pending' |
978 | ; and return. |
978 | ; and return. |
979 | bts [esi+usb_controller.PendingPorts], ecx |
979 | bts [esi+usb_controller.PendingPorts], ecx |
980 | cmp [esi+usb_controller.ResettingPort], -1 |
980 | cmp [esi+usb_controller.ResettingPort], -1 |
981 | jnz .nothing |
981 | jnz .nothing |
982 | btr [esi+usb_controller.PendingPorts], ecx |
982 | btr [esi+usb_controller.PendingPorts], ecx |
983 | ; Otherwise, fall through to ohci_new_port.reset. |
983 | ; Otherwise, fall through to ohci_new_port.reset. |
984 | 984 | ||
985 | ; This function is called from ehci_new_port and usb_test_pending_port. |
985 | ; This function is called from ehci_new_port and usb_test_pending_port. |
986 | ; It starts reset signalling for the port. Note that in USB first stages |
986 | ; It starts reset signalling for the port. Note that in USB first stages |
987 | ; of configuration can not be done for several ports in parallel. |
987 | ; of configuration can not be done for several ports in parallel. |
988 | .reset: |
988 | .reset: |
989 | push edi |
989 | push edi |
990 | mov edi, [esi+ehci_controller.MMIOBase2-sizeof.ehci_controller] |
990 | mov edi, [esi+ehci_controller.MMIOBase2-sizeof.ehci_controller] |
991 | mov eax, [edi+EhciPortsReg+ecx*4] |
991 | mov eax, [edi+EhciPortsReg+ecx*4] |
992 | ; 1. Store information about resetting hub (roothub) and port. |
992 | ; 1. Store information about resetting hub (roothub) and port. |
993 | and [esi+usb_controller.ResettingHub], 0 |
993 | and [esi+usb_controller.ResettingHub], 0 |
994 | mov [esi+usb_controller.ResettingPort], cl |
994 | mov [esi+usb_controller.ResettingPort], cl |
995 | ; 2. Initiate reset signalling. |
995 | ; 2. Initiate reset signalling. |
996 | or ah, 1 |
996 | or ah, 1 |
997 | and al, not (4 or 2Ah) |
997 | and al, not (4 or 2Ah) |
998 | mov [edi+EhciPortsReg+ecx*4], eax |
998 | mov [edi+EhciPortsReg+ecx*4], eax |
999 | ; 3. Store the current time and set status to 1 = reset signalling active. |
999 | ; 3. Store the current time and set status to 1 = reset signalling active. |
1000 | mov eax, [timer_ticks] |
1000 | mov eax, [timer_ticks] |
1001 | mov [esi+usb_controller.ResetTime], eax |
1001 | mov [esi+usb_controller.ResetTime], eax |
1002 | mov [esi+usb_controller.ResettingStatus], 1 |
1002 | mov [esi+usb_controller.ResettingStatus], 1 |
1003 | ; dbgstr 'high-speed or full-speed device, resetting' |
1003 | ; dbgstr 'high-speed or full-speed device, resetting' |
1004 | DEBUGF 1,'K : [%d] EHCI %x: port %d has HS or FS device, resetting\n',[timer_ticks],esi,ecx |
1004 | DEBUGF 1,'K : [%d] EHCI %x: port %d has HS or FS device, resetting\n',[timer_ticks],esi,ecx |
1005 | pop edi |
1005 | pop edi |
1006 | .nothing: |
1006 | .nothing: |
1007 | ret |
1007 | ret |
1008 | .low_speed: |
1008 | .low_speed: |
1009 | ; dbgstr 'low-speed device, releasing' |
1009 | ; dbgstr 'low-speed device, releasing' |
1010 | DEBUGF 1,'K : [%d] EHCI %x: port %d has LS device, releasing\n',[timer_ticks],esi,ecx |
1010 | DEBUGF 1,'K : [%d] EHCI %x: port %d has LS device, releasing\n',[timer_ticks],esi,ecx |
1011 | or dh, 20h |
1011 | or dh, 20h |
1012 | and dl, not 2Ah |
1012 | and dl, not 2Ah |
1013 | mov [edi+EhciPortsReg+ecx*4], edx |
1013 | mov [edi+EhciPortsReg+ecx*4], edx |
1014 | ret |
1014 | ret |
1015 | .se1: |
1015 | .se1: |
1016 | dbgstr 'SE1 after connect debounce. Broken hardware?' |
1016 | dbgstr 'SE1 after connect debounce. Broken hardware?' |
1017 | ret |
1017 | ret |
1018 | endp |
1018 | endp |
1019 | 1019 | ||
1020 | ; This procedure is called from several places in main USB code |
1020 | ; This procedure is called from several places in main USB code |
1021 | ; and allocates required packets for the given transfer. |
1021 | ; and allocates required packets for the given transfer. |
1022 | ; ebx = pipe, other parameters are passed through the stack: |
1022 | ; ebx = pipe, other parameters are passed through the stack: |
1023 | ; buffer,size = data to transfer |
1023 | ; buffer,size = data to transfer |
1024 | ; flags = same as in usb_open_pipe: bit 0 = allow short transfer, other bits reserved |
1024 | ; flags = same as in usb_open_pipe: bit 0 = allow short transfer, other bits reserved |
1025 | ; td = pointer to the current end-of-queue descriptor |
1025 | ; td = pointer to the current end-of-queue descriptor |
1026 | ; direction = |
1026 | ; direction = |
1027 | ; 0000b for normal transfers, |
1027 | ; 0000b for normal transfers, |
1028 | ; 1000b for control SETUP transfer, |
1028 | ; 1000b for control SETUP transfer, |
1029 | ; 1101b for control OUT transfer, |
1029 | ; 1101b for control OUT transfer, |
1030 | ; 1110b for control IN transfer |
1030 | ; 1110b for control IN transfer |
1031 | ; returns eax = pointer to the new end-of-queue descriptor |
1031 | ; returns eax = pointer to the new end-of-queue descriptor |
1032 | ; (not included in the queue itself) or 0 on error |
1032 | ; (not included in the queue itself) or 0 on error |
1033 | proc ehci_alloc_transfer stdcall uses edi, \ |
1033 | proc ehci_alloc_transfer stdcall uses edi, \ |
1034 | buffer:dword, size:dword, flags:dword, td:dword, direction:dword |
1034 | buffer:dword, size:dword, flags:dword, td:dword, direction:dword |
1035 | locals |
1035 | locals |
1036 | origTD dd ? |
1036 | origTD dd ? |
1037 | packetSize dd ? ; must be last variable, see usb_init_transfer |
1037 | packetSize dd ? ; must be last variable, see usb_init_transfer |
1038 | endl |
1038 | endl |
1039 | ; 1. Save original value of td: |
1039 | ; 1. Save original value of td: |
1040 | ; it will be useful for rollback if something would fail. |
1040 | ; it will be useful for rollback if something would fail. |
1041 | mov eax, [td] |
1041 | mov eax, [td] |
1042 | mov [origTD], eax |
1042 | mov [origTD], eax |
1043 | ; One transfer descriptor can describe up to 5 pages. |
1043 | ; One transfer descriptor can describe up to 5 pages. |
1044 | ; In the worst case (when the buffer is something*1000h+0FFFh) |
1044 | ; In the worst case (when the buffer is something*1000h+0FFFh) |
1045 | ; this corresponds to 4001h bytes. If the requested size is |
1045 | ; this corresponds to 4001h bytes. If the requested size is |
1046 | ; greater, we should split the transfer into several descriptors. |
1046 | ; greater, we should split the transfer into several descriptors. |
1047 | ; Boundaries to split must be multiples of endpoint transfer size |
1047 | ; Boundaries to split must be multiples of endpoint transfer size |
1048 | ; to avoid short packets except in the end of the transfer, |
1048 | ; to avoid short packets except in the end of the transfer. |
- | 1049 | cmp [size], 4001h |
|
1049 | ; 4000h is always a good value. |
1050 | jbe .lastpacket |
1050 | ; 2. While the remaining data cannot fit in one descriptor, |
1051 | ; 2. While the remaining data cannot fit in one descriptor, |
1051 | ; allocate full descriptors (of maximal possible size). |
1052 | ; allocate full descriptors (of maximal possible size). |
- | 1053 | ; 2a. Calculate size of one descriptor: must be a multiple of transfer size |
|
- | 1054 | ; and must be not greater than 4001h. |
|
- | 1055 | movzx ecx, word [ebx+ohci_pipe.Flags+2-sizeof.ohci_pipe] |
|
1052 | mov edi, 4000h |
1056 | mov eax, 4001h |
- | 1057 | xor edx, edx |
|
- | 1058 | mov edi, eax |
|
- | 1059 | div ecx |
|
- | 1060 | sub edi, edx |
|
1053 | mov [packetSize], edi |
1061 | mov [packetSize], edi |
1054 | .fullpackets: |
1062 | .fullpackets: |
1055 | cmp [size], edi |
- | |
1056 | jbe .lastpacket |
- | |
1057 | call ehci_alloc_packet |
1063 | call ehci_alloc_packet |
1058 | test eax, eax |
1064 | test eax, eax |
1059 | jz .fail |
1065 | jz .fail |
1060 | mov [td], eax |
1066 | mov [td], eax |
1061 | add [buffer], edi |
1067 | add [buffer], edi |
1062 | sub [size], edi |
1068 | sub [size], edi |
- | 1069 | cmp [size], 4001h |
|
1063 | jmp .fullpackets |
1070 | ja .fullpackets |
1064 | ; 3. The remaining data can fit in one packet; |
1071 | ; 3. The remaining data can fit in one packet; |
1065 | ; allocate the last descriptor with size = size of remaining data. |
1072 | ; allocate the last descriptor with size = size of remaining data. |
1066 | .lastpacket: |
1073 | .lastpacket: |
1067 | mov eax, [size] |
1074 | mov eax, [size] |
1068 | mov [packetSize], eax |
1075 | mov [packetSize], eax |
1069 | call ehci_alloc_packet |
1076 | call ehci_alloc_packet |
1070 | test eax, eax |
1077 | test eax, eax |
1071 | jz .fail |
1078 | jz .fail |
1072 | ; 9. Update flags in the last packet. |
1079 | ; 9. Update flags in the last packet. |
1073 | mov edx, [flags] |
1080 | mov edx, [flags] |
1074 | mov [ecx+ehci_gtd.Flags-sizeof.ehci_gtd], edx |
1081 | mov [ecx+ehci_gtd.Flags-sizeof.ehci_gtd], edx |
1075 | ; 10. Fill AlternateNextTD field in all allocated TDs. |
1082 | ; 10. Fill AlternateNextTD field in all allocated TDs. |
1076 | ; If the caller says that short transfer is ok, the queue must advance to |
1083 | ; If the caller says that short transfer is ok, the queue must advance to |
1077 | ; the next descriptor, which is in eax. |
1084 | ; the next descriptor, which is in eax. |
1078 | ; Otherwise, the queue should stop, so make AlternateNextTD point to |
1085 | ; Otherwise, the queue should stop, so make AlternateNextTD point to |
1079 | ; always-inactive descriptor StopQueueTD. |
1086 | ; always-inactive descriptor StopQueueTD. |
1080 | push eax |
1087 | push eax |
1081 | test dl, 1 |
1088 | test dl, 1 |
1082 | jz .disable_short |
1089 | jz .disable_short |
1083 | sub eax, sizeof.ehci_gtd |
1090 | sub eax, sizeof.ehci_gtd |
1084 | jmp @f |
1091 | jmp @f |
1085 | .disable_short: |
1092 | .disable_short: |
1086 | mov eax, [ebx+usb_pipe.Controller] |
1093 | mov eax, [ebx+usb_pipe.Controller] |
1087 | add eax, ehci_controller.StopQueueTD - sizeof.ehci_controller |
1094 | add eax, ehci_controller.StopQueueTD - sizeof.ehci_controller |
1088 | @@: |
1095 | @@: |
1089 | call get_phys_addr |
1096 | call get_phys_addr |
1090 | mov edx, [origTD] |
1097 | mov edx, [origTD] |
1091 | @@: |
1098 | @@: |
1092 | cmp edx, [esp] |
1099 | cmp edx, [esp] |
1093 | jz @f |
1100 | jz @f |
1094 | mov [edx+ehci_gtd.AlternateNextTD-sizeof.ehci_gtd], eax |
1101 | mov [edx+ehci_gtd.AlternateNextTD-sizeof.ehci_gtd], eax |
1095 | mov edx, [edx+usb_gtd.NextVirt] |
1102 | mov edx, [edx+usb_gtd.NextVirt] |
1096 | jmp @b |
1103 | jmp @b |
1097 | @@: |
1104 | @@: |
1098 | pop eax |
1105 | pop eax |
1099 | ret |
1106 | ret |
1100 | .fail: |
1107 | .fail: |
1101 | mov edi, ehci_hardware_func |
1108 | mov edi, ehci_hardware_func |
1102 | mov eax, [td] |
1109 | mov eax, [td] |
1103 | stdcall usb_undo_tds, [origTD] |
1110 | stdcall usb_undo_tds, [origTD] |
1104 | xor eax, eax |
1111 | xor eax, eax |
1105 | ret |
1112 | ret |
1106 | endp |
1113 | endp |
1107 | 1114 | ||
1108 | ; Helper procedure for ehci_alloc_transfer. |
1115 | ; Helper procedure for ehci_alloc_transfer. |
1109 | ; Allocates and initializes one transfer descriptor. |
1116 | ; Allocates and initializes one transfer descriptor. |
1110 | ; ebx = pipe, other parameters are passed through the stack; |
1117 | ; ebx = pipe, other parameters are passed through the stack; |
1111 | ; fills the current last descriptor and |
1118 | ; fills the current last descriptor and |
1112 | ; returns eax = next descriptor (not filled). |
1119 | ; returns eax = next descriptor (not filled). |
1113 | proc ehci_alloc_packet |
1120 | proc ehci_alloc_packet |
1114 | ; inherit some variables from the parent ehci_alloc_transfer |
1121 | ; inherit some variables from the parent ehci_alloc_transfer |
1115 | virtual at ebp-8 |
1122 | virtual at ebp-8 |
1116 | .origTD dd ? |
1123 | .origTD dd ? |
1117 | .packetSize dd ? |
1124 | .packetSize dd ? |
1118 | rd 2 |
1125 | rd 2 |
1119 | .buffer dd ? |
1126 | .buffer dd ? |
1120 | .transferSize dd ? |
1127 | .transferSize dd ? |
1121 | .Flags dd ? |
1128 | .Flags dd ? |
1122 | .td dd ? |
1129 | .td dd ? |
1123 | .direction dd ? |
1130 | .direction dd ? |
1124 | end virtual |
1131 | end virtual |
1125 | ; 1. Allocate the next TD. |
1132 | ; 1. Allocate the next TD. |
1126 | call ehci_alloc_td |
1133 | call ehci_alloc_td |
1127 | test eax, eax |
1134 | test eax, eax |
1128 | jz .nothing |
1135 | jz .nothing |
1129 | ; 2. Initialize controller-independent parts of both TDs. |
1136 | ; 2. Initialize controller-independent parts of both TDs. |
1130 | push eax |
1137 | push eax |
1131 | call usb_init_transfer |
1138 | call usb_init_transfer |
1132 | pop eax |
1139 | pop eax |
1133 | ; 3. Copy PID to the new descriptor. |
1140 | ; 3. Copy PID to the new descriptor. |
1134 | mov edx, [ecx+ehci_gtd.Token-sizeof.ehci_gtd] |
1141 | mov edx, [ecx+ehci_gtd.Token-sizeof.ehci_gtd] |
1135 | mov [eax+ehci_gtd.Token-sizeof.ehci_gtd], edx |
1142 | mov [eax+ehci_gtd.Token-sizeof.ehci_gtd], edx |
1136 | mov [eax+ehci_gtd.NextTD-sizeof.ehci_gtd], 1 |
1143 | mov [eax+ehci_gtd.NextTD-sizeof.ehci_gtd], 1 |
1137 | mov [eax+ehci_gtd.AlternateNextTD-sizeof.ehci_gtd], 1 |
1144 | mov [eax+ehci_gtd.AlternateNextTD-sizeof.ehci_gtd], 1 |
1138 | ; 4. Save the returned value (next descriptor). |
1145 | ; 4. Save the returned value (next descriptor). |
1139 | push eax |
1146 | push eax |
1140 | ; 5. Store the physical address of the next descriptor. |
1147 | ; 5. Store the physical address of the next descriptor. |
1141 | sub eax, sizeof.ehci_gtd |
1148 | sub eax, sizeof.ehci_gtd |
1142 | call get_phys_addr |
1149 | call get_phys_addr |
1143 | mov [ecx+ehci_gtd.NextTD-sizeof.ehci_gtd], eax |
1150 | mov [ecx+ehci_gtd.NextTD-sizeof.ehci_gtd], eax |
1144 | ; 6. For zero-length transfers, store zero in all fields for buffer addresses. |
1151 | ; 6. For zero-length transfers, store zero in all fields for buffer addresses. |
1145 | ; Otherwise, fill them with real values. |
1152 | ; Otherwise, fill them with real values. |
1146 | xor eax, eax |
1153 | xor eax, eax |
1147 | mov [ecx+ehci_gtd.Flags-sizeof.ehci_gtd], eax |
1154 | mov [ecx+ehci_gtd.Flags-sizeof.ehci_gtd], eax |
1148 | repeat 10 |
1155 | repeat 10 |
1149 | mov [ecx+ehci_gtd.BufferPointers-sizeof.ehci_gtd+(%-1)*4], eax |
1156 | mov [ecx+ehci_gtd.BufferPointers-sizeof.ehci_gtd+(%-1)*4], eax |
1150 | end repeat |
1157 | end repeat |
1151 | cmp [.packetSize], eax |
1158 | cmp [.packetSize], eax |
1152 | jz @f |
1159 | jz @f |
1153 | mov eax, [.buffer] |
1160 | mov eax, [.buffer] |
1154 | call get_phys_addr |
1161 | call get_phys_addr |
1155 | mov [ecx+ehci_gtd.BufferPointers-sizeof.ehci_gtd], eax |
1162 | mov [ecx+ehci_gtd.BufferPointers-sizeof.ehci_gtd], eax |
1156 | and eax, 0xFFF |
1163 | and eax, 0xFFF |
1157 | mov edx, [.packetSize] |
1164 | mov edx, [.packetSize] |
1158 | add edx, eax |
1165 | add edx, eax |
1159 | sub edx, 0x1000 |
1166 | sub edx, 0x1000 |
1160 | jbe @f |
1167 | jbe @f |
1161 | mov eax, [.buffer] |
1168 | mov eax, [.buffer] |
1162 | add eax, 0x1000 |
1169 | add eax, 0x1000 |
1163 | call get_pg_addr |
1170 | call get_pg_addr |
1164 | mov [ecx+ehci_gtd.BufferPointers+4-sizeof.ehci_gtd], eax |
1171 | mov [ecx+ehci_gtd.BufferPointers+4-sizeof.ehci_gtd], eax |
1165 | sub edx, 0x1000 |
1172 | sub edx, 0x1000 |
1166 | jbe @f |
1173 | jbe @f |
1167 | mov eax, [.buffer] |
1174 | mov eax, [.buffer] |
1168 | add eax, 0x2000 |
1175 | add eax, 0x2000 |
1169 | call get_pg_addr |
1176 | call get_pg_addr |
1170 | mov [ecx+ehci_gtd.BufferPointers+8-sizeof.ehci_gtd], eax |
1177 | mov [ecx+ehci_gtd.BufferPointers+8-sizeof.ehci_gtd], eax |
1171 | sub edx, 0x1000 |
1178 | sub edx, 0x1000 |
1172 | jbe @f |
1179 | jbe @f |
1173 | mov eax, [.buffer] |
1180 | mov eax, [.buffer] |
1174 | add eax, 0x3000 |
1181 | add eax, 0x3000 |
1175 | call get_pg_addr |
1182 | call get_pg_addr |
1176 | mov [ecx+ehci_gtd.BufferPointers+12-sizeof.ehci_gtd], eax |
1183 | mov [ecx+ehci_gtd.BufferPointers+12-sizeof.ehci_gtd], eax |
1177 | sub edx, 0x1000 |
1184 | sub edx, 0x1000 |
1178 | jbe @f |
1185 | jbe @f |
1179 | mov eax, [.buffer] |
1186 | mov eax, [.buffer] |
1180 | add eax, 0x4000 |
1187 | add eax, 0x4000 |
1181 | call get_pg_addr |
1188 | call get_pg_addr |
1182 | mov [ecx+ehci_gtd.BufferPointers+16-sizeof.ehci_gtd], eax |
1189 | mov [ecx+ehci_gtd.BufferPointers+16-sizeof.ehci_gtd], eax |
1183 | @@: |
1190 | @@: |
1184 | ; 7. Fill Token field: |
1191 | ; 7. Fill Token field: |
1185 | ; set Status = 0 (inactive, ehci_insert_transfer would mark everything active); |
1192 | ; set Status = 0 (inactive, ehci_insert_transfer would mark everything active); |
1186 | ; keep current PID if [.direction] is zero, use two lower bits of [.direction] |
1193 | ; keep current PID if [.direction] is zero, use two lower bits of [.direction] |
1187 | ; otherwise shifted as (0|1|2) -> (2|0|1); |
1194 | ; otherwise shifted as (0|1|2) -> (2|0|1); |
1188 | ; set error counter to 3; |
1195 | ; set error counter to 3; |
1189 | ; set current page to 0; |
1196 | ; set current page to 0; |
1190 | ; do not interrupt on complete (ehci_insert_transfer sets this bit where needed); |
1197 | ; do not interrupt on complete (ehci_insert_transfer sets this bit where needed); |
1191 | ; set DataToggle to bit 2 of [.direction]. |
1198 | ; set DataToggle to bit 2 of [.direction]. |
1192 | mov eax, [ecx+ehci_gtd.Token-sizeof.ehci_gtd] |
1199 | mov eax, [ecx+ehci_gtd.Token-sizeof.ehci_gtd] |
1193 | and eax, 300h ; keep PID code |
1200 | and eax, 300h ; keep PID code |
1194 | mov edx, [.direction] |
1201 | mov edx, [.direction] |
1195 | test edx, edx |
1202 | test edx, edx |
1196 | jz .haspid |
1203 | jz .haspid |
1197 | and edx, 3 |
1204 | and edx, 3 |
1198 | dec edx |
1205 | dec edx |
1199 | jns @f |
1206 | jns @f |
1200 | add edx, 3 |
1207 | add edx, 3 |
1201 | @@: |
1208 | @@: |
1202 | mov ah, dl |
1209 | mov ah, dl |
1203 | mov edx, [.direction] |
1210 | mov edx, [.direction] |
1204 | and edx, not 3 |
1211 | and edx, not 3 |
1205 | shl edx, 29 |
1212 | shl edx, 29 |
1206 | or eax, edx |
1213 | or eax, edx |
1207 | .haspid: |
1214 | .haspid: |
1208 | or eax, 0C00h |
1215 | or eax, 0C00h |
1209 | mov edx, [.packetSize] |
1216 | mov edx, [.packetSize] |
1210 | shl edx, 16 |
1217 | shl edx, 16 |
1211 | or eax, edx |
1218 | or eax, edx |
1212 | mov [ecx+ehci_gtd.Token-sizeof.ehci_gtd], eax |
1219 | mov [ecx+ehci_gtd.Token-sizeof.ehci_gtd], eax |
1213 | ; 4. Restore the returned value saved in step 2. |
1220 | ; 4. Restore the returned value saved in step 2. |
1214 | pop eax |
1221 | pop eax |
1215 | .nothing: |
1222 | .nothing: |
1216 | ret |
1223 | ret |
1217 | endp |
1224 | endp |
1218 | 1225 | ||
1219 | ; This procedure is called from several places in main USB code |
1226 | ; This procedure is called from several places in main USB code |
1220 | ; and activates the transfer which was previously allocated by |
1227 | ; and activates the transfer which was previously allocated by |
1221 | ; ehci_alloc_transfer. |
1228 | ; ehci_alloc_transfer. |
1222 | ; ecx -> last descriptor for the transfer, ebx -> usb_pipe |
1229 | ; ecx -> last descriptor for the transfer, ebx -> usb_pipe |
1223 | proc ehci_insert_transfer |
1230 | proc ehci_insert_transfer |
1224 | or byte [ecx+ehci_gtd.Token+1-sizeof.ehci_gtd], 80h ; set IOC bit |
1231 | or byte [ecx+ehci_gtd.Token+1-sizeof.ehci_gtd], 80h ; set IOC bit |
1225 | mov eax, [esp+4] |
1232 | mov eax, [esp+4] |
1226 | .activate: |
1233 | .activate: |
1227 | or byte [eax+ehci_gtd.Token-sizeof.ehci_gtd], 80h ; set Active bit |
1234 | or byte [eax+ehci_gtd.Token-sizeof.ehci_gtd], 80h ; set Active bit |
1228 | cmp eax, ecx |
1235 | cmp eax, ecx |
1229 | mov eax, [eax+usb_gtd.NextVirt] |
1236 | mov eax, [eax+usb_gtd.NextVirt] |
1230 | jnz .activate |
1237 | jnz .activate |
1231 | ret |
1238 | ret |
1232 | endp |
1239 | endp |
1233 | 1240 | ||
1234 | ; This function is called from ehci_process_deferred when |
1241 | ; This function is called from ehci_process_deferred when |
1235 | ; reset signalling for a new device needs to be finished. |
1242 | ; reset signalling for a new device needs to be finished. |
1236 | proc ehci_port_reset_done |
1243 | proc ehci_port_reset_done |
1237 | movzx ecx, [esi+usb_controller.ResettingPort] |
1244 | movzx ecx, [esi+usb_controller.ResettingPort] |
1238 | and dword [edi+EhciPortsReg+ecx*4], not 12Ah |
1245 | and dword [edi+EhciPortsReg+ecx*4], not 12Ah |
1239 | mov eax, [timer_ticks] |
1246 | mov eax, [timer_ticks] |
1240 | mov [esi+usb_controller.ResetTime], eax |
1247 | mov [esi+usb_controller.ResetTime], eax |
1241 | mov [esi+usb_controller.ResettingStatus], 2 |
1248 | mov [esi+usb_controller.ResettingStatus], 2 |
1242 | DEBUGF 1,'K : [%d] EHCI %x: reset port %d done\n',[timer_ticks],esi,ecx |
1249 | DEBUGF 1,'K : [%d] EHCI %x: reset port %d done\n',[timer_ticks],esi,ecx |
1243 | ret |
1250 | ret |
1244 | endp |
1251 | endp |
1245 | 1252 | ||
1246 | ; This function is called from ehci_process_deferred when |
1253 | ; This function is called from ehci_process_deferred when |
1247 | ; a new device has been reset, recovered after reset and needs to be configured. |
1254 | ; a new device has been reset, recovered after reset and needs to be configured. |
1248 | proc ehci_port_init |
1255 | proc ehci_port_init |
1249 | ; 1. Get the status and set it to zero. |
1256 | ; 1. Get the status and set it to zero. |
1250 | ; If reset has been failed (device disconnected during reset), |
1257 | ; If reset has been failed (device disconnected during reset), |
1251 | ; continue to next device (if there is one). |
1258 | ; continue to next device (if there is one). |
1252 | xor eax, eax |
1259 | xor eax, eax |
1253 | xchg al, [esi+usb_controller.ResettingStatus] |
1260 | xchg al, [esi+usb_controller.ResettingStatus] |
1254 | test al, al |
1261 | test al, al |
1255 | js usb_test_pending_port |
1262 | js usb_test_pending_port |
1256 | ; 2. Get the port status. High-speed devices should be now enabled, |
1263 | ; 2. Get the port status. High-speed devices should be now enabled, |
1257 | ; full-speed devices are left disabled; |
1264 | ; full-speed devices are left disabled; |
1258 | ; if the port is disabled, release it to a companion and continue to |
1265 | ; if the port is disabled, release it to a companion and continue to |
1259 | ; next device (if there is one). |
1266 | ; next device (if there is one). |
1260 | movzx ecx, [esi+usb_controller.ResettingPort] |
1267 | movzx ecx, [esi+usb_controller.ResettingPort] |
1261 | mov eax, [edi+EhciPortsReg+ecx*4] |
1268 | mov eax, [edi+EhciPortsReg+ecx*4] |
1262 | DEBUGF 1,'K : [%d] EHCI %x status of port %d is %x\n',[timer_ticks],esi,ecx,eax |
1269 | DEBUGF 1,'K : [%d] EHCI %x status of port %d is %x\n',[timer_ticks],esi,ecx,eax |
1263 | test al, 4 |
1270 | test al, 4 |
1264 | jnz @f |
1271 | jnz @f |
1265 | ; DEBUGF 1,'K : USB port disabled after reset, status = %x\n',eax |
1272 | ; DEBUGF 1,'K : USB port disabled after reset, status = %x\n',eax |
1266 | dbgstr 'releasing to companion' |
1273 | dbgstr 'releasing to companion' |
1267 | or ah, 20h |
1274 | or ah, 20h |
1268 | mov [edi+EhciPortsReg+ecx*4], eax |
1275 | mov [edi+EhciPortsReg+ecx*4], eax |
1269 | jmp usb_test_pending_port |
1276 | jmp usb_test_pending_port |
1270 | @@: |
1277 | @@: |
1271 | ; 3. Call the worker procedure to notify the protocol layer |
1278 | ; 3. Call the worker procedure to notify the protocol layer |
1272 | ; about new EHCI device. It is high-speed. |
1279 | ; about new EHCI device. It is high-speed. |
1273 | movi eax, USB_SPEED_HS |
1280 | movi eax, USB_SPEED_HS |
1274 | call ehci_new_device |
1281 | call ehci_new_device |
1275 | test eax, eax |
1282 | test eax, eax |
1276 | jnz .nothing |
1283 | jnz .nothing |
1277 | ; 4. If something at the protocol layer has failed |
1284 | ; 4. If something at the protocol layer has failed |
1278 | ; (no memory, no bus address), disable the port and stop the initialization. |
1285 | ; (no memory, no bus address), disable the port and stop the initialization. |
1279 | .disable_exit: |
1286 | .disable_exit: |
1280 | and dword [edi+EhciPortsReg+ecx*4], not (4 or 2Ah) |
1287 | and dword [edi+EhciPortsReg+ecx*4], not (4 or 2Ah) |
1281 | jmp usb_test_pending_port |
1288 | jmp usb_test_pending_port |
1282 | .nothing: |
1289 | .nothing: |
1283 | ret |
1290 | ret |
1284 | endp |
1291 | endp |
1285 | 1292 | ||
1286 | ; This procedure is called from ehci_port_init and from hub support code |
1293 | ; This procedure is called from ehci_port_init and from hub support code |
1287 | ; when a new device is connected and has been reset. |
1294 | ; when a new device is connected and has been reset. |
1288 | ; It calls usb_new_device at the protocol layer with correct parameters. |
1295 | ; It calls usb_new_device at the protocol layer with correct parameters. |
1289 | ; in: esi -> usb_controller, eax = speed. |
1296 | ; in: esi -> usb_controller, eax = speed. |
1290 | proc ehci_new_device |
1297 | proc ehci_new_device |
1291 | push ebx ecx ; save used registers (ecx is important for ehci_port_init) |
1298 | push ebx ecx ; save used registers (ecx is important for ehci_port_init) |
1292 | ; 1. Store the speed for the protocol layer. |
1299 | ; 1. Store the speed for the protocol layer. |
1293 | mov [esi+usb_controller.ResettingSpeed], al |
1300 | mov [esi+usb_controller.ResettingSpeed], al |
1294 | ; 2. Shift speed bits to the proper place in ehci_pipe.Token. |
1301 | ; 2. Shift speed bits to the proper place in ehci_pipe.Token. |
1295 | shl eax, 12 |
1302 | shl eax, 12 |
1296 | ; 3. For high-speed devices, go to step 5 with edx = 0. |
1303 | ; 3. For high-speed devices, go to step 5 with edx = 0. |
1297 | xor edx, edx |
1304 | xor edx, edx |
1298 | cmp ah, USB_SPEED_HS shl (12-8) |
1305 | cmp ah, USB_SPEED_HS shl (12-8) |
1299 | jz .common |
1306 | jz .common |
1300 | ; 4. For low-speed and full-speed devices, fill address:port |
1307 | ; 4. For low-speed and full-speed devices, fill address:port |
1301 | ; of the last high-speed hub (the closest to the device hub) |
1308 | ; of the last high-speed hub (the closest to the device hub) |
1302 | ; for split transactions, and set ControlEndpoint bit in eax; |
1309 | ; for split transactions, and set ControlEndpoint bit in eax; |
1303 | ; ehci_init_pipe assumes that the parent pipe is a control pipe. |
1310 | ; ehci_init_pipe assumes that the parent pipe is a control pipe. |
1304 | movzx ecx, [esi+usb_controller.ResettingPort] |
1311 | movzx ecx, [esi+usb_controller.ResettingPort] |
1305 | mov edx, [esi+usb_controller.ResettingHub] |
1312 | mov edx, [esi+usb_controller.ResettingHub] |
1306 | push eax |
1313 | push eax |
1307 | .find_hs_hub: |
1314 | .find_hs_hub: |
1308 | mov eax, [edx+usb_hub.ConfigPipe] |
1315 | mov eax, [edx+usb_hub.ConfigPipe] |
1309 | mov eax, [eax+usb_pipe.DeviceData] |
1316 | mov eax, [eax+usb_pipe.DeviceData] |
1310 | cmp [eax+usb_device_data.Speed], USB_SPEED_HS |
1317 | cmp [eax+usb_device_data.Speed], USB_SPEED_HS |
1311 | jz .found_hs_hub |
1318 | jz .found_hs_hub |
1312 | movzx ecx, [eax+usb_device_data.Port] |
1319 | movzx ecx, [eax+usb_device_data.Port] |
1313 | mov edx, [eax+usb_device_data.Hub] |
1320 | mov edx, [eax+usb_device_data.Hub] |
1314 | jmp .find_hs_hub |
1321 | jmp .find_hs_hub |
1315 | .found_hs_hub: |
1322 | .found_hs_hub: |
1316 | mov edx, [edx+usb_hub.ConfigPipe] |
1323 | mov edx, [edx+usb_hub.ConfigPipe] |
1317 | inc ecx |
1324 | inc ecx |
1318 | mov edx, [edx+ehci_pipe.Token-sizeof.ehci_pipe] |
1325 | mov edx, [edx+ehci_pipe.Token-sizeof.ehci_pipe] |
1319 | shl ecx, 23 |
1326 | shl ecx, 23 |
1320 | and edx, 7Fh |
1327 | and edx, 7Fh |
1321 | shl edx, 16 |
1328 | shl edx, 16 |
1322 | or edx, ecx ; ehci_pipe.Flags |
1329 | or edx, ecx ; ehci_pipe.Flags |
1323 | pop eax |
1330 | pop eax |
1324 | or eax, 1 shl 27 ; ehci_pipe.Token |
1331 | or eax, 1 shl 27 ; ehci_pipe.Token |
1325 | .common: |
1332 | .common: |
1326 | ; 5. Create pseudo-pipe in the stack. |
1333 | ; 5. Create pseudo-pipe in the stack. |
1327 | ; See ehci_init_pipe: only .Controller, .Token, .Flags fields are used. |
1334 | ; See ehci_init_pipe: only .Controller, .Token, .Flags fields are used. |
1328 | push esi ; usb_pipe.Controller |
1335 | push esi ; usb_pipe.Controller |
1329 | mov ecx, esp |
1336 | mov ecx, esp |
1330 | sub esp, sizeof.ehci_pipe - ehci_pipe.Flags - 4 |
1337 | sub esp, sizeof.ehci_pipe - ehci_pipe.Flags - 4 |
1331 | push edx ; ehci_pipe.Flags |
1338 | push edx ; ehci_pipe.Flags |
1332 | push eax ; ehci_pipe.Token |
1339 | push eax ; ehci_pipe.Token |
1333 | ; 6. Notify the protocol layer. |
1340 | ; 6. Notify the protocol layer. |
1334 | call usb_new_device |
1341 | call usb_new_device |
1335 | ; 7. Cleanup the stack after step 5 and return. |
1342 | ; 7. Cleanup the stack after step 5 and return. |
1336 | add esp, sizeof.ehci_pipe - ehci_pipe.Flags + 8 |
1343 | add esp, sizeof.ehci_pipe - ehci_pipe.Flags + 8 |
1337 | pop ecx ebx ; restore used registers |
1344 | pop ecx ebx ; restore used registers |
1338 | ret |
1345 | ret |
1339 | endp |
1346 | endp |
1340 | 1347 | ||
1341 | ; This procedure is called in the USB thread from usb_thread_proc, |
1348 | ; This procedure is called in the USB thread from usb_thread_proc, |
1342 | ; processes regular actions and those actions which can't be safely done |
1349 | ; processes regular actions and those actions which can't be safely done |
1343 | ; from interrupt handler. |
1350 | ; from interrupt handler. |
1344 | ; Returns maximal time delta before the next call. |
1351 | ; Returns maximal time delta before the next call. |
1345 | proc ehci_process_deferred |
1352 | proc ehci_process_deferred |
1346 | push ebx edi ; save used registers to be stdcall |
1353 | push ebx edi ; save used registers to be stdcall |
1347 | mov edi, [esi+ehci_controller.MMIOBase2-sizeof.ehci_controller] |
1354 | mov edi, [esi+ehci_controller.MMIOBase2-sizeof.ehci_controller] |
1348 | ; 1. Get the mask of events to process. |
1355 | ; 1. Get the mask of events to process. |
1349 | xor eax, eax |
1356 | xor eax, eax |
1350 | xchg eax, [esi+ehci_controller.DeferredActions-sizeof.ehci_controller] |
1357 | xchg eax, [esi+ehci_controller.DeferredActions-sizeof.ehci_controller] |
1351 | push eax |
1358 | push eax |
1352 | ; 2. Initialize the return value. |
1359 | ; 2. Initialize the return value. |
1353 | push -1 |
1360 | push -1 |
1354 | ; Handle roothub events. |
1361 | ; Handle roothub events. |
1355 | ; 3a. Test whether there are such events. |
1362 | ; 3a. Test whether there are such events. |
1356 | test al, 4 |
1363 | test al, 4 |
1357 | jz .skip_roothub |
1364 | jz .skip_roothub |
1358 | ; Status of some port has changed. Loop over all ports. |
1365 | ; Status of some port has changed. Loop over all ports. |
1359 | ; 3b. Prepare for the loop: start from port 0. |
1366 | ; 3b. Prepare for the loop: start from port 0. |
1360 | xor ecx, ecx |
1367 | xor ecx, ecx |
1361 | .portloop: |
1368 | .portloop: |
1362 | ; 3c. Get the port status and changes of it. |
1369 | ; 3c. Get the port status and changes of it. |
1363 | ; If there are no changes, just continue to the next port. |
1370 | ; If there are no changes, just continue to the next port. |
1364 | mov eax, [edi+EhciPortsReg+ecx*4] |
1371 | mov eax, [edi+EhciPortsReg+ecx*4] |
1365 | test al, 2Ah |
1372 | test al, 2Ah |
1366 | jz .nextport |
1373 | jz .nextport |
1367 | ; 3d. Clear change bits and read the status again. |
1374 | ; 3d. Clear change bits and read the status again. |
1368 | ; (It is possible, although quite unlikely, that some event occurs between |
1375 | ; (It is possible, although quite unlikely, that some event occurs between |
1369 | ; the first read and the clearing, invalidating the old status. If an event |
1376 | ; the first read and the clearing, invalidating the old status. If an event |
1370 | ; occurs after the clearing, we will not miss it, looking in the next scan. |
1377 | ; occurs after the clearing, we will not miss it, looking in the next scan. |
1371 | mov [edi+EhciPortsReg+ecx*4], eax |
1378 | mov [edi+EhciPortsReg+ecx*4], eax |
1372 | mov ebx, eax |
1379 | mov ebx, eax |
1373 | mov eax, [edi+EhciPortsReg+ecx*4] |
1380 | mov eax, [edi+EhciPortsReg+ecx*4] |
1374 | DEBUGF 1,'K : [%d] EHCI %x: status of port %d changed to %x\n',[timer_ticks],esi,ecx,ebx |
1381 | DEBUGF 1,'K : [%d] EHCI %x: status of port %d changed to %x\n',[timer_ticks],esi,ecx,ebx |
1375 | ; 3e. Handle overcurrent. |
1382 | ; 3e. Handle overcurrent. |
1376 | ; Note: that needs work. |
1383 | ; Note: that needs work. |
1377 | test bl, 20h ; overcurrent change |
1384 | test bl, 20h ; overcurrent change |
1378 | jz .noovercurrent |
1385 | jz .noovercurrent |
1379 | test al, 10h ; overcurrent active |
1386 | test al, 10h ; overcurrent active |
1380 | jz .noovercurrent |
1387 | jz .noovercurrent |
1381 | DEBUGF 1,'K : overcurrent at port %d\n',ecx |
1388 | DEBUGF 1,'K : overcurrent at port %d\n',ecx |
1382 | .noovercurrent: |
1389 | .noovercurrent: |
1383 | ; 3f. Handle changing of connection status. |
1390 | ; 3f. Handle changing of connection status. |
1384 | test bl, 2 |
1391 | test bl, 2 |
1385 | jz .nocsc |
1392 | jz .nocsc |
1386 | ; There was a connect or disconnect event at this port. |
1393 | ; There was a connect or disconnect event at this port. |
1387 | ; 3g. Disconnect the old device on this port, if any. |
1394 | ; 3g. Disconnect the old device on this port, if any. |
1388 | ; If the port was resetting, indicate fail; later stages will process it. |
1395 | ; If the port was resetting, indicate fail; later stages will process it. |
1389 | cmp [esi+usb_controller.ResettingHub], 0 |
1396 | cmp [esi+usb_controller.ResettingHub], 0 |
1390 | jnz @f |
1397 | jnz @f |
1391 | cmp cl, [esi+usb_controller.ResettingPort] |
1398 | cmp cl, [esi+usb_controller.ResettingPort] |
1392 | jnz @f |
1399 | jnz @f |
1393 | mov [esi+usb_controller.ResettingStatus], -1 |
1400 | mov [esi+usb_controller.ResettingStatus], -1 |
1394 | @@: |
1401 | @@: |
1395 | bts [esi+usb_controller.NewDisconnected], ecx |
1402 | bts [esi+usb_controller.NewDisconnected], ecx |
1396 | ; 3h. Change connected status. For the connection event, also store |
1403 | ; 3h. Change connected status. For the connection event, also store |
1397 | ; the connection time; any further processing is permitted only after |
1404 | ; the connection time; any further processing is permitted only after |
1398 | ; USB_CONNECT_DELAY ticks. |
1405 | ; USB_CONNECT_DELAY ticks. |
1399 | test al, 1 |
1406 | test al, 1 |
1400 | jz .disconnect |
1407 | jz .disconnect |
1401 | mov eax, [timer_ticks] |
1408 | mov eax, [timer_ticks] |
1402 | mov [esi+usb_controller.ConnectedTime+ecx*4], eax |
1409 | mov [esi+usb_controller.ConnectedTime+ecx*4], eax |
1403 | bts [esi+usb_controller.NewConnected], ecx |
1410 | bts [esi+usb_controller.NewConnected], ecx |
1404 | jmp .nextport |
1411 | jmp .nextport |
1405 | .disconnect: |
1412 | .disconnect: |
1406 | btr [esi+usb_controller.NewConnected], ecx |
1413 | btr [esi+usb_controller.NewConnected], ecx |
1407 | jmp .nextport |
1414 | jmp .nextport |
1408 | .nocsc: |
1415 | .nocsc: |
1409 | ; 3i. Handle port disabling. |
1416 | ; 3i. Handle port disabling. |
1410 | ; Note: that needs work. |
1417 | ; Note: that needs work. |
1411 | test al, 8 |
1418 | test al, 8 |
1412 | jz @f |
1419 | jz @f |
1413 | test al, 4 |
1420 | test al, 4 |
1414 | jz @f |
1421 | jz @f |
1415 | DEBUGF 1,'K : port %d disabled\n',ecx |
1422 | DEBUGF 1,'K : port %d disabled\n',ecx |
1416 | @@: |
1423 | @@: |
1417 | ; 3j. Continue the loop for the next port. |
1424 | ; 3j. Continue the loop for the next port. |
1418 | .nextport: |
1425 | .nextport: |
1419 | inc ecx |
1426 | inc ecx |
1420 | cmp ecx, [esi+usb_controller.NumPorts] |
1427 | cmp ecx, [esi+usb_controller.NumPorts] |
1421 | jb .portloop |
1428 | jb .portloop |
1422 | .skip_roothub: |
1429 | .skip_roothub: |
1423 | ; 4. Process disconnect events. This should be done after step 3 |
1430 | ; 4. Process disconnect events. This should be done after step 3 |
1424 | ; (which includes the first stage of disconnect processing). |
1431 | ; (which includes the first stage of disconnect processing). |
1425 | call usb_disconnect_stage2 |
1432 | call usb_disconnect_stage2 |
1426 | ; 5. Check for previously connected devices. |
1433 | ; 5. Check for previously connected devices. |
1427 | ; If there is a connected device which was connected less than |
1434 | ; If there is a connected device which was connected less than |
1428 | ; USB_CONNECT_DELAY ticks ago, plan to wake up when the delay will be over. |
1435 | ; USB_CONNECT_DELAY ticks ago, plan to wake up when the delay will be over. |
1429 | ; Otherwise, call ehci_new_port. |
1436 | ; Otherwise, call ehci_new_port. |
1430 | ; This should be done after step 3. |
1437 | ; This should be done after step 3. |
1431 | xor ecx, ecx |
1438 | xor ecx, ecx |
1432 | cmp [esi+usb_controller.NewConnected], ecx |
1439 | cmp [esi+usb_controller.NewConnected], ecx |
1433 | jz .skip_newconnected |
1440 | jz .skip_newconnected |
1434 | .portloop2: |
1441 | .portloop2: |
1435 | bt [esi+usb_controller.NewConnected], ecx |
1442 | bt [esi+usb_controller.NewConnected], ecx |
1436 | jnc .noconnect |
1443 | jnc .noconnect |
1437 | mov eax, [timer_ticks] |
1444 | mov eax, [timer_ticks] |
1438 | sub eax, [esi+usb_controller.ConnectedTime+ecx*4] |
1445 | sub eax, [esi+usb_controller.ConnectedTime+ecx*4] |
1439 | sub eax, USB_CONNECT_DELAY |
1446 | sub eax, USB_CONNECT_DELAY |
1440 | jge .connected |
1447 | jge .connected |
1441 | neg eax |
1448 | neg eax |
1442 | cmp [esp], eax |
1449 | cmp [esp], eax |
1443 | jb .nextport2 |
1450 | jb .nextport2 |
1444 | mov [esp], eax |
1451 | mov [esp], eax |
1445 | jmp .nextport2 |
1452 | jmp .nextport2 |
1446 | .connected: |
1453 | .connected: |
1447 | btr [esi+usb_controller.NewConnected], ecx |
1454 | btr [esi+usb_controller.NewConnected], ecx |
1448 | call ehci_new_port |
1455 | call ehci_new_port |
1449 | jmp .portloop2 |
1456 | jmp .portloop2 |
1450 | .noconnect: |
1457 | .noconnect: |
1451 | .nextport2: |
1458 | .nextport2: |
1452 | inc ecx |
1459 | inc ecx |
1453 | cmp ecx, [esi+usb_controller.NumPorts] |
1460 | cmp ecx, [esi+usb_controller.NumPorts] |
1454 | jb .portloop2 |
1461 | jb .portloop2 |
1455 | .skip_newconnected: |
1462 | .skip_newconnected: |
1456 | ; 6. Process wait lists. |
1463 | ; 6. Process wait lists. |
1457 | ; 6a. Periodic endpoints. |
1464 | ; 6a. Periodic endpoints. |
1458 | ; If a request is pending >8 microframes, satisfy it. |
1465 | ; If a request is pending >8 microframes, satisfy it. |
1459 | ; If a request is pending <=8 microframes, schedule next wakeup in 0.01s. |
1466 | ; If a request is pending <=8 microframes, schedule next wakeup in 0.01s. |
1460 | mov eax, [esi+usb_controller.WaitPipeRequestPeriodic] |
1467 | mov eax, [esi+usb_controller.WaitPipeRequestPeriodic] |
1461 | cmp eax, [esi+usb_controller.ReadyPipeHeadPeriodic] |
1468 | cmp eax, [esi+usb_controller.ReadyPipeHeadPeriodic] |
1462 | jz .noperiodic |
1469 | jz .noperiodic |
1463 | mov edx, [edi+EhciFrameIndexReg] |
1470 | mov edx, [edi+EhciFrameIndexReg] |
1464 | sub edx, [esi+usb_controller.StartWaitFrame] |
1471 | sub edx, [esi+usb_controller.StartWaitFrame] |
1465 | and edx, 0x3FFF |
1472 | and edx, 0x3FFF |
1466 | cmp edx, 8 |
1473 | cmp edx, 8 |
1467 | jbe @f |
1474 | jbe @f |
1468 | mov [esi+usb_controller.ReadyPipeHeadPeriodic], eax |
1475 | mov [esi+usb_controller.ReadyPipeHeadPeriodic], eax |
1469 | jmp .noperiodic |
1476 | jmp .noperiodic |
1470 | @@: |
1477 | @@: |
1471 | pop eax |
1478 | pop eax |
1472 | push 1 ; wakeup in 0.01 sec for next test |
1479 | push 1 ; wakeup in 0.01 sec for next test |
1473 | .noperiodic: |
1480 | .noperiodic: |
1474 | ; 6b. Asynchronous endpoints. |
1481 | ; 6b. Asynchronous endpoints. |
1475 | ; Satisfy a request when InterruptOnAsyncAdvance fired. |
1482 | ; Satisfy a request when InterruptOnAsyncAdvance fired. |
1476 | test byte [esp+4], 20h |
1483 | test byte [esp+4], 20h |
1477 | jz @f |
1484 | jz @f |
1478 | dbgstr 'async advance int' |
1485 | dbgstr 'async advance int' |
1479 | mov eax, [esi+usb_controller.WaitPipeRequestAsync] |
1486 | mov eax, [esi+usb_controller.WaitPipeRequestAsync] |
1480 | mov [esi+usb_controller.ReadyPipeHeadAsync], eax |
1487 | mov [esi+usb_controller.ReadyPipeHeadAsync], eax |
1481 | @@: |
1488 | @@: |
1482 | ; Some hardware in some (rarely) conditions set the status bit, |
1489 | ; Some hardware in some (rarely) conditions set the status bit, |
1483 | ; but just does not generate the corresponding interrupt. |
1490 | ; but just does not generate the corresponding interrupt. |
1484 | ; Force checking the status here. |
1491 | ; Force checking the status here. |
1485 | mov eax, [esi+usb_controller.WaitPipeRequestAsync] |
1492 | mov eax, [esi+usb_controller.WaitPipeRequestAsync] |
1486 | cmp [esi+usb_controller.ReadyPipeHeadAsync], eax |
1493 | cmp [esi+usb_controller.ReadyPipeHeadAsync], eax |
1487 | jz .noasync |
1494 | jz .noasync |
1488 | spin_lock_irq [esi+usb_controller.WaitSpinlock] |
1495 | spin_lock_irq [esi+usb_controller.WaitSpinlock] |
1489 | mov edx, [edi+EhciStatusReg] |
1496 | mov edx, [edi+EhciStatusReg] |
1490 | test dl, 20h |
1497 | test dl, 20h |
1491 | jz @f |
1498 | jz @f |
1492 | mov dword [edi+EhciStatusReg], 20h |
1499 | mov dword [edi+EhciStatusReg], 20h |
1493 | and dword [esi+ehci_controller.DeferredActions-sizeof.ehci_controller], not 20h |
1500 | and dword [esi+ehci_controller.DeferredActions-sizeof.ehci_controller], not 20h |
1494 | dbgstr 'warning: async advance int missed' |
1501 | dbgstr 'warning: async advance int missed' |
1495 | mov [esi+usb_controller.ReadyPipeHeadAsync], eax |
1502 | mov [esi+usb_controller.ReadyPipeHeadAsync], eax |
1496 | jmp .async_unlock |
1503 | jmp .async_unlock |
1497 | @@: |
1504 | @@: |
1498 | cmp dword [esp], 100 |
1505 | cmp dword [esp], 100 |
1499 | jb .async_unlock |
1506 | jb .async_unlock |
1500 | mov dword [esp], 100 |
1507 | mov dword [esp], 100 |
1501 | .async_unlock: |
1508 | .async_unlock: |
1502 | spin_unlock_irq [esi+usb_controller.WaitSpinlock] |
1509 | spin_unlock_irq [esi+usb_controller.WaitSpinlock] |
1503 | .noasync: |
1510 | .noasync: |
1504 | ; 7. Finalize transfers processed by hardware. |
1511 | ; 7. Finalize transfers processed by hardware. |
1505 | ; It is better to perform this step after step 4 (disconnect events), |
1512 | ; It is better to perform this step after step 4 (disconnect events), |
1506 | ; although not strictly obligatory. This way, an active transfer aborted |
1513 | ; although not strictly obligatory. This way, an active transfer aborted |
1507 | ; due to disconnect would be handled with more specific USB_STATUS_CLOSED, |
1514 | ; due to disconnect would be handled with more specific USB_STATUS_CLOSED, |
1508 | ; not USB_STATUS_NORESPONSE. |
1515 | ; not USB_STATUS_NORESPONSE. |
1509 | test byte [esp+4], 3 |
1516 | test byte [esp+4], 3 |
1510 | jz @f |
1517 | jz @f |
1511 | call ehci_process_updated_schedule |
1518 | call ehci_process_updated_schedule |
1512 | @@: |
1519 | @@: |
1513 | ; 8. Test whether reset signalling has been started and should be stopped now. |
1520 | ; 8. Test whether reset signalling has been started and should be stopped now. |
1514 | ; This must be done after step 7, because completion of some transfer could |
1521 | ; This must be done after step 7, because completion of some transfer could |
1515 | ; result in resetting a new port. |
1522 | ; result in resetting a new port. |
1516 | .test_reset: |
1523 | .test_reset: |
1517 | ; 8a. Test whether reset signalling is active. |
1524 | ; 8a. Test whether reset signalling is active. |
1518 | cmp [esi+usb_controller.ResettingStatus], 1 |
1525 | cmp [esi+usb_controller.ResettingStatus], 1 |
1519 | jnz .no_reset_in_progress |
1526 | jnz .no_reset_in_progress |
1520 | ; 8b. Yep. Test whether it should be stopped. |
1527 | ; 8b. Yep. Test whether it should be stopped. |
1521 | mov eax, [timer_ticks] |
1528 | mov eax, [timer_ticks] |
1522 | sub eax, [esi+usb_controller.ResetTime] |
1529 | sub eax, [esi+usb_controller.ResetTime] |
1523 | sub eax, USB_RESET_TIME |
1530 | sub eax, USB_RESET_TIME |
1524 | jge .reset_done |
1531 | jge .reset_done |
1525 | ; 8c. Not yet, but initiate wakeup in -eax ticks and exit this step. |
1532 | ; 8c. Not yet, but initiate wakeup in -eax ticks and exit this step. |
1526 | neg eax |
1533 | neg eax |
1527 | cmp [esp], eax |
1534 | cmp [esp], eax |
1528 | jb .skip_reset |
1535 | jb .skip_reset |
1529 | mov [esp], eax |
1536 | mov [esp], eax |
1530 | jmp .skip_reset |
1537 | jmp .skip_reset |
1531 | .reset_done: |
1538 | .reset_done: |
1532 | ; 8d. Yep, call the worker function and proceed to 8e. |
1539 | ; 8d. Yep, call the worker function and proceed to 8e. |
1533 | call ehci_port_reset_done |
1540 | call ehci_port_reset_done |
1534 | .no_reset_in_progress: |
1541 | .no_reset_in_progress: |
1535 | ; 8e. Test whether reset process is done, either successful or failed. |
1542 | ; 8e. Test whether reset process is done, either successful or failed. |
1536 | cmp [esi+usb_controller.ResettingStatus], 0 |
1543 | cmp [esi+usb_controller.ResettingStatus], 0 |
1537 | jz .skip_reset |
1544 | jz .skip_reset |
1538 | ; 8f. Yep. Test whether it should be stopped. |
1545 | ; 8f. Yep. Test whether it should be stopped. |
1539 | mov eax, [timer_ticks] |
1546 | mov eax, [timer_ticks] |
1540 | sub eax, [esi+usb_controller.ResetTime] |
1547 | sub eax, [esi+usb_controller.ResetTime] |
1541 | sub eax, USB_RESET_RECOVERY_TIME |
1548 | sub eax, USB_RESET_RECOVERY_TIME |
1542 | jge .reset_recovery_done |
1549 | jge .reset_recovery_done |
1543 | ; 8g. Not yet, but initiate wakeup in -eax ticks and exit this step. |
1550 | ; 8g. Not yet, but initiate wakeup in -eax ticks and exit this step. |
1544 | neg eax |
1551 | neg eax |
1545 | cmp [esp], eax |
1552 | cmp [esp], eax |
1546 | jb .skip_reset |
1553 | jb .skip_reset |
1547 | mov [esp], eax |
1554 | mov [esp], eax |
1548 | jmp .skip_reset |
1555 | jmp .skip_reset |
1549 | .reset_recovery_done: |
1556 | .reset_recovery_done: |
1550 | ; 8h. Yep, call the worker function. This could initiate another reset, |
1557 | ; 8h. Yep, call the worker function. This could initiate another reset, |
1551 | ; so return to the beginning of this step. |
1558 | ; so return to the beginning of this step. |
1552 | call ehci_port_init |
1559 | call ehci_port_init |
1553 | jmp .test_reset |
1560 | jmp .test_reset |
1554 | .skip_reset: |
1561 | .skip_reset: |
1555 | ; 9. Process wait-done notifications, test for new wait requests. |
1562 | ; 9. Process wait-done notifications, test for new wait requests. |
1556 | ; Note: that must be done after steps 4 and 7 which could create new requests. |
1563 | ; Note: that must be done after steps 4 and 7 which could create new requests. |
1557 | ; 9a. Call the worker function. |
1564 | ; 9a. Call the worker function. |
1558 | call usb_process_wait_lists |
1565 | call usb_process_wait_lists |
1559 | ; 9b. If it reports that an asynchronous endpoint should be removed, |
1566 | ; 9b. If it reports that an asynchronous endpoint should be removed, |
1560 | ; doorbell InterruptOnAsyncAdvance and schedule wakeup in 1s |
1567 | ; doorbell InterruptOnAsyncAdvance and schedule wakeup in 1s |
1561 | ; (sometimes it just does not fire). |
1568 | ; (sometimes it just does not fire). |
1562 | test al, 1 shl CONTROL_PIPE |
1569 | test al, 1 shl CONTROL_PIPE |
1563 | jz @f |
1570 | jz @f |
1564 | mov edx, [esi+usb_controller.WaitPipeListAsync] |
1571 | mov edx, [esi+usb_controller.WaitPipeListAsync] |
1565 | mov [esi+usb_controller.WaitPipeRequestAsync], edx |
1572 | mov [esi+usb_controller.WaitPipeRequestAsync], edx |
1566 | or dword [edi+EhciCommandReg], 1 shl 6 |
1573 | or dword [edi+EhciCommandReg], 1 shl 6 |
1567 | dbgstr 'async advance doorbell' |
1574 | dbgstr 'async advance doorbell' |
1568 | cmp dword [esp], 100 |
1575 | cmp dword [esp], 100 |
1569 | jb @f |
1576 | jb @f |
1570 | mov dword [esp], 100 |
1577 | mov dword [esp], 100 |
1571 | @@: |
1578 | @@: |
1572 | ; 9c. If it reports that a periodic endpoint should be removed, |
1579 | ; 9c. If it reports that a periodic endpoint should be removed, |
1573 | ; save the current frame and schedule wakeup in 0.01 sec. |
1580 | ; save the current frame and schedule wakeup in 0.01 sec. |
1574 | test al, 1 shl INTERRUPT_PIPE |
1581 | test al, 1 shl INTERRUPT_PIPE |
1575 | jz @f |
1582 | jz @f |
1576 | mov eax, [esi+usb_controller.WaitPipeListPeriodic] |
1583 | mov eax, [esi+usb_controller.WaitPipeListPeriodic] |
1577 | mov [esi+usb_controller.WaitPipeRequestPeriodic], eax |
1584 | mov [esi+usb_controller.WaitPipeRequestPeriodic], eax |
1578 | mov edx, [edi+EhciFrameIndexReg] |
1585 | mov edx, [edi+EhciFrameIndexReg] |
1579 | mov [esi+usb_controller.StartWaitFrame], edx |
1586 | mov [esi+usb_controller.StartWaitFrame], edx |
1580 | mov dword [esp], 1 ; wakeup in 0.01 sec for next test |
1587 | mov dword [esp], 1 ; wakeup in 0.01 sec for next test |
1581 | @@: |
1588 | @@: |
1582 | ; 10. Pop the return value, restore the stack after step 1 and return. |
1589 | ; 10. Pop the return value, restore the stack after step 1 and return. |
1583 | pop eax |
1590 | pop eax |
1584 | pop ecx |
1591 | pop ecx |
1585 | pop edi ebx ; restore used registers to be stdcall |
1592 | pop edi ebx ; restore used registers to be stdcall |
1586 | ret |
1593 | ret |
1587 | endp |
1594 | endp |
1588 | 1595 | ||
1589 | ; This procedure is called in the USB thread from ehci_process_deferred |
1596 | ; This procedure is called in the USB thread from ehci_process_deferred |
1590 | ; when EHCI IRQ handler has signalled that new IOC-packet was processed. |
1597 | ; when EHCI IRQ handler has signalled that new IOC-packet was processed. |
1591 | ; It scans all lists for completed packets and calls ehci_process_finalized_td |
1598 | ; It scans all lists for completed packets and calls ehci_process_finalized_td |
1592 | ; for those packets. |
1599 | ; for those packets. |
1593 | proc ehci_process_updated_schedule |
1600 | proc ehci_process_updated_schedule |
1594 | ; Important note: we cannot hold the list lock during callbacks, |
1601 | ; Important note: we cannot hold the list lock during callbacks, |
1595 | ; because callbacks sometimes open and/or close pipes and thus acquire/release |
1602 | ; because callbacks sometimes open and/or close pipes and thus acquire/release |
1596 | ; the corresponding lock itself. |
1603 | ; the corresponding lock itself. |
1597 | ; Fortunately, pipes can be finally freed only by another step of |
1604 | ; Fortunately, pipes can be finally freed only by another step of |
1598 | ; ehci_process_deferred, so all pipes existing at the start of this function |
1605 | ; ehci_process_deferred, so all pipes existing at the start of this function |
1599 | ; will be valid while this function is running. Some pipes can be removed |
1606 | ; will be valid while this function is running. Some pipes can be removed |
1600 | ; from the corresponding list, some pipes can be inserted; insert/remove |
1607 | ; from the corresponding list, some pipes can be inserted; insert/remove |
1601 | ; functions guarantee that traversing one list yields all pipes that were in |
1608 | ; functions guarantee that traversing one list yields all pipes that were in |
1602 | ; that list at the beginning of the traversing (possibly with some new pipes, |
1609 | ; that list at the beginning of the traversing (possibly with some new pipes, |
1603 | ; possibly without some new pipes, that doesn't matter). |
1610 | ; possibly without some new pipes, that doesn't matter). |
1604 | push edi |
1611 | push edi |
1605 | ; 1. Process all Periodic lists. |
1612 | ; 1. Process all Periodic lists. |
1606 | lea edi, [esi+ehci_controller.IntEDs-sizeof.ehci_controller+ehci_static_ep.SoftwarePart] |
1613 | lea edi, [esi+ehci_controller.IntEDs-sizeof.ehci_controller+ehci_static_ep.SoftwarePart] |
1607 | lea ebx, [esi+ehci_controller.IntEDs+63*sizeof.ehci_static_ep-sizeof.ehci_controller+ehci_static_ep.SoftwarePart] |
1614 | lea ebx, [esi+ehci_controller.IntEDs+63*sizeof.ehci_static_ep-sizeof.ehci_controller+ehci_static_ep.SoftwarePart] |
1608 | @@: |
1615 | @@: |
1609 | call ehci_process_updated_list |
1616 | call ehci_process_updated_list |
1610 | cmp edi, ebx |
1617 | cmp edi, ebx |
1611 | jnz @b |
1618 | jnz @b |
1612 | ; 2. Process the Control list. |
1619 | ; 2. Process the Control list. |
1613 | add edi, ehci_controller.ControlDelta |
1620 | add edi, ehci_controller.ControlDelta |
1614 | call ehci_process_updated_list |
1621 | call ehci_process_updated_list |
1615 | ; 3. Process the Bulk list. |
1622 | ; 3. Process the Bulk list. |
1616 | call ehci_process_updated_list |
1623 | call ehci_process_updated_list |
1617 | ; 4. Return. |
1624 | ; 4. Return. |
1618 | pop edi |
1625 | pop edi |
1619 | ret |
1626 | ret |
1620 | endp |
1627 | endp |
1621 | 1628 | ||
1622 | ; This procedure is called from ehci_process_updated_schedule, see comments there. |
1629 | ; This procedure is called from ehci_process_updated_schedule, see comments there. |
1623 | ; It processes one list, esi -> usb_controller, edi -> usb_static_ep, |
1630 | ; It processes one list, esi -> usb_controller, edi -> usb_static_ep, |
1624 | ; and advances edi to next head. |
1631 | ; and advances edi to next head. |
1625 | proc ehci_process_updated_list |
1632 | proc ehci_process_updated_list |
1626 | push ebx |
1633 | push ebx |
1627 | ; 1. Perform the external loop over all pipes. |
1634 | ; 1. Perform the external loop over all pipes. |
1628 | mov ebx, [edi+usb_static_ep.NextVirt] |
1635 | mov ebx, [edi+usb_static_ep.NextVirt] |
1629 | .loop: |
1636 | .loop: |
1630 | cmp ebx, edi |
1637 | cmp ebx, edi |
1631 | jz .done |
1638 | jz .done |
1632 | ; store pointer to the next pipe in the stack |
1639 | ; store pointer to the next pipe in the stack |
1633 | push [ebx+usb_static_ep.NextVirt] |
1640 | push [ebx+usb_static_ep.NextVirt] |
1634 | ; 2. For every pipe, perform the internal loop over all descriptors. |
1641 | ; 2. For every pipe, perform the internal loop over all descriptors. |
1635 | ; All descriptors are organized in the queue; we process items from the start |
1642 | ; All descriptors are organized in the queue; we process items from the start |
1636 | ; of the queue until a) the last descriptor (not the part of the queue itself) |
1643 | ; of the queue until a) the last descriptor (not the part of the queue itself) |
1637 | ; or b) an active (not yet processed by the hardware) descriptor is reached. |
1644 | ; or b) an active (not yet processed by the hardware) descriptor is reached. |
1638 | lea ecx, [ebx+usb_pipe.Lock] |
1645 | lea ecx, [ebx+usb_pipe.Lock] |
1639 | call mutex_lock |
1646 | call mutex_lock |
1640 | mov ebx, [ebx+usb_pipe.LastTD] |
1647 | mov ebx, [ebx+usb_pipe.LastTD] |
1641 | push ebx |
1648 | push ebx |
1642 | mov ebx, [ebx+usb_gtd.NextVirt] |
1649 | mov ebx, [ebx+usb_gtd.NextVirt] |
1643 | .tdloop: |
1650 | .tdloop: |
1644 | ; 3. For every descriptor, test active flag and check for end-of-queue; |
1651 | ; 3. For every descriptor, test active flag and check for end-of-queue; |
1645 | ; if either of conditions holds, exit from the internal loop. |
1652 | ; if either of conditions holds, exit from the internal loop. |
1646 | cmp ebx, [esp] |
1653 | cmp ebx, [esp] |
1647 | jz .tddone |
1654 | jz .tddone |
1648 | cmp byte [ebx+ehci_gtd.Token-sizeof.ehci_gtd], 0 |
1655 | cmp byte [ebx+ehci_gtd.Token-sizeof.ehci_gtd], 0 |
1649 | js .tddone |
1656 | js .tddone |
1650 | ; Release the queue lock while processing one descriptor: |
1657 | ; Release the queue lock while processing one descriptor: |
1651 | ; callback function could (and often would) schedule another transfer. |
1658 | ; callback function could (and often would) schedule another transfer. |
1652 | push ecx |
1659 | push ecx |
1653 | call mutex_unlock |
1660 | call mutex_unlock |
1654 | call ehci_process_updated_td |
1661 | call ehci_process_updated_td |
1655 | pop ecx |
1662 | pop ecx |
1656 | call mutex_lock |
1663 | call mutex_lock |
1657 | jmp .tdloop |
1664 | jmp .tdloop |
1658 | .tddone: |
1665 | .tddone: |
1659 | call mutex_unlock |
1666 | call mutex_unlock |
1660 | pop ebx |
1667 | pop ebx |
1661 | ; End of internal loop, restore pointer to the next pipe |
1668 | ; End of internal loop, restore pointer to the next pipe |
1662 | ; and continue the external loop. |
1669 | ; and continue the external loop. |
1663 | pop ebx |
1670 | pop ebx |
1664 | jmp .loop |
1671 | jmp .loop |
1665 | .done: |
1672 | .done: |
1666 | pop ebx |
1673 | pop ebx |
1667 | add edi, sizeof.ehci_static_ep |
1674 | add edi, sizeof.ehci_static_ep |
1668 | ret |
1675 | ret |
1669 | endp |
1676 | endp |
1670 | 1677 | ||
1671 | ; This procedure is called from ehci_process_updated_list, which is itself |
1678 | ; This procedure is called from ehci_process_updated_list, which is itself |
1672 | ; called from ehci_process_updated_schedule, see comments there. |
1679 | ; called from ehci_process_updated_schedule, see comments there. |
1673 | ; It processes one completed descriptor. |
1680 | ; It processes one completed descriptor. |
1674 | ; in: ebx -> usb_gtd, out: ebx -> next usb_gtd. |
1681 | ; in: ebx -> usb_gtd, out: ebx -> next usb_gtd. |
1675 | proc ehci_process_updated_td |
1682 | proc ehci_process_updated_td |
1676 | ; mov eax, [ebx+usb_gtd.Pipe] |
1683 | ; mov eax, [ebx+usb_gtd.Pipe] |
1677 | ; cmp [eax+usb_pipe.Type], INTERRUPT_PIPE |
1684 | ; cmp [eax+usb_pipe.Type], INTERRUPT_PIPE |
1678 | ; jnz @f |
1685 | ; jnz @f |
1679 | ; DEBUGF 1,'K : finalized TD for pipe %x:\n',eax |
1686 | ; DEBUGF 1,'K : finalized TD for pipe %x:\n',eax |
1680 | ; lea eax, [ebx-sizeof.ehci_gtd] |
1687 | ; lea eax, [ebx-sizeof.ehci_gtd] |
1681 | ; DEBUGF 1,'K : %x %x %x %x\n',[eax],[eax+4],[eax+8],[eax+12] |
1688 | ; DEBUGF 1,'K : %x %x %x %x\n',[eax],[eax+4],[eax+8],[eax+12] |
1682 | ; DEBUGF 1,'K : %x %x %x %x\n',[eax+16],[eax+20],[eax+24],[eax+28] |
1689 | ; DEBUGF 1,'K : %x %x %x %x\n',[eax+16],[eax+20],[eax+24],[eax+28] |
1683 | ;@@: |
1690 | ;@@: |
1684 | ; 1. Remove this descriptor from the list of descriptors for this pipe. |
1691 | ; 1. Remove this descriptor from the list of descriptors for this pipe. |
1685 | call usb_unlink_td |
1692 | call usb_unlink_td |
1686 | ; 2. Calculate actual number of bytes transferred. |
1693 | ; 2. Calculate actual number of bytes transferred. |
1687 | mov eax, [ebx+ehci_gtd.Token-sizeof.ehci_gtd] |
1694 | mov eax, [ebx+ehci_gtd.Token-sizeof.ehci_gtd] |
1688 | lea edx, [eax+eax] |
1695 | lea edx, [eax+eax] |
1689 | shr edx, 17 |
1696 | shr edx, 17 |
1690 | sub edx, [ebx+usb_gtd.Length] |
1697 | sub edx, [ebx+usb_gtd.Length] |
1691 | neg edx |
1698 | neg edx |
1692 | ; 3. Check whether we need some special processing beyond notifying the driver. |
1699 | ; 3. Check whether we need some special processing beyond notifying the driver. |
1693 | ; Transfer errors require special processing. |
1700 | ; Transfer errors require special processing. |
1694 | ; Short packets require special processing if |
1701 | ; Short packets require special processing if |
1695 | ; a) this is not the last descriptor for transfer stage |
1702 | ; a) this is not the last descriptor for transfer stage |
1696 | ; (in this case we need to process subsequent descriptors for the stage too) |
1703 | ; (in this case we need to process subsequent descriptors for the stage too) |
1697 | ; or b) the caller considers short transfers to be an error. |
1704 | ; or b) the caller considers short transfers to be an error. |
1698 | ; ehci_alloc_transfer sets bit 0 of ehci_gtd.Flags to 0 if short packet |
1705 | ; ehci_alloc_transfer sets bit 0 of ehci_gtd.Flags to 0 if short packet |
1699 | ; in this descriptor requires special processing and to 1 otherwise. |
1706 | ; in this descriptor requires special processing and to 1 otherwise. |
1700 | ; If special processing is not needed, advance to 4 with ecx = 0. |
1707 | ; If special processing is not needed, advance to 4 with ecx = 0. |
1701 | ; Otherwise, go to 6. |
1708 | ; Otherwise, go to 6. |
1702 | xor ecx, ecx |
1709 | xor ecx, ecx |
1703 | test al, 40h |
1710 | test al, 40h |
1704 | jnz .error |
1711 | jnz .error |
1705 | test byte [ebx+ehci_gtd.Flags-sizeof.ehci_gtd], 1 |
1712 | test byte [ebx+ehci_gtd.Flags-sizeof.ehci_gtd], 1 |
1706 | jnz .notify |
1713 | jnz .notify |
1707 | cmp edx, [ebx+usb_gtd.Length] |
1714 | cmp edx, [ebx+usb_gtd.Length] |
1708 | jnz .special |
1715 | jnz .special |
1709 | .notify: |
1716 | .notify: |
1710 | ; 4. Either the descriptor in ebx was processed without errors, |
1717 | ; 4. Either the descriptor in ebx was processed without errors, |
1711 | ; or all necessary error actions were taken and ebx points to the last |
1718 | ; or all necessary error actions were taken and ebx points to the last |
1712 | ; related descriptor. |
1719 | ; related descriptor. |
1713 | ; 4a. Test whether it is the last descriptor in the transfer |
1720 | ; 4a. Test whether it is the last descriptor in the transfer |
1714 | ; <=> it has an associated callback. |
1721 | ; <=> it has an associated callback. |
1715 | mov eax, [ebx+usb_gtd.Callback] |
1722 | mov eax, [ebx+usb_gtd.Callback] |
1716 | test eax, eax |
1723 | test eax, eax |
1717 | jz .nocallback |
1724 | jz .nocallback |
1718 | ; 4b. It has an associated callback; call it with corresponding parameters. |
1725 | ; 4b. It has an associated callback; call it with corresponding parameters. |
1719 | stdcall_verify eax, [ebx+usb_gtd.Pipe], ecx, \ |
1726 | stdcall_verify eax, [ebx+usb_gtd.Pipe], ecx, \ |
1720 | [ebx+usb_gtd.Buffer], edx, [ebx+usb_gtd.UserData] |
1727 | [ebx+usb_gtd.Buffer], edx, [ebx+usb_gtd.UserData] |
1721 | jmp .callback |
1728 | jmp .callback |
1722 | .nocallback: |
1729 | .nocallback: |
1723 | ; 4c. It is an intermediate descriptor. Add its length to the length |
1730 | ; 4c. It is an intermediate descriptor. Add its length to the length |
1724 | ; in the following descriptor. |
1731 | ; in the following descriptor. |
1725 | mov eax, [ebx+usb_gtd.NextVirt] |
1732 | mov eax, [ebx+usb_gtd.NextVirt] |
1726 | add [eax+usb_gtd.Length], edx |
1733 | add [eax+usb_gtd.Length], edx |
1727 | .callback: |
1734 | .callback: |
1728 | ; 5. Free the current descriptor and return the next one. |
1735 | ; 5. Free the current descriptor and return the next one. |
1729 | push [ebx+usb_gtd.NextVirt] |
1736 | push [ebx+usb_gtd.NextVirt] |
1730 | stdcall ehci_free_td, ebx |
1737 | stdcall ehci_free_td, ebx |
1731 | pop ebx |
1738 | pop ebx |
1732 | ret |
1739 | ret |
1733 | .error: |
1740 | .error: |
1734 | push ebx |
1741 | push ebx |
1735 | sub ebx, sizeof.ehci_gtd |
1742 | sub ebx, sizeof.ehci_gtd |
1736 | DEBUGF 1,'K : TD failed:\n' |
1743 | DEBUGF 1,'K : TD failed:\n' |
1737 | DEBUGF 1,'K : %x %x %x %x\n',[ebx],[ebx+4],[ebx+8],[ebx+12] |
1744 | DEBUGF 1,'K : %x %x %x %x\n',[ebx],[ebx+4],[ebx+8],[ebx+12] |
1738 | DEBUGF 1,'K : %x %x %x %x\n',[ebx+16],[ebx+20],[ebx+24],[ebx+28] |
1745 | DEBUGF 1,'K : %x %x %x %x\n',[ebx+16],[ebx+20],[ebx+24],[ebx+28] |
1739 | pop ebx |
1746 | pop ebx |
1740 | DEBUGF 1,'K : pipe now:\n' |
1747 | DEBUGF 1,'K : pipe now:\n' |
1741 | mov ecx, [ebx+usb_gtd.Pipe] |
1748 | mov ecx, [ebx+usb_gtd.Pipe] |
1742 | sub ecx, sizeof.ehci_pipe |
1749 | sub ecx, sizeof.ehci_pipe |
1743 | DEBUGF 1,'K : %x %x %x %x\n',[ecx],[ecx+4],[ecx+8],[ecx+12] |
1750 | DEBUGF 1,'K : %x %x %x %x\n',[ecx],[ecx+4],[ecx+8],[ecx+12] |
1744 | DEBUGF 1,'K : %x %x %x %x\n',[ecx+16],[ecx+20],[ecx+24],[ecx+28] |
1751 | DEBUGF 1,'K : %x %x %x %x\n',[ecx+16],[ecx+20],[ecx+24],[ecx+28] |
1745 | DEBUGF 1,'K : %x %x %x %x\n',[ecx+32],[ecx+36],[ecx+40],[ecx+44] |
1752 | DEBUGF 1,'K : %x %x %x %x\n',[ecx+32],[ecx+36],[ecx+40],[ecx+44] |
1746 | .special: |
1753 | .special: |
1747 | ; 6. Special processing is needed. |
1754 | ; 6. Special processing is needed. |
1748 | ; 6a. Save the status and length. |
1755 | ; 6a. Save the status and length. |
1749 | push edx |
1756 | push edx |
1750 | push eax |
1757 | push eax |
1751 | ; 6b. Traverse the list of descriptors looking for the final descriptor |
1758 | ; 6b. Traverse the list of descriptors looking for the final descriptor |
1752 | ; for this transfer. Free and unlink non-final descriptors. |
1759 | ; for this transfer. Free and unlink non-final descriptors. |
1753 | ; Final descriptor will be freed in step 5. |
1760 | ; Final descriptor will be freed in step 5. |
1754 | .look_final: |
1761 | .look_final: |
1755 | call usb_is_final_packet |
1762 | call usb_is_final_packet |
1756 | jnc .found_final |
1763 | jnc .found_final |
1757 | push [ebx+usb_gtd.NextVirt] |
1764 | push [ebx+usb_gtd.NextVirt] |
1758 | stdcall ehci_free_td, ebx |
1765 | stdcall ehci_free_td, ebx |
1759 | pop ebx |
1766 | pop ebx |
1760 | call usb_unlink_td |
1767 | call usb_unlink_td |
1761 | jmp .look_final |
1768 | jmp .look_final |
1762 | .found_final: |
1769 | .found_final: |
1763 | ; 6c. Restore the status saved in 6a and transform it to the error code. |
1770 | ; 6c. Restore the status saved in 6a and transform it to the error code. |
1764 | ; Notes: |
1771 | ; Notes: |
1765 | ; * any USB transaction error results in Halted bit; if it is not set, |
1772 | ; * any USB transaction error results in Halted bit; if it is not set, |
1766 | ; but we are here, it must be due to short packet; |
1773 | ; but we are here, it must be due to short packet; |
1767 | ; * babble is considered a fatal USB transaction error, |
1774 | ; * babble is considered a fatal USB transaction error, |
1768 | ; other errors just lead to retrying the transaction; |
1775 | ; other errors just lead to retrying the transaction; |
1769 | ; if babble is detected, return the corresponding error; |
1776 | ; if babble is detected, return the corresponding error; |
1770 | ; * if several non-fatal errors have occured during transaction retries, |
1777 | ; * if several non-fatal errors have occured during transaction retries, |
1771 | ; all corresponding bits are set. In this case, return some error code, |
1778 | ; all corresponding bits are set. In this case, return some error code, |
1772 | ; the order is quite arbitrary. |
1779 | ; the order is quite arbitrary. |
1773 | pop eax ; status |
1780 | pop eax ; status |
1774 | movi ecx, USB_STATUS_UNDERRUN |
1781 | movi ecx, USB_STATUS_UNDERRUN |
1775 | test al, 40h ; not Halted? |
1782 | test al, 40h ; not Halted? |
1776 | jz .know_error |
1783 | jz .know_error |
1777 | mov cl, USB_STATUS_OVERRUN |
1784 | mov cl, USB_STATUS_OVERRUN |
1778 | test al, 10h ; Babble detected? |
1785 | test al, 10h ; Babble detected? |
1779 | jnz .know_error |
1786 | jnz .know_error |
1780 | mov cl, USB_STATUS_BUFOVERRUN |
1787 | mov cl, USB_STATUS_BUFOVERRUN |
1781 | test al, 20h ; Data Buffer error? |
1788 | test al, 20h ; Data Buffer error? |
1782 | jnz .know_error |
1789 | jnz .know_error |
1783 | mov cl, USB_STATUS_NORESPONSE |
1790 | mov cl, USB_STATUS_NORESPONSE |
1784 | test al, 8 ; Transaction Error? |
1791 | test al, 8 ; Transaction Error? |
1785 | jnz .know_error |
1792 | jnz .know_error |
1786 | mov cl, USB_STATUS_STALL |
1793 | mov cl, USB_STATUS_STALL |
1787 | .know_error: |
1794 | .know_error: |
1788 | ; 6d. If error code is USB_STATUS_UNDERRUN and the last TD allows short packets, |
1795 | ; 6d. If error code is USB_STATUS_UNDERRUN and the last TD allows short packets, |
1789 | ; it is not an error; in this case, go to 4 with ecx = 0. |
1796 | ; it is not an error; in this case, go to 4 with ecx = 0. |
1790 | cmp ecx, USB_STATUS_UNDERRUN |
1797 | cmp ecx, USB_STATUS_UNDERRUN |
1791 | jnz @f |
1798 | jnz @f |
1792 | test byte [ebx+ehci_gtd.Flags-sizeof.ehci_gtd], 1 |
1799 | test byte [ebx+ehci_gtd.Flags-sizeof.ehci_gtd], 1 |
1793 | jz @f |
1800 | jz @f |
1794 | xor ecx, ecx |
1801 | xor ecx, ecx |
1795 | pop edx ; length |
1802 | pop edx ; length |
1796 | jmp .notify |
1803 | jmp .notify |
1797 | @@: |
1804 | @@: |
1798 | ; 6e. Abort the entire transfer. |
1805 | ; 6e. Abort the entire transfer. |
1799 | ; There are two cases: either there is only one transfer stage |
1806 | ; There are two cases: either there is only one transfer stage |
1800 | ; (everything except control transfers), then ebx points to the last TD and |
1807 | ; (everything except control transfers), then ebx points to the last TD and |
1801 | ; all previous TD were unlinked and dismissed (if possible), |
1808 | ; all previous TD were unlinked and dismissed (if possible), |
1802 | ; or there are several stages (a control transfer) and ebx points to the last |
1809 | ; or there are several stages (a control transfer) and ebx points to the last |
1803 | ; TD of Data or Status stage (usb_is_final_packet does not stop in Setup stage, |
1810 | ; TD of Data or Status stage (usb_is_final_packet does not stop in Setup stage, |
1804 | ; because Setup stage can not produce short packets); for Data stage, we need |
1811 | ; because Setup stage can not produce short packets); for Data stage, we need |
1805 | ; to unlink and free (if possible) one more TD and advance ebx to the next one. |
1812 | ; to unlink and free (if possible) one more TD and advance ebx to the next one. |
1806 | cmp [ebx+usb_gtd.Callback], 0 |
1813 | cmp [ebx+usb_gtd.Callback], 0 |
1807 | jnz .normal |
1814 | jnz .normal |
1808 | push ecx |
1815 | push ecx |
1809 | push [ebx+usb_gtd.NextVirt] |
1816 | push [ebx+usb_gtd.NextVirt] |
1810 | stdcall ehci_free_td, ebx |
1817 | stdcall ehci_free_td, ebx |
1811 | pop ebx |
1818 | pop ebx |
1812 | call usb_unlink_td |
1819 | call usb_unlink_td |
1813 | pop ecx |
1820 | pop ecx |
1814 | .normal: |
1821 | .normal: |
1815 | ; 6f. For bulk/interrupt transfers we have no choice but halt the queue, |
1822 | ; 6f. For bulk/interrupt transfers we have no choice but halt the queue, |
1816 | ; the driver should intercede (through some API which is not written yet). |
1823 | ; the driver should intercede (through some API which is not written yet). |
1817 | ; Control pipes normally recover at the next SETUP transaction (first stage |
1824 | ; Control pipes normally recover at the next SETUP transaction (first stage |
1818 | ; of any control transfer), so we hope on the best and just advance the queue |
1825 | ; of any control transfer), so we hope on the best and just advance the queue |
1819 | ; to the next transfer. (According to the standard, "A control pipe may also |
1826 | ; to the next transfer. (According to the standard, "A control pipe may also |
1820 | ; support functional stall as well, but this is not recommended."). |
1827 | ; support functional stall as well, but this is not recommended."). |
1821 | mov edx, [ebx+usb_gtd.Pipe] |
1828 | mov edx, [ebx+usb_gtd.Pipe] |
1822 | mov eax, [ebx+ehci_gtd.NextTD-sizeof.ehci_gtd] |
1829 | mov eax, [ebx+ehci_gtd.NextTD-sizeof.ehci_gtd] |
1823 | or al, 1 |
1830 | or al, 1 |
1824 | mov [edx+ehci_pipe.Overlay.NextTD-sizeof.ehci_pipe], eax |
1831 | mov [edx+ehci_pipe.Overlay.NextTD-sizeof.ehci_pipe], eax |
1825 | mov [edx+ehci_pipe.Overlay.AlternateNextTD-sizeof.ehci_pipe], eax |
1832 | mov [edx+ehci_pipe.Overlay.AlternateNextTD-sizeof.ehci_pipe], eax |
1826 | cmp [edx+usb_pipe.Type], CONTROL_PIPE |
1833 | cmp [edx+usb_pipe.Type], CONTROL_PIPE |
1827 | jz .control |
1834 | jz .control |
1828 | ; Bulk/interrupt transfer; halt the queue. |
1835 | ; Bulk/interrupt transfer; halt the queue. |
1829 | mov [edx+ehci_pipe.Overlay.Token-sizeof.ehci_pipe], 40h |
1836 | mov [edx+ehci_pipe.Overlay.Token-sizeof.ehci_pipe], 40h |
1830 | pop edx |
1837 | pop edx |
1831 | jmp .notify |
1838 | jmp .notify |
1832 | ; Control transfer. |
1839 | ; Control transfer. |
1833 | .control: |
1840 | .control: |
1834 | and [edx+ehci_pipe.Overlay.Token-sizeof.ehci_pipe], 0 |
1841 | and [edx+ehci_pipe.Overlay.Token-sizeof.ehci_pipe], 0 |
1835 | dec [edx+ehci_pipe.Overlay.NextTD-sizeof.ehci_pipe] |
1842 | dec [edx+ehci_pipe.Overlay.NextTD-sizeof.ehci_pipe] |
1836 | pop edx |
1843 | pop edx |
1837 | jmp .notify |
1844 | jmp .notify |
1838 | endp |
1845 | endp |
1839 | 1846 | ||
1840 | ; This procedure unlinks the pipe from the corresponding pipe list. |
1847 | ; This procedure unlinks the pipe from the corresponding pipe list. |
1841 | ; esi -> usb_controller, ebx -> usb_pipe |
1848 | ; esi -> usb_controller, ebx -> usb_pipe |
1842 | proc ehci_unlink_pipe |
1849 | proc ehci_unlink_pipe |
1843 | cmp [ebx+usb_pipe.Type], INTERRUPT_PIPE |
1850 | cmp [ebx+usb_pipe.Type], INTERRUPT_PIPE |
1844 | jnz @f |
1851 | jnz @f |
1845 | test word [ebx+ehci_pipe.Flags-sizeof.ehci_pipe+2], 3FFFh |
1852 | test word [ebx+ehci_pipe.Flags-sizeof.ehci_pipe+2], 3FFFh |
1846 | jnz .interrupt_fs |
1853 | jnz .interrupt_fs |
1847 | call ehci_hs_interrupt_list_unlink |
1854 | call ehci_hs_interrupt_list_unlink |
1848 | jmp .interrupt_common |
1855 | jmp .interrupt_common |
1849 | .interrupt_fs: |
1856 | .interrupt_fs: |
1850 | call ehci_fs_interrupt_list_unlink |
1857 | call ehci_fs_interrupt_list_unlink |
1851 | .interrupt_common: |
1858 | .interrupt_common: |
1852 | @@: |
1859 | @@: |
1853 | mov edx, [ebx+usb_pipe.NextVirt] |
1860 | mov edx, [ebx+usb_pipe.NextVirt] |
1854 | mov eax, [ebx+usb_pipe.PrevVirt] |
1861 | mov eax, [ebx+usb_pipe.PrevVirt] |
1855 | mov [edx+usb_pipe.PrevVirt], eax |
1862 | mov [edx+usb_pipe.PrevVirt], eax |
1856 | mov [eax+usb_pipe.NextVirt], edx |
1863 | mov [eax+usb_pipe.NextVirt], edx |
1857 | mov edx, esi |
1864 | mov edx, esi |
1858 | sub edx, eax |
1865 | sub edx, eax |
1859 | cmp edx, sizeof.ehci_controller |
1866 | cmp edx, sizeof.ehci_controller |
1860 | mov edx, [ebx+ehci_pipe.NextQH-sizeof.ehci_pipe] |
1867 | mov edx, [ebx+ehci_pipe.NextQH-sizeof.ehci_pipe] |
1861 | jb .prev_is_static |
1868 | jb .prev_is_static |
1862 | mov [eax+ehci_pipe.NextQH-sizeof.ehci_pipe], edx |
1869 | mov [eax+ehci_pipe.NextQH-sizeof.ehci_pipe], edx |
1863 | ret |
1870 | ret |
1864 | .prev_is_static: |
1871 | .prev_is_static: |
1865 | mov [eax+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart], edx |
1872 | mov [eax+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart], edx |
1866 | ret |
1873 | ret |
1867 | endp |
1874 | endp |
1868 | 1875 | ||
1869 | proc ehci_alloc_td |
1876 | proc ehci_alloc_td |
1870 | push ebx |
1877 | push ebx |
1871 | mov ebx, ehci_gtd_mutex |
1878 | mov ebx, ehci_gtd_mutex |
1872 | stdcall usb_allocate_common, (sizeof.ehci_gtd + sizeof.usb_gtd + 1Fh) and not 1Fh |
1879 | stdcall usb_allocate_common, (sizeof.ehci_gtd + sizeof.usb_gtd + 1Fh) and not 1Fh |
1873 | test eax, eax |
1880 | test eax, eax |
1874 | jz @f |
1881 | jz @f |
1875 | add eax, sizeof.ehci_gtd |
1882 | add eax, sizeof.ehci_gtd |
1876 | @@: |
1883 | @@: |
1877 | pop ebx |
1884 | pop ebx |
1878 | ret |
1885 | ret |
1879 | endp |
1886 | endp |
1880 | 1887 | ||
1881 | ; This procedure is called from several places from main USB code and |
1888 | ; This procedure is called from several places from main USB code and |
1882 | ; frees all additional data associated with the transfer descriptor. |
1889 | ; frees all additional data associated with the transfer descriptor. |
1883 | ; EHCI has no additional data, so just free ehci_gtd structure. |
1890 | ; EHCI has no additional data, so just free ehci_gtd structure. |
1884 | proc ehci_free_td |
1891 | proc ehci_free_td |
1885 | sub dword [esp+4], sizeof.ehci_gtd |
1892 | sub dword [esp+4], sizeof.ehci_gtd |
1886 | jmp usb_free_common |
1893 | jmp usb_free_common |
1887 | endp=>=8>>>>>>>>>=>=> |
1894 | endp=>=8>>>>>>>>>=>=> |