Rev 4418 | Rev 4850 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 4418 | Rev 4547 | ||
---|---|---|---|
Line 31... | Line 31... | ||
31 | 31 | ||
32 | ; Compile-time setting. If set, the code will dump all descriptors as they are |
32 | ; Compile-time setting. If set, the code will dump all descriptors as they are |
33 | ; read to the debug board. |
33 | ; read to the debug board. |
Line -... | Line 34... | ||
- | 34 | USB_DUMP_DESCRIPTORS = 1 |
|
- | 35 | ||
- | 36 | ; According to the USB specification (9.2.6.3), |
|
- | 37 | ; any device must response to SET_ADDRESS in 50 ms, or 5 timer ticks. |
|
- | 38 | ; Of course, our world is far from ideal. |
|
- | 39 | ; I have seen devices that just NAK everything when being reset from working |
|
- | 40 | ; state, but start to work after second reset. |
|
- | 41 | ; Our strategy is as follows: give 2 seconds for the first attempt, |
|
- | 42 | ; this should be enough for normal devices and not too long to detect buggy ones. |
|
- | 43 | ; If the device continues NAKing, reset it and retry several times, |
|
- | 44 | ; doubling the interval: 2s -> 4s -> 8s -> 16s. Give up after that. |
|
- | 45 | ; Numbers are quite arbitrary. |
|
- | 46 | TIMEOUT_SET_ADDRESS_INITIAL = 200 |
|
34 | USB_DUMP_DESCRIPTORS = 1 |
47 | TIMEOUT_SET_ADDRESS_LAST = 1600 |
35 | 48 | ||
36 | ; ============================================================================= |
49 | ; ============================================================================= |
37 | ; ================================ Structures ================================= |
50 | ; ================================ Structures ================================= |
38 | ; ============================================================================= |
51 | ; ============================================================================= |
Line 177... | Line 190... | ||
177 | ; in: [esi+usb_controller.ResettingSpeed] is the speed of the device, one of |
190 | ; in: [esi+usb_controller.ResettingSpeed] is the speed of the device, one of |
178 | ; USB_SPEED_xx. |
191 | ; USB_SPEED_xx. |
179 | ; out: eax = 0 <=> failed, the caller should disable the port. |
192 | ; out: eax = 0 <=> failed, the caller should disable the port. |
180 | proc usb_new_device |
193 | proc usb_new_device |
181 | push ebx edi ; save used registers to be stdcall |
194 | push ebx edi ; save used registers to be stdcall |
- | 195 | ; 1. Check whether we're here because we were trying to reset |
|
- | 196 | ; already-registered device in hope to fix something serious. |
|
- | 197 | ; If so, skip allocation and go to 6. |
|
- | 198 | movzx eax, [esi+usb_controller.ResettingPort] |
|
- | 199 | mov edx, [esi+usb_controller.ResettingHub] |
|
- | 200 | test edx, edx |
|
- | 201 | jz .test_roothub |
|
- | 202 | mov edx, [edx+usb_hub.ConnectedDevicesPtr] |
|
- | 203 | mov ebx, [edx+eax*4] |
|
- | 204 | jmp @f |
|
- | 205 | .test_roothub: |
|
- | 206 | mov ebx, [esi+usb_controller.DevicesByPort+eax*4] |
|
- | 207 | @@: |
|
- | 208 | test ebx, ebx |
|
- | 209 | jnz .try_set_address |
|
182 | ; 1. Allocate resources. Any device uses the following resources: |
210 | ; 2. Allocate resources. Any device uses the following resources: |
183 | ; - device address in the bus |
211 | ; - device address in the bus |
184 | ; - memory for device data |
212 | ; - memory for device data |
185 | ; - pipe for zero endpoint |
213 | ; - pipe for zero endpoint |
186 | ; If some allocation fails, we must undo our actions. Closing the pipe |
214 | ; If some allocation fails, we must undo our actions. Closing the pipe |
187 | ; is a hard task, so we avoid it and open the pipe as the last resource. |
215 | ; is a hard task, so we avoid it and open the pipe as the last resource. |
188 | ; The order for other two allocations is quite arbitrary. |
216 | ; The order for other two allocations is quite arbitrary. |
189 | ; 1a. Allocate a bus address. |
217 | ; 2a. Allocate a bus address. |
190 | push ecx |
218 | push ecx |
191 | call usb_set_address_request |
219 | call usb_set_address_request |
192 | pop ecx |
220 | pop ecx |
193 | ; 1b. If failed, just return zero. |
221 | ; 2b. If failed, just return zero. |
194 | test eax, eax |
222 | test eax, eax |
195 | jz .nothing |
223 | jz .nothing |
196 | ; 1c. Allocate memory for device data. |
224 | ; 2c. Allocate memory for device data. |
197 | ; For now, we need sizeof.usb_device_data and extra 8 bytes for GET_DESCRIPTOR |
225 | ; For now, we need sizeof.usb_device_data and extra 8 bytes for GET_DESCRIPTOR |
198 | ; input and output, see usb_after_set_address. Later we will reallocate it |
226 | ; input and output, see usb_after_set_address. Later we will reallocate it |
199 | ; to actual size needed for descriptors. |
227 | ; to actual size needed for descriptors. |
200 | movi eax, sizeof.usb_device_data + 8 |
228 | movi eax, sizeof.usb_device_data + 8 |
201 | push ecx |
229 | push ecx |
202 | call malloc |
230 | call malloc |
203 | pop ecx |
231 | pop ecx |
204 | ; 1d. If failed, free the bus address and return zero. |
232 | ; 2d. If failed, free the bus address and return zero. |
205 | test eax, eax |
233 | test eax, eax |
206 | jz .nomemory |
234 | jz .nomemory |
207 | ; 1e. Open pipe for endpoint zero. |
235 | ; 2e. Open pipe for endpoint zero. |
208 | ; For now, we do not know the actual maximum packet size; |
236 | ; For now, we do not know the actual maximum packet size; |
209 | ; for full-speed devices it can be any of 8, 16, 32, 64 bytes, |
237 | ; for full-speed devices it can be any of 8, 16, 32, 64 bytes, |
210 | ; low-speed devices must have 8 bytes, high-speed devices must have 64 bytes. |
238 | ; low-speed devices must have 8 bytes, high-speed devices must have 64 bytes. |
211 | ; Thus, we must use some fake "maximum packet size" until the actual size |
239 | ; Thus, we must use some fake "maximum packet size" until the actual size |
212 | ; will be known. However, the maximum packet size must be at least 8, and |
240 | ; will be known. However, the maximum packet size must be at least 8, and |
Line 225... | Line 253... | ||
225 | xor edi, edi |
253 | xor edi, edi |
226 | stdcall usb_open_pipe, ecx, edi, 64, edi, edi |
254 | stdcall usb_open_pipe, ecx, edi, 64, edi, edi |
227 | ; Put pointer to pipe into ebx. "xchg eax,reg" is one byte, mov is two bytes. |
255 | ; Put pointer to pipe into ebx. "xchg eax,reg" is one byte, mov is two bytes. |
228 | xchg eax, ebx |
256 | xchg eax, ebx |
229 | pop eax |
257 | pop eax |
230 | ; 1f. If failed, free the memory, the bus address and return zero. |
258 | ; 2f. If failed, free the memory, the bus address and return zero. |
231 | test ebx, ebx |
259 | test ebx, ebx |
232 | jz .freememory |
260 | jz .freememory |
233 | ; 2. Store pointer to device data in the pipe structure. |
261 | ; 3. Store pointer to device data in the pipe structure. |
234 | mov [ebx+usb_pipe.DeviceData], eax |
262 | mov [ebx+usb_pipe.DeviceData], eax |
235 | ; 3. Init device data, using usb_controller.Resetting* variables. |
263 | ; 4. Init device data, using usb_controller.Resetting* variables. |
- | 264 | mov [eax+usb_device_data.Timer], edi |
|
- | 265 | mov dword [eax+usb_device_data.DeviceDescriptor], TIMEOUT_SET_ADDRESS_INITIAL |
|
236 | mov [eax+usb_device_data.TTHub], edi |
266 | mov [eax+usb_device_data.TTHub], edi |
237 | mov [eax+usb_device_data.TTPort], 0 |
267 | mov [eax+usb_device_data.TTPort], 0 |
238 | mov [eax+usb_device_data.NumInterfaces], edi |
268 | mov [eax+usb_device_data.NumInterfaces], edi |
239 | mov [eax+usb_device_data.DeviceDescrSize], 0 |
269 | mov [eax+usb_device_data.DeviceDescrSize], 0 |
240 | mov dl, [esi+usb_controller.ResettingSpeed] |
270 | mov dl, [esi+usb_controller.ResettingSpeed] |
Line 266... | Line 296... | ||
266 | mov [eax+usb_device_data.Interfaces], edi |
296 | mov [eax+usb_device_data.Interfaces], edi |
267 | movzx ecx, [esi+usb_controller.ResettingPort] |
297 | movzx ecx, [esi+usb_controller.ResettingPort] |
268 | mov [eax+usb_device_data.Port], cl |
298 | mov [eax+usb_device_data.Port], cl |
269 | mov edx, [esi+usb_controller.ResettingHub] |
299 | mov edx, [esi+usb_controller.ResettingHub] |
270 | mov [eax+usb_device_data.Hub], edx |
300 | mov [eax+usb_device_data.Hub], edx |
271 | ; 4. Store pointer to the config pipe in the hub data. |
301 | ; 5. Store pointer to the config pipe in the hub data. |
272 | ; Config pipe serves as device identifier. |
302 | ; Config pipe serves as device identifier. |
273 | ; Root hubs use the array inside usb_controller structure, |
303 | ; Root hubs use the array inside usb_controller structure, |
274 | ; non-root hubs use the array immediately after usb_hub structure. |
304 | ; non-root hubs use the array immediately after usb_hub structure. |
275 | test edx, edx |
305 | test edx, edx |
276 | jz .roothub |
306 | jz .roothub |
Line 279... | Line 309... | ||
279 | jmp @f |
309 | jmp @f |
280 | .roothub: |
310 | .roothub: |
281 | mov [esi+usb_controller.DevicesByPort+ecx*4], ebx |
311 | mov [esi+usb_controller.DevicesByPort+ecx*4], ebx |
282 | @@: |
312 | @@: |
283 | call usb_reinit_pipe_list |
313 | call usb_reinit_pipe_list |
284 | ; 5. Issue SET_ADDRESS control request, using buffer filled in step 1a. |
314 | ; 6. Issue SET_ADDRESS control request, using buffer filled in step 2a. |
285 | ; Use the return value from usb_control_async as our return value; |
315 | ; 6a. Configure timer to force reset after timeout. |
- | 316 | ; Note: we can't use self-destructing timer, because we need to be able to cancel it, |
|
- | 317 | ; and for self-destructing timer we could have race condition in cancelling/destructing. |
|
- | 318 | ; DEBUGF 1,'K : pipe %x\n',ebx |
|
- | 319 | .try_set_address: |
|
- | 320 | xor edi, edi |
|
- | 321 | mov edx, [ebx+usb_pipe.DeviceData] |
|
- | 322 | stdcall timer_hs, [edx+usb_device_data.DeviceDescriptor], 7FFFFFFFh, usb_abort_pipe, ebx |
|
- | 323 | test eax, eax |
|
- | 324 | jz .nothing |
|
- | 325 | mov edx, [ebx+usb_pipe.DeviceData] |
|
- | 326 | mov [edx+usb_device_data.Timer], eax |
|
286 | ; if it is zero, then something has failed. |
327 | ; 6b. If it succeeded, setup timer to configure wait timeout. |
287 | lea eax, [esi+usb_controller.SetAddressBuffer] |
328 | lea eax, [esi+usb_controller.SetAddressBuffer] |
288 | stdcall usb_control_async, ebx, eax, edi, edi, usb_set_address_callback, edi, edi |
329 | stdcall usb_control_async, ebx, eax, edi, edi, usb_set_address_callback, edi, edi |
- | 330 | ; Use the return value from usb_control_async as our return value; |
|
- | 331 | ; if it is zero, then something has failed. |
|
289 | .nothing: |
332 | .nothing: |
290 | ; 6. Return. |
333 | ; 7. Return. |
291 | pop edi ebx ; restore used registers to be stdcall |
334 | pop edi ebx ; restore used registers to be stdcall |
292 | ret |
335 | ret |
293 | ; Handlers of failures in steps 1b, 1d, 1f. |
336 | ; Handlers of failures in steps 2b, 2d, 2f. |
294 | .freememory: |
337 | .freememory: |
295 | call free |
338 | call free |
296 | jmp .freeaddr |
339 | jmp .freeaddr |
297 | .nomemory: |
340 | .nomemory: |
298 | dbgstr 'No memory for device data' |
341 | dbgstr 'No memory for device data' |
Line 347... | Line 390... | ||
347 | ; This procedure is called by USB stack when SET_ADDRESS request initiated by |
390 | ; This procedure is called by USB stack when SET_ADDRESS request initiated by |
348 | ; usb_new_device is completed, either successfully or unsuccessfully. |
391 | ; usb_new_device is completed, either successfully or unsuccessfully. |
349 | ; Note that USB stack uses esi = pointer to usb_controller. |
392 | ; Note that USB stack uses esi = pointer to usb_controller. |
350 | proc usb_set_address_callback stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword |
393 | proc usb_set_address_callback stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword |
351 | push ebx ; save ebx to be stdcall |
394 | push ebx ; save ebx to be stdcall |
352 | ; Load data to registers for further references. |
- | |
353 | mov ebx, [pipe] |
395 | mov ebx, [pipe] |
- | 396 | ; 1. In any case, cancel the timer. |
|
- | 397 | mov eax, [ebx+usb_pipe.DeviceData] |
|
- | 398 | stdcall cancel_timer_hs, [eax+usb_device_data.Timer] |
|
- | 399 | mov eax, [ebx+usb_pipe.DeviceData] |
|
- | 400 | mov [eax+usb_device_data.Timer], 0 |
|
- | 401 | ; Load data to registers for further references. |
|
354 | mov ecx, dword [esi+usb_controller.SetAddressBuffer+2] |
402 | mov ecx, dword [esi+usb_controller.SetAddressBuffer+2] |
355 | mov eax, [esi+usb_controller.HardwareFunc] |
403 | mov eax, [esi+usb_controller.HardwareFunc] |
356 | ; 1. Check whether the device has accepted new address. If so, proceed to 2. |
404 | ; 2. Check whether the device has accepted new address. If so, proceed to 3. |
- | 405 | ; Otherwise, go to 4 if killed by usb_set_address_timeout or to 5 otherwise. |
|
- | 406 | cmp [status], USB_STATUS_CANCELLED |
|
357 | ; Otherwise, go to 3. |
407 | jz .timeout |
358 | cmp [status], 0 |
408 | cmp [status], 0 |
359 | jnz .error |
409 | jnz .error |
360 | ; 2. Address accepted. |
410 | ; 3. Address accepted. |
361 | ; 2a. The controller-specific structure for the control pipe still uses |
411 | ; 3a. The controller-specific structure for the control pipe still uses |
362 | ; zero address. Call the controller-specific function to change it to |
412 | ; zero address. Call the controller-specific function to change it to |
363 | ; the actual address. |
413 | ; the actual address. |
364 | ; Note that the hardware could cache the controller-specific structure, |
414 | ; Note that the hardware could cache the controller-specific structure, |
365 | ; so setting the address could take some time until the cache is evicted. |
415 | ; so setting the address could take some time until the cache is evicted. |
366 | ; Thus, the call is asynchronous; meet us in usb_after_set_address when it will |
416 | ; Thus, the call is asynchronous; meet us in usb_after_set_address when it will |
367 | ; be safe to continue. |
417 | ; be safe to continue. |
368 | ; dbgstr 'address set in device' |
418 | ; dbgstr 'address set in device' |
369 | call [eax+usb_hardware_func.SetDeviceAddress] |
419 | call [eax+usb_hardware_func.SetDeviceAddress] |
370 | ; 2b. If the port is in non-root hub, clear 'reset in progress' flag. |
420 | ; 3b. If the port is in non-root hub, clear 'reset in progress' flag. |
371 | ; In any case, proceed to 4. |
421 | ; In any case, proceed to 6. |
372 | mov eax, [esi+usb_controller.ResettingHub] |
422 | mov eax, [esi+usb_controller.ResettingHub] |
373 | test eax, eax |
423 | test eax, eax |
374 | jz .return |
424 | jz .return |
375 | and [eax+usb_hub.Actions], not HUB_RESET_IN_PROGRESS |
425 | and [eax+usb_hub.Actions], not HUB_RESET_IN_PROGRESS |
376 | .return: |
426 | .return: |
377 | ; 4. Address configuration done, we can proceed with other ports. |
427 | ; 6. Address configuration done, we can proceed with other ports. |
378 | ; Call the worker function for that. |
428 | ; Call the worker function for that. |
379 | call usb_test_pending_port |
429 | call usb_test_pending_port |
- | 430 | .wakeup: |
|
- | 431 | push esi edi |
|
- | 432 | call usb_wakeup |
|
- | 433 | pop edi esi |
|
380 | .nothing: |
434 | .nothing: |
381 | pop ebx ; restore ebx to be stdcall |
435 | pop ebx ; restore ebx to be stdcall |
382 | ret |
436 | ret |
- | 437 | .timeout: |
|
- | 438 | ; 4. Device continues to NAK the request. Reset it and retry. |
|
- | 439 | mov edx, [ebx+usb_pipe.DeviceData] |
|
- | 440 | mov ecx, [edx+usb_device_data.DeviceDescriptor] |
|
- | 441 | add ecx, ecx |
|
- | 442 | cmp ecx, TIMEOUT_SET_ADDRESS_LAST |
|
- | 443 | ja .error |
|
- | 444 | mov [edx+usb_device_data.DeviceDescriptor], ecx |
|
- | 445 | dbgstr 'Timeout in USB device initialization, trying to reset...' |
|
- | 446 | cmp [esi+usb_controller.ResettingHub], 0 |
|
- | 447 | jz .reset_roothub |
|
- | 448 | push esi |
|
- | 449 | mov esi, [esi+usb_controller.ResettingHub] |
|
- | 450 | call usb_hub_initiate_reset |
|
- | 451 | pop esi |
|
- | 452 | jmp .nothing |
|
- | 453 | .reset_roothub: |
|
- | 454 | movzx ecx, [esi+usb_controller.ResettingPort] |
|
- | 455 | call [eax+usb_hardware_func.InitiateReset] |
|
- | 456 | jmp .wakeup |
|
383 | .error: |
457 | .error: |
384 | ; 3. Device error: device not responding, disconnect etc. |
458 | ; 5. Device error: device not responding, disconnect etc. |
385 | DEBUGF 1,'K : error %d in SET_ADDRESS, USB device disabled\n',[status] |
459 | DEBUGF 1,'K : error %d in SET_ADDRESS, USB device disabled\n',[status] |
386 | ; 3a. The address has not been accepted. Mark it as free. |
460 | ; 5a. The address has not been accepted. Mark it as free. |
387 | bts dword [esi+usb_controller.ExistingAddresses], ecx |
461 | bts dword [esi+usb_controller.ExistingAddresses], ecx |
388 | ; 3b. Disable the port with bad device. |
462 | ; 5b. Disable the port with bad device. |
389 | ; For the root hub, call the controller-specific function and go to 6. |
463 | ; For the root hub, call the controller-specific function and go to 6. |
390 | ; For non-root hubs, let the hub code do its work and return (the request |
464 | ; For non-root hubs, let the hub code do its work and return (the request |
391 | ; could take some time, the hub code is responsible for proceeding). |
465 | ; could take some time, the hub code is responsible for proceeding). |
392 | cmp [esi+usb_controller.ResettingHub], 0 |
466 | cmp [esi+usb_controller.ResettingHub], 0 |
393 | jz .roothub |
467 | jz .roothub |