Subversion Repositories Kolibri OS

Rev

Rev 3626 | Rev 4423 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
3555 Serge 1
; USB Host Controller support code: hardware-independent part,
2
; common for all controller types.
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
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      ?
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.
116
ends
117
 
118
; Device-specific data.
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
131
; after disconnect processing.
132
NumPipes        dd      ?
133
; Number of not-yet-closed pipes.
134
Hub             dd      ?
135
; NULL if connected to the root hub, pointer to usb_hub otherwise.
3908 Serge 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.
3555 Serge 139
Port            db      ?
140
; Port on the hub, zero-based.
3908 Serge 141
TTPort          db      ?
142
; Port on the TTHub, zero-based.
3555 Serge 143
DeviceDescrSize db      ?
144
; Size of device descriptor.
145
Speed           db      ?
146
; Device speed, one of USB_SPEED_*.
3908 Serge 147
NumInterfaces   dd      ?
148
; Number of interfaces.
3555 Serge 149
ConfigDataSize  dd      ?
150
; Total size of data associated with the configuration descriptor
3908 Serge 151
; (including the configuration descriptor itself).
3555 Serge 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
167
ID              dd      ?       ; '*HCI'
168
DataSize        dd      ?       ; sizeof(*hci_controller)
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).
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
183
PortDisable             dd      ?
184
; Disable the given port in the root hub.
185
; in: esi -> usb_controller, ecx = port (zero-based)
186
InitiateReset           dd      ?
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
191
AllocPipe               dd      ?
192
; out: eax = pointer to allocated usb_pipe
193
FreePipe                dd      ?
194
; void stdcall with one argument = pointer to previously allocated usb_pipe
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
201
AllocTD                 dd      ?
202
; out: eax = pointer to allocated usb_gtd
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
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
226
; device and call usb_new_device).
227
; esi -> usb_controller, eax = speed (one of USB_SPEED_* constants).
228
ends
229
 
230
; =============================================================================
231
; =================================== Code ====================================
232
; =============================================================================
233
 
234
; Initializes one controller, called by usb_init for every controller.
235
; edi -> usb_hardware_func, eax -> PCIDEV structure for the device.
236
proc usb_init_controller
237
        push    ebp
238
        mov     ebp, esp
239
; 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.
241
        push    dword [eax+PCIDEV.devfn]
242
        push    eax
243
; 2. Allocate *hci_controller + usb_controller.
244
        mov     ebx, [edi+usb_hardware_func.DataSize]
245
        add     ebx, sizeof.usb_controller
246
        stdcall kernel_alloc, ebx
247
        test    eax, eax
248
        jz      .nothing
249
; 3. Zero-initialize both structures.
250
        push    edi eax
251
        mov     ecx, ebx
252
        shr     ecx, 2
253
        xchg    edi, eax
254
        xor     eax, eax
255
        rep stosd
256
; 4. Initialize usb_controller structure,
257
; except data known only to controller-specific code (like NumPorts)
258
; and link fields
259
; (this structure will be inserted to the overall list at step 6).
260
        dec     eax
261
        mov     [edi+usb_controller.ExistingAddresses+4-sizeof.usb_controller], eax
262
        mov     [edi+usb_controller.ExistingAddresses+8-sizeof.usb_controller], eax
263
        mov     [edi+usb_controller.ExistingAddresses+12-sizeof.usb_controller], eax
264
        mov     [edi+usb_controller.ResettingPort-sizeof.usb_controller], al    ; no resetting port
265
        dec     eax     ; don't allocate zero address
266
        mov     [edi+usb_controller.ExistingAddresses-sizeof.usb_controller], eax
267
        lea     ecx, [edi+usb_controller.PeriodicLock-sizeof.usb_controller]
268
        call    mutex_init
269
        add     ecx, usb_controller.ControlLock - usb_controller.PeriodicLock
270
        call    mutex_init
271
        add     ecx, usb_controller.BulkLock - usb_controller.ControlLock
272
        call    mutex_init
273
        pop     eax edi
274
        mov     [eax+ebx-sizeof.usb_controller+usb_controller.HardwareFunc], edi
275
        push    eax
276
; 5. Call controller-specific initialization.
277
; If failed, free memory allocated in step 2 and return.
278
        call    [edi+usb_hardware_func.Init]
