Rev 3908 | Rev 5201 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 3908 | Rev 4423 | ||
---|---|---|---|
Line 1... | Line 1... | ||
1 | ; USB Host Controller support code: hardware-independent part, |
1 | ; USB Host Controller support code: hardware-independent part, |
2 | ; common for all controller types. |
2 | ; common for all controller types. |
Line 3... | Line -... | ||
3 | - | ||
4 | ; ============================================================================= |
- | |
5 | ; ================================= Constants ================================= |
- | |
6 | ; ============================================================================= |
- | |
7 | ; USB device must have at least 100ms of stable power before initializing can |
- | |
8 | ; proceed; one timer tick is 10ms, so enforce delay in 10 ticks |
- | |
9 | USB_CONNECT_DELAY = 10 |
- | |
10 | ; USB requires at least 10 ms for reset signalling. Normally, this is one timer |
- | |
11 | ; tick. However, it is possible that we start reset signalling in the end of |
- | |
12 | ; interval between timer ticks and then we test time in the start of the next |
- | |
13 | ; interval; in this case, the delta between [timer_ticks] is 1, but the real |
- | |
14 | ; time passed is significantly less than 10 ms. To avoid this, we add an extra |
- | |
15 | ; tick; this guarantees that at least 10 ms have passed. |
- | |
16 | USB_RESET_TIME = 2 |
- | |
17 | ; USB requires at least 10 ms of reset recovery, a delay between reset |
- | |
18 | ; signalling and any commands to device. Add an extra tick for the same reasons |
- | |
19 | ; as with the previous constant. |
- | |
20 | USB_RESET_RECOVERY_TIME = 2 |
- | |
21 | - | ||
22 | ; ============================================================================= |
- | |
23 | ; ================================ Structures ================================= |
- | |
24 | ; ============================================================================= |
- | |
25 | ; Controller descriptor. |
- | |
26 | ; This structure represents the common (controller-independent) part |
- | |
27 | ; of a controller for the USB code. The corresponding controller-dependent |
- | |
28 | ; part *hci_controller is located immediately before usb_controller. |
- | |
29 | struct usb_controller |
- | |
30 | ; Two following fields organize all controllers in the global linked list. |
- | |
31 | Next dd ? |
- | |
32 | Prev dd ? |
- | |
33 | HardwareFunc dd ? |
- | |
34 | ; Pointer to usb_hardware_func structure with controller-specific functions. |
- | |
35 | NumPorts dd ? |
- | |
36 | ; Number of ports in the root hub. |
- | |
37 | SetAddressBuffer rb 8 |
- | |
38 | ; Buffer for USB control command SET_ADDRESS. |
- | |
39 | ExistingAddresses rd 128/32 |
- | |
40 | ; Bitmask for 128 bits; bit i is cleared <=> address i is free for allocating |
- | |
41 | ; for new devices. Bit 0 is always set. |
- | |
42 | ; |
- | |
43 | ; The hardware is allowed to cache some data from hardware structures. |
- | |
44 | ; Regular operations are designed considering this, |
- | |
45 | ; but sometimes it is required to wait for synchronization of hardware cache |
- | |
46 | ; with modified structures in memory. |
- | |
47 | ; The code keeps two queues of pipes waiting for synchronization, |
- | |
48 | ; one for asynchronous (bulk/control) pipes, one for periodic pipes, hardware |
- | |
49 | ; cache is invalidated under different conditions for those types. |
- | |
50 | ; Both queues are organized in the same way, as single-linked lists. |
- | |
51 | ; There are three special positions: the head of list (new pipes are added |
- | |
52 | ; here), the first pipe to be synchronized at the current iteration, |
- | |
53 | ; the tail of list (all pipes starting from here are synchronized). |
- | |
54 | WaitPipeListAsync dd ? |
- | |
55 | WaitPipeListPeriodic dd ? |
- | |
56 | ; List heads. |
- | |
57 | WaitPipeRequestAsync dd ? |
- | |
58 | WaitPipeRequestPeriodic dd ? |
- | |
59 | ; Pending request to hardware to refresh cache for items from WaitPipeList*. |
- | |
60 | ; (Pointers to some items in WaitPipeList* or NULLs). |
- | |
61 | ReadyPipeHeadAsync dd ? |
- | |
62 | ReadyPipeHeadPeriodic dd ? |
- | |
63 | ; Items of RemovingList* which were released by hardware and are ready |
- | |
64 | ; for further processing. |
- | |
65 | ; (Pointers to some items in WaitPipeList* or NULLs). |
- | |
66 | NewConnected dd ? |
- | |
67 | ; bit mask of recently connected ports of the root hub, |
- | |
68 | ; bit set = a device was recently connected to the corresponding port; |
- | |
69 | ; after USB_CONNECT_DELAY ticks of stable status these ports are moved to |
- | |
70 | ; PendingPorts |
- | |
71 | NewDisconnected dd ? |
- | |
72 | ; bit mask of disconnected ports of the root hub, |
- | |
73 | ; bit set = a device in the corresponding port was disconnected, |
- | |
74 | ; disconnect processing is required. |
- | |
75 | PendingPorts dd ? |
- | |
76 | ; bit mask of ports which are ready to be initialized |
- | |
77 | ControlLock MUTEX ? |
- | |
78 | ; mutex which guards all operations with control queue |
- | |
79 | BulkLock MUTEX ? |
- | |
80 | ; mutex which guards all operations with bulk queue |
- | |
81 | PeriodicLock MUTEX ? |
- | |
82 | ; mutex which guards all operations with periodic queues |
3 | |
83 | WaitSpinlock: |
- | |
84 | ; spinlock guarding WaitPipeRequest/ReadyPipeHead (but not WaitPipeList) |
- | |
85 | StartWaitFrame dd ? |
- | |
86 | ; USB frame number when WaitPipeRequest* was registered. |
- | |
87 | ResettingHub dd ? |
- | |
88 | ; Pointer to usb_hub responsible for the currently resetting port, if any. |
- | |
89 | ; NULL for the root hub. |
- | |
90 | ResettingPort db ? |
- | |
91 | ; Port that is currently resetting, 0-based. |
- | |
92 | ResettingSpeed db ? |
- | |
93 | ; Speed of currently resetting device. |
- | |
94 | ResettingStatus db ? |
4 | iglobal |
95 | ; Status of port reset. 0 = no port is resetting, -1 = reset failed, |
- | |
96 | ; 1 = reset in progress, 2 = reset recovery in progress. |
- | |
97 | rb 1 ; alignment |
- | |
98 | ResetTime dd ? |
- | |
99 | ; Time when reset signalling or reset recovery has been started. |
- | |
100 | ConnectedTime rd 16 |
- | |
101 | ; Time, in timer ticks, when the port i has signalled the connect event. |
- | |
102 | ; Valid only if bit i in NewConnected is set. |
- | |
103 | DevicesByPort rd 16 |
- | |
104 | ; Pointer to usb_pipe for zero endpoint (which serves as device handle) |
- | |
105 | ; for each port. |
- | |
106 | ends |
- | |
107 | - | ||
108 | ; Interface-specific data. Several interfaces of one device can operate |
- | |
109 | ; independently, each is controlled by some driver and is identified by |
- | |
110 | ; some driver-specific data passed as is to the driver. |
- | |
111 | struct usb_interface_data |
- | |
112 | DriverData dd ? |
- | |
113 | ; Passed as is to the driver. |
- | |
114 | DriverFunc dd ? |
- | |
115 | ; Pointer to USBSRV structure for the driver. |
5 | ; USB HC support: some functions interesting only for *HCI-drivers. |
116 | ends |
- | |
117 | - | ||
118 | ; Device-specific data. |
6 | align 4 |
119 | struct usb_device_data |
- | |
120 | PipeListLock MUTEX |
- | |
121 | ; Lock guarding OpenedPipeList. Must be the first item of the structure, |
- | |
122 | ; the code passes pointer to usb_device_data as is to mutex_lock/unlock. |
- | |
123 | OpenedPipeList rd 2 |
- | |
124 | ; List of all opened pipes for the device. |
- | |
125 | ; Used when the device is disconnected, so all pipes should be closed. |
- | |
126 | ClosedPipeList rd 2 |
- | |
127 | ; List of all closed, but still valid pipes for the device. |
- | |
128 | ; A pipe closed with USBClosePipe is just deallocated, |
- | |
129 | ; but a pipe closed due to disconnect must remain valid until driver-provided |
- | |
130 | ; disconnect handler returns; this list links all such pipes to deallocate them |
7 | usb_hc_func: |
131 | ; after disconnect processing. |
- | |
132 | NumPipes dd ? |
8 | dd usb_process_gtd |
133 | ; Number of not-yet-closed pipes. |
9 | dd usb_init_static_endpoint |
134 | Hub dd ? |
- | |
135 | ; NULL if connected to the root hub, pointer to usb_hub otherwise. |
- | |
136 | TTHub dd ? |
- | |
137 | ; Pointer to usb_hub for (the) hub with Transaction Translator for the device, |
- | |
138 | ; NULL if the device operates in the same speed as the controller. |
10 | dd usb_wakeup_if_needed |
139 | Port db ? |
- | |
140 | ; Port on the hub, zero-based. |
- | |
141 | TTPort db ? |
- | |
142 | ; Port on the TTHub, zero-based. |
- | |
143 | DeviceDescrSize db ? |
11 | dd usb_subscribe_control |
144 | ; Size of device descriptor. |
- | |
145 | Speed db ? |
- | |
146 | ; Device speed, one of USB_SPEED_*. |
- | |
147 | NumInterfaces dd ? |
- | |
148 | ; Number of interfaces. |
- | |
149 | ConfigDataSize dd ? |
- | |
150 | ; Total size of data associated with the configuration descriptor |
- | |
151 | ; (including the configuration descriptor itself). |
- | |
152 | Interfaces dd ? |
- | |
153 | ; Offset from the beginning of this structure to Interfaces field. |
- | |
154 | ; Variable-length fields: |
- | |
155 | ; DeviceDescriptor: |
- | |
156 | ; device descriptor starts here |
- | |
157 | ; ConfigDescriptor = DeviceDescriptor + DeviceDescrSize |
- | |
158 | ; configuration descriptor with all associated data |
- | |
159 | ; Interfaces = ALIGN_UP(ConfigDescriptor + ConfigDataSize, 4) |
- | |
160 | ; array of NumInterfaces elements of type usb_interface_data |
- | |
161 | ends |
- | |
162 | - | ||
163 | usb_device_data.DeviceDescriptor = sizeof.usb_device_data |
- | |
164 | - | ||
165 | ; Description of controller-specific data and functions. |
- | |
166 | struct usb_hardware_func |
12 | dd usb_subscription_done |
167 | ID dd ? ; '*HCI' |
13 | dd usb_allocate_common |
168 | DataSize dd ? ; sizeof(*hci_controller) |
14 | dd usb_free_common |
169 | Init dd ? |
- | |
170 | ; Initialize controller-specific part of controller data. |
- | |
171 | ; in: eax -> *hci_controller to initialize, [ebp-4] = (bus shl 8) + devfn |
- | |
172 | ; out: eax = 0 <=> failed, otherwise eax -> usb_controller |
- | |
173 | ProcessDeferred dd ? |
- | |
174 | ; Called regularly from the main loop of USB thread |
- | |
175 | ; (either due to timeout from a previous call, or due to explicit wakeup). |
15 | dd usb_td_to_virt |
176 | ; in: esi -> usb_controller |
- | |
177 | ; out: eax = maximum timeout for next call (-1 = infinity) |
- | |
178 | SetDeviceAddress dd ? |
- | |
179 | ; in: esi -> usb_controller, ebx -> usb_pipe, cl = address |
- | |
180 | GetDeviceAddress dd ? |
- | |
181 | ; in: esi -> usb_controller, ebx -> usb_pipe |
- | |
182 | ; out: eax = address |
16 | dd usb_init_transfer |
183 | PortDisable dd ? |
- | |
184 | ; Disable the given port in the root hub. |
- | |
185 | ; in: esi -> usb_controller, ecx = port (zero-based) |
- | |
186 | InitiateReset dd ? |
17 | dd usb_undo_tds |
187 | ; Start reset signalling on the given port. |
- | |
188 | ; in: esi -> usb_controller, ecx = port (zero-based) |
- | |
189 | SetEndpointPacketSize dd ? |
- | |
190 | ; in: esi -> usb_controller, ebx -> usb_pipe, ecx = packet size |
18 | dd usb_test_pending_port |
191 | AllocPipe dd ? |
19 | dd usb_get_tt |
192 | ; out: eax = pointer to allocated usb_pipe |
- | |
193 | FreePipe dd ? |
- | |
194 | ; void stdcall with one argument = pointer to previously allocated usb_pipe |
20 | dd usb_get_tt_think_time |
195 | InitPipe dd ? |
- | |
196 | ; in: edi -> usb_pipe for target, ecx -> usb_pipe for config pipe, |
- | |
197 | ; esi -> usb_controller, eax -> usb_gtd for the first TD, |
- | |
198 | ; [ebp+12] = endpoint, [ebp+16] = maxpacket, [ebp+20] = type |
- | |
199 | UnlinkPipe dd ? |
- | |
200 | ; esi -> usb_controller, ebx -> usb_pipe |
21 | dd usb_new_device |
201 | AllocTD dd ? |
22 | dd usb_disconnect_stage2 |
202 | ; out: eax = pointer to allocated usb_gtd |
23 | dd usb_process_wait_lists |
203 | FreeTD dd ? |
- | |
204 | ; void stdcall with one argument = pointer to previously allocated usb_gtd |
- | |
205 | AllocTransfer dd ? |
- | |
206 | ; Allocate and initialize one stage of a transfer. |
- | |
207 | ; ebx -> usb_pipe, other parameters are passed through the stack: |
- | |
208 | ; buffer,size = data to transfer |
24 | dd usb_unlink_td |
209 | ; flags = same as in usb_open_pipe: |
- | |
210 | ; bit 0 = allow short transfer, other bits reserved |
- | |
211 | ; td = pointer to the current end-of-queue descriptor |
- | |
212 | ; direction = |
- | |
213 | ; 0000b for normal transfers, |
- | |
214 | ; 1000b for control SETUP transfer, |
- | |
215 | ; 1101b for control OUT transfer, |
- | |
216 | ; 1110b for control IN transfer |
- | |
217 | ; returns eax = pointer to the new end-of-queue descriptor |
- | |
218 | ; (not included in the queue itself) or 0 on error |
- | |
219 | InsertTransfer dd ? |
- | |
220 | ; Activate previously initialized transfer (maybe with multiple stages). |
- | |
221 | ; esi -> usb_controller, ebx -> usb_pipe, |
- | |
222 | ; [esp+4] -> first usb_gtd for the transfer, |
- | |
223 | ; ecx -> last descriptor for the transfer |
- | |
224 | NewDevice dd ? |
- | |
225 | ; Initiate configuration of a new device (create pseudo-pipe describing that |
25 | dd usb_is_final_packet |
226 | ; device and call usb_new_device). |
- | |
227 | ; esi -> usb_controller, eax = speed (one of USB_SPEED_* constants). |
26 | dd usb_find_ehci_companion |
228 | ends |
- | |
229 | - | ||
230 | ; ============================================================================= |
- | |
231 | ; =================================== Code ==================================== |
- | |
Line 232... | Line 27... | ||
232 | ; ============================================================================= |
27 | endg |
233 | 28 | ||
234 | ; Initializes one controller, called by usb_init for every controller. |
29 | ; Initializes one controller, called by usb_init for every controller. |
235 | ; edi -> usb_hardware_func, eax -> PCIDEV structure for the device. |
30 | ; eax -> PCIDEV structure for the device. |
236 | proc usb_init_controller |
31 | proc usb_init_controller |
237 | push ebp |
32 | push ebp |
238 | mov ebp, esp |
33 | mov ebp, esp |
239 | ; 1. Store in the stack PCI coordinates and save pointer to PCIDEV: |
34 | ; 1. Store in the stack PCI coordinates and save pointer to PCIDEV: |
240 | ; make [ebp-4] = (bus shl 8) + devfn, used by controller-specific Init funcs. |
35 | ; make [ebp-4] = (bus shl 8) + devfn, used by controller-specific Init funcs. |
- | 36 | push dword [eax+PCIDEV.devfn] |
|
- | 37 | push eax |
|
- | 38 | mov edi, [eax+PCIDEV.owner] |
|
- | 39 | test edi, edi |
|
241 | push dword [eax+PCIDEV.devfn] |
40 | jz .nothing |
242 | push eax |
41 | mov edi, [edi+USBSRV.usb_func] |
243 | ; 2. Allocate *hci_controller + usb_controller. |
42 | ; 2. Allocate *hci_controller + usb_controller. |
244 | mov ebx, [edi+usb_hardware_func.DataSize] |
43 | mov ebx, [edi+usb_hardware_func.DataSize] |
245 | add ebx, sizeof.usb_controller |
44 | add ebx, sizeof.usb_controller |
Line 262... | Line 61... | ||
262 | mov [edi+usb_controller.ExistingAddresses+8-sizeof.usb_controller], eax |
61 | mov [edi+usb_controller.ExistingAddresses+8-sizeof.usb_controller], eax |
263 | mov [edi+usb_controller.ExistingAddresses+12-sizeof.usb_controller], eax |
62 | mov [edi+usb_controller.ExistingAddresses+12-sizeof.usb_controller], eax |
264 | mov [edi+usb_controller.ResettingPort-sizeof.usb_controller], al ; no resetting port |
63 | mov [edi+usb_controller.ResettingPort-sizeof.usb_controller], al ; no resetting port |
265 | dec eax ; don't allocate zero address |
64 | dec eax ; don't allocate zero address |
266 | mov [edi+usb_controller.ExistingAddresses-sizeof.usb_controller], eax |
65 | mov [edi+usb_controller.ExistingAddresses-sizeof.usb_controller], eax |
- | 66 | mov eax, [ebp-4] |
|
- | 67 | mov [edi+usb_controller.PCICoordinates-sizeof.usb_controller], eax |
|
267 | lea ecx, [edi+usb_controller.PeriodicLock-sizeof.usb_controller] |
68 | lea ecx, [edi+usb_controller.PeriodicLock-sizeof.usb_controller] |
268 | call mutex_init |
69 | call mutex_init |
269 | add ecx, usb_controller.ControlLock - usb_controller.PeriodicLock |
70 | add ecx, usb_controller.ControlLock - usb_controller.PeriodicLock |
270 | call mutex_init |
71 | call mutex_init |
271 | add ecx, usb_controller.BulkLock - usb_controller.ControlLock |
72 | add ecx, usb_controller.BulkLock - usb_controller.ControlLock |
Line 472... | Line 273... | ||
472 | cmp [esi+usb_controller.WaitPipeListAsync+edx], 1 |
273 | cmp [esi+usb_controller.WaitPipeListAsync+edx], 1 |
473 | cmc |
274 | cmc |
474 | .nothing: |
275 | .nothing: |
475 | ret |
276 | ret |
476 | endp=>=> |
277 | endp |
- | 278 | ||
- | 279 | ; Called from USB1 controller-specific initialization. |
|
- | 280 | ; Finds EHCI companion controller for given USB1 controller. |
|
- | 281 | ; in: bl = PCI device:function for USB1 controller, bh = PCI bus |
|
- | 282 | ; out: eax -> usb_controller for EHCI companion |
|
- | 283 | proc usb_find_ehci_companion |
|
- | 284 | ; 1. Loop over all registered controllers. |
|
- | 285 | mov eax, usb_controllers_list |
|
- | 286 | .next: |
|
- | 287 | mov eax, [eax+usb_controller.Next] |
|
- | 288 | cmp eax, usb_controllers_list |
|
- | 289 | jz .notfound |
|
- | 290 | ; 2. For every controller, check the type, ignore everything that is not EHCI. |
|
- | 291 | mov edx, [eax+usb_controller.HardwareFunc] |
|
- | 292 | cmp [edx+usb_hardware_func.ID], 'EHCI' |
|
- | 293 | jnz .next |
|
- | 294 | ; 3. For EHCI controller, compare PCI coordinates with input data: |
|
- | 295 | ; bus and device must be the same, function can be different. |
|
- | 296 | mov edx, [eax+usb_controller.PCICoordinates] |
|
- | 297 | xor edx, ebx |
|
- | 298 | cmp dx, 8 |
|
- | 299 | jae .next |
|
- | 300 | ret |
|
- | 301 | .notfound: |
|
- | 302 | xor eax, eax |
|
- | 303 | ret |
|
- | 304 | endp |
|
- | 305 | ||
- | 306 | ; Find Transaction Translator hub and port for the given device. |
|
- | 307 | ; in: edx = parent hub for the device, ecx = port for the device |
|
- | 308 | ; out: edx = TT hub for the device, ecx = TT port for the device. |
|
- | 309 | proc usb_get_tt |
|
- | 310 | ; If the parent hub is high-speed, it is TT for the device. |
|
- | 311 | ; Otherwise, the parent hub itself is behind TT, and the device |
|
- | 312 | ; has the same TT hub+port as the parent hub. |
|
- | 313 | mov eax, [edx+usb_hub.ConfigPipe] |
|
- | 314 | mov eax, [eax+usb_pipe.DeviceData] |
|
- | 315 | cmp [eax+usb_device_data.Speed], USB_SPEED_HS |
|
- | 316 | jz @f |
|
- | 317 | movzx ecx, [eax+usb_device_data.TTPort] |
|
- | 318 | mov edx, [eax+usb_device_data.TTHub] |
|
- | 319 | @@: |
|
- | 320 | mov edx, [edx+usb_hub.ConfigPipe] |
|
- | 321 | ret |
|
- | 322 | endp |