279
        test    eax, eax
280
        jz      .fail
281
        pop     ecx
282
; 6. Insert the controller to the global list.
283
        xchg    eax, ebx
284
        mov     ecx, usb_controllers_list_mutex
285
        call    mutex_lock
286
        mov     edx, usb_controllers_list
287
        mov     eax, [edx+usb_controller.Prev]
288
        mov     [ebx+usb_controller.Next], edx
289
        mov     [ebx+usb_controller.Prev], eax
290
        mov     [edx+usb_controller.Prev], ebx
291
        mov     [eax+usb_controller.Next], ebx
292
        call    mutex_unlock
293
; 7. Wakeup USB thread to call ProcessDeferred.
294
        call    usb_wakeup
295
.nothing:
296
; 8. Restore pointer to PCIDEV saved in step 1 and return.
297
        pop     eax
298
        leave
299
        ret
300
.fail:
301
        call    kernel_free
302
        jmp     .nothing
303
endp
304
 
305
; Helper function, calculates physical address including offset in page.
306
proc get_phys_addr
307
        push    ecx
308
        mov     ecx, eax
309
        and     ecx, 0xFFF
310
        call    get_pg_addr
311
        add     eax, ecx
312
        pop     ecx
313
        ret
314
endp
315
 
316
; Put the given control pipe in the wait list;
317
; called when the pipe structure is changed and a possible hardware cache
318
; needs to be synchronized. When it will be known that the cache is updated,
319
; usb_subscription_done procedure will be called.
320
proc usb_subscribe_control
321
        cmp     [ebx+usb_pipe.NextWait], -1
322
        jnz     @f
323
        mov     eax, [esi+usb_controller.WaitPipeListAsync]
324
        mov     [ebx+usb_pipe.NextWait], eax
325
        mov     [esi+usb_controller.WaitPipeListAsync], ebx
326
@@:
327
        ret
328
endp
329
 
330
; Called after synchronization of hardware cache with software changes.
331
; Continues process of device enumeration based on when it was delayed
332
; due to call to usb_subscribe_control.
333
proc usb_subscription_done
334
        mov     eax, [ebx+usb_pipe.DeviceData]
335
        cmp     [eax+usb_device_data.DeviceDescrSize], 0
336
        jz      usb_after_set_address
337
        jmp     usb_after_set_endpoint_size
338
endp
339
 
340
; This function is called when a new device has either passed
341
; or failed first stages of configuration, so the next device
342
; can enter configuration process.
343
proc usb_test_pending_port
344
        mov     [esi+usb_controller.ResettingPort], -1
345
        cmp     [esi+usb_controller.PendingPorts], 0
346
        jz      .nothing
347
        bsf     ecx, [esi+usb_controller.PendingPorts]
348
        btr     [esi+usb_controller.PendingPorts], ecx
349
        mov     eax, [esi+usb_controller.HardwareFunc]
350
        jmp     [eax+usb_hardware_func.InitiateReset]
351
.nothing:
352
        ret
353
endp
354
 
355
; This procedure is regularly called from controller-specific ProcessDeferred,
356
; it checks whether there are disconnected events and if so, process them.
357
proc usb_disconnect_stage2
358
        bsf     ecx, [esi+usb_controller.NewDisconnected]
359
        jz      .nothing
360
        lock btr [esi+usb_controller.NewDisconnected], ecx
361
        btr     [esi+usb_controller.PendingPorts], ecx
362
        xor     ebx, ebx
363
        xchg    ebx, [esi+usb_controller.DevicesByPort+ecx*4]
364
        test    ebx, ebx
365
        jz      usb_disconnect_stage2
366
        call    usb_device_disconnected
367
        jmp     usb_disconnect_stage2
368
.nothing:
369
        ret
370
endp
371
 
372
; Initial stage of disconnect processing: called when device is disconnected.
373
proc usb_device_disconnected
374
; Loop over all pipes, close everything, wait until hardware reacts.
375
; The final handling is done in usb_pipe_closed.
376
        push    ebx
377
        mov     ecx, [ebx+usb_pipe.DeviceData]
378
        call    mutex_lock
379
        lea     eax, [ecx+usb_device_data.OpenedPipeList-usb_pipe.NextSibling]
380
        push    eax
381
        mov     ebx, [eax+usb_pipe.NextSibling]
382
.pipe_loop:
383
        call    usb_close_pipe_nolock
384
        mov     ebx, [ebx+usb_pipe.NextSibling]
385
        cmp     ebx, [esp]
386
        jnz     .pipe_loop
387
        pop     eax
388
        pop     ebx
389
        mov     ecx, [ebx+usb_pipe.DeviceData]
390
        call    mutex_unlock
391
        ret
392
endp
393
 
394
; Called from controller-specific ProcessDeferred,
395
; processes wait-pipe-done notifications,
396
; returns whether there are more items in wait queues.
397
; in: esi -> usb_controller
398
; out: eax = bitmask of pipe types with non-empty wait queue
399
proc usb_process_wait_lists
400
        xor     edx, edx
401
        push    edx
402
        call    usb_process_one_wait_list
403
        jnc     @f
404
        or      byte [esp], 1 shl CONTROL_PIPE
405
@@:
3626 Serge 406
        movi    edx, 4
3555 Serge 407
        call    usb_process_one_wait_list
408
        jnc     @f
409
        or      byte [esp], 1 shl INTERRUPT_PIPE
410
@@:
411
        xor     edx, edx
412
        call    usb_process_one_wait_list
413
        jnc     @f
414
        or      byte [esp], 1 shl CONTROL_PIPE
415
@@:
416
        pop     eax
417
        ret
418
endp
419
 
420
; Helper procedure for usb_process_wait_lists;
421
; does the same for one wait queue.
422
; in: esi -> usb_controller,
423
; edx=0 for *Async, edx=4 for *Periodic list
424
; out: CF = issue new request
425
proc usb_process_one_wait_list
426
; 1. Check whether there is a pending request. If so, do nothing.
427
        mov     ebx, [esi+usb_controller.WaitPipeRequestAsync+edx]
428
        cmp     ebx, [esi+usb_controller.ReadyPipeHeadAsync+edx]
429
        clc
430
        jnz     .nothing
431
; 2. Check whether there are new data. If so, issue a new request.
432
        cmp     ebx, [esi+usb_controller.WaitPipeListAsync+edx]
433
        stc
434
        jnz     .nothing
435
        test    ebx, ebx
436
        jz      .nothing
437
; 3. Clear all lists.
438
        xor     ecx, ecx
439
        mov     [esi+usb_controller.WaitPipeListAsync+edx], ecx
440
        mov     [esi+usb_controller.WaitPipeRequestAsync+edx], ecx
441
        mov     [esi+usb_controller.ReadyPipeHeadAsync+edx], ecx
442
; 4. Loop over all pipes from the wait list.
443
.pipe_loop:
444
; For every pipe:
445
; 5. Save edx and next pipe in the list.
446
        push    edx
447
        push    [ebx+usb_pipe.NextWait]
448
; 6. If USB_FLAG_EXTRA_WAIT is set, reinsert the pipe to the list and continue.
449
        test    [ebx+usb_pipe.Flags], USB_FLAG_EXTRA_WAIT
450
        jz      .process
451
        mov     eax, [esi+usb_controller.WaitPipeListAsync+edx]
452
        mov     [ebx+usb_pipe.NextWait], eax
453
        mov     [esi+usb_controller.WaitPipeListAsync+edx], ebx
454
        jmp     .continue
455
.process:
456
; 7. Call the handler depending on USB_FLAG_CLOSED.
457
        or      [ebx+usb_pipe.NextWait], -1
458
        test    [ebx+usb_pipe.Flags], USB_FLAG_CLOSED
459
        jz      .nodisconnect
460
        call    usb_pipe_closed
461
        jmp     .continue
462
.nodisconnect:
463
        call    usb_subscription_done
464
.continue:
465
; 8. Restore edx and next pipe saved in step 5 and continue the loop.
466
        pop     ebx
467
        pop     edx
468
        test    ebx, ebx
469
        jnz     .pipe_loop
470
.check_new_work:
471
; 9. Set CF depending on whether WaitPipeList* is nonzero.
472
        cmp     [esi+usb_controller.WaitPipeListAsync+edx], 1
473
        cmc
474
.nothing:
475
        ret
476
endp