Subversion Repositories Kolibri OS

Rev

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

Rev Author Line No. Line
3520 clevermous 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.
136
Port            db      ?
137
; Port on the hub, zero-based.
138
DeviceDescrSize db      ?
139
; Size of device descriptor.
140
NumInterfaces   db      ?
141
; Number of interfaces.
142
Speed           db      ?
143
; Device speed, one of USB_SPEED_*.
144
ConfigDataSize  dd      ?
145
; Total size of data associated with the configuration descriptor
146
; (including the configuration descriptor itself);
147
Interfaces      dd      ?
148
; Offset from the beginning of this structure to Interfaces field.
149
; Variable-length fields:
150
; DeviceDescriptor:
151
;  device descriptor starts here
152
; ConfigDescriptor = DeviceDescriptor + DeviceDescrSize
153
;  configuration descriptor with all associated data
154
; Interfaces = ALIGN_UP(ConfigDescriptor + ConfigDataSize, 4)
155
;  array of NumInterfaces elements of type usb_interface_data
156
ends
157
 
158
usb_device_data.DeviceDescriptor = sizeof.usb_device_data
159
 
160
; Description of controller-specific data and functions.
161
struct usb_hardware_func
162
ID              dd      ?       ; '*HCI'
163
DataSize        dd      ?       ; sizeof(*hci_controller)
164
Init            dd      ?
165
; Initialize controller-specific part of controller data.
166
; in: eax -> *hci_controller to initialize, [ebp-4] = (bus shl 8) + devfn
167
; out: eax = 0 <=> failed, otherwise eax -> usb_controller
168
ProcessDeferred dd      ?
169
; Called regularly from the main loop of USB thread
170
; (either due to timeout from a previous call, or due to explicit wakeup).
171
; in: esi -> usb_controller
172
; out: eax = maximum timeout for next call (-1 = infinity)
173
SetDeviceAddress        dd      ?
174
; in: esi -> usb_controller, ebx -> usb_pipe, cl = address
175
GetDeviceAddress        dd      ?
176
; in: esi -> usb_controller, ebx -> usb_pipe
177
; out: eax = address
178
PortDisable             dd      ?
179
; Disable the given port in the root hub.
180
; in: esi -> usb_controller, ecx = port (zero-based)
181
InitiateReset           dd      ?
182
; Start reset signalling on the given port.
183
; in: esi -> usb_controller, ecx = port (zero-based)
184
SetEndpointPacketSize   dd      ?
185
; in: esi -> usb_controller, ebx -> usb_pipe, ecx = packet size
186
AllocPipe               dd      ?
187
; out: eax = pointer to allocated usb_pipe
188
FreePipe                dd      ?
189
; void stdcall with one argument = pointer to previously allocated usb_pipe
190
InitPipe                dd      ?
191
; in: edi -> usb_pipe for target, ecx -> usb_pipe for config pipe,
192
; esi -> usb_controller, eax -> usb_gtd for the first TD,
193
; [ebp+12] = endpoint, [ebp+16] = maxpacket, [ebp+20] = type
194
UnlinkPipe              dd      ?
195
; esi -> usb_controller, ebx -> usb_pipe
196
AllocTD                 dd      ?
197
; out: eax = pointer to allocated usb_gtd
198
FreeTD                  dd      ?
199
; void stdcall with one argument = pointer to previously allocated usb_gtd
200
AllocTransfer           dd      ?
201
; Allocate and initialize one stage of a transfer.
202
; ebx -> usb_pipe, other parameters are passed through the stack:
203
; buffer,size = data to transfer
204
; flags = same as in usb_open_pipe:
205
;   bit 0 = allow short transfer, other bits reserved
206
; td = pointer to the current end-of-queue descriptor
207
; direction =
208
;   0000b for normal transfers,
209
;   1000b for control SETUP transfer,
210
;   1101b for control OUT transfer,
211
;   1110b for control IN transfer
212
; returns eax = pointer to the new end-of-queue descriptor
213
; (not included in the queue itself) or 0 on error
214
InsertTransfer          dd      ?
215
; Activate previously initialized transfer (maybe with multiple stages).
216
; esi -> usb_controller, ebx -> usb_pipe,
217
; [esp+4] -> first usb_gtd for the transfer,
218
; ecx -> last descriptor for the transfer
219
NewDevice               dd      ?
220
; Initiate configuration of a new device (create pseudo-pipe describing that
221
; device and call usb_new_device).
222
; esi -> usb_controller, eax = speed (one of USB_SPEED_* constants).
223
ends
224
 
225
; =============================================================================
226
; =================================== Code ====================================
227
; =============================================================================
228
 
229
; Initializes one controller, called by usb_init for every controller.
230
; edi -> usb_hardware_func, eax -> PCIDEV structure for the device.
231
proc usb_init_controller
232
        push    ebp
233
        mov     ebp, esp
234
; 1. Store in the stack PCI coordinates and save pointer to PCIDEV:
235
; make [ebp-4] = (bus shl 8) + devfn, used by controller-specific Init funcs.
236
        push    dword [eax+PCIDEV.devfn]
237
        push    eax
238
; 2. Allocate *hci_controller + usb_controller.
239
        mov     ebx, [edi+usb_hardware_func.DataSize]
240
        add     ebx, sizeof.usb_controller
241
        stdcall kernel_alloc, ebx
242
        test    eax, eax
243
        jz      .nothing
244
; 3. Zero-initialize both structures.
245
        push    edi eax
246
        mov     ecx, ebx
247
        shr     ecx, 2
248
        xchg    edi, eax
249
        xor     eax, eax
250
        rep stosd
251
; 4. Initialize usb_controller structure,
252
; except data known only to controller-specific code (like NumPorts)
253
; and link fields
254
; (this structure will be inserted to the overall list at step 6).
255
        dec     eax
256
        mov     [edi+usb_controller.ExistingAddresses+4-sizeof.usb_controller], eax
257
        mov     [edi+usb_controller.ExistingAddresses+8-sizeof.usb_controller], eax
258
        mov     [edi+usb_controller.ExistingAddresses+12-sizeof.usb_controller], eax
259
        mov     [edi+usb_controller.ResettingPort-sizeof.usb_controller], al    ; no resetting port
260
        dec     eax     ; don't allocate zero address
261
        mov     [edi+usb_controller.ExistingAddresses-sizeof.usb_controller], eax
262
        lea     ecx, [edi+usb_controller.PeriodicLock-sizeof.usb_controller]
263
        call    mutex_init
264
        add     ecx, usb_controller.ControlLock - usb_controller.PeriodicLock
265
        call    mutex_init
266
        add     ecx, usb_controller.BulkLock - usb_controller.ControlLock
267
        call    mutex_init
268
        pop     eax edi
269
        mov     [eax+ebx-sizeof.usb_controller+usb_controller.HardwareFunc], edi
270
        push    eax
271
; 5. Call controller-specific initialization.
272
; If failed, free memory allocated in step 2 and return.
273
        call    [edi+usb_hardware_func.Init]
274
        test    eax, eax
275
        jz      .fail
276
        pop     ecx
277
; 6. Insert the controller to the global list.
278
        xchg    eax, ebx
279
        mov     ecx, usb_controllers_list_mutex
280
        call    mutex_lock
281
        mov     edx, usb_controllers_list
282
        mov     eax, [edx+usb_controller.Prev]
283
        mov     [ebx+usb_controller.Next], edx
284
        mov     [ebx+usb_controller.Prev], eax
285
        mov     [edx+usb_controller.Prev], ebx
286
        mov     [eax+usb_controller.Next], ebx
287
        call    mutex_unlock
288
; 7. Wakeup USB thread to call ProcessDeferred.
289
        call    usb_wakeup
290
.nothing:
291
; 8. Restore pointer to PCIDEV saved in step 1 and return.
292
        pop     eax
293
        leave
294
        ret
295
.fail:
296
        call    kernel_free
297
        jmp     .nothing
298
endp
299
 
300
; Helper function, calculates physical address including offset in page.
301
proc get_phys_addr
302
        push    ecx
303
        mov     ecx, eax
304
        and     ecx, 0xFFF
305
        call    get_pg_addr
306
        add     eax, ecx
307
        pop     ecx
308
        ret
309
endp
310
 
311
; Put the given control pipe in the wait list;
312
; called when the pipe structure is changed and a possible hardware cache
313
; needs to be synchronized. When it will be known that the cache is updated,
314
; usb_subscription_done procedure will be called.
315
proc usb_subscribe_control
316
        cmp     [ebx+usb_pipe.NextWait], -1
317
        jnz     @f
318
        mov     eax, [esi+usb_controller.WaitPipeListAsync]
319
        mov     [ebx+usb_pipe.NextWait], eax
320
        mov     [esi+usb_controller.WaitPipeListAsync], ebx
321
@@:
322
        ret
323
endp
324
 
325
; Called after synchronization of hardware cache with software changes.
326
; Continues process of device enumeration based on when it was delayed
327
; due to call to usb_subscribe_control.
328
proc usb_subscription_done
329
        mov     eax, [ebx+usb_pipe.DeviceData]
330
        cmp     [eax+usb_device_data.DeviceDescrSize], 0
331
        jz      usb_after_set_address
332
        jmp     usb_after_set_endpoint_size
333
endp
334
 
335
; This function is called when a new device has either passed
336
; or failed first stages of configuration, so the next device
337
; can enter configuration process.
338
proc usb_test_pending_port
339
        mov     [esi+usb_controller.ResettingPort], -1
340
        cmp     [esi+usb_controller.PendingPorts], 0
341
        jz      .nothing
342
        bsf     ecx, [esi+usb_controller.PendingPorts]
343
        btr     [esi+usb_controller.PendingPorts], ecx
344
        mov     eax, [esi+usb_controller.HardwareFunc]
345
        jmp     [eax+usb_hardware_func.InitiateReset]
346
.nothing:
347
        ret
348
endp
349
 
350
; This procedure is regularly called from controller-specific ProcessDeferred,
351
; it checks whether there are disconnected events and if so, process them.
352
proc usb_disconnect_stage2
353
        bsf     ecx, [esi+usb_controller.NewDisconnected]
354
        jz      .nothing
355
        lock btr [esi+usb_controller.NewDisconnected], ecx
356
        btr     [esi+usb_controller.PendingPorts], ecx
357
        xor     ebx, ebx
358
        xchg    ebx, [esi+usb_controller.DevicesByPort+ecx*4]
359
        test    ebx, ebx
360
        jz      usb_disconnect_stage2
361
        call    usb_device_disconnected
362
        jmp     usb_disconnect_stage2
363
.nothing:
364
        ret
365
endp
366
 
367
; Initial stage of disconnect processing: called when device is disconnected.
368
proc usb_device_disconnected
369
; Loop over all pipes, close everything, wait until hardware reacts.
370
; The final handling is done in usb_pipe_closed.
371
        push    ebx
372
        mov     ecx, [ebx+usb_pipe.DeviceData]
373
        call    mutex_lock
374
        lea     eax, [ecx+usb_device_data.OpenedPipeList-usb_pipe.NextSibling]
375
        push    eax
376
        mov     ebx, [eax+usb_pipe.NextSibling]
377
.pipe_loop:
378
        call    usb_close_pipe_nolock
379
        mov     ebx, [ebx+usb_pipe.NextSibling]
380
        cmp     ebx, [esp]
381
        jnz     .pipe_loop
382
        pop     eax
383
        pop     ebx
384
        mov     ecx, [ebx+usb_pipe.DeviceData]
385
        call    mutex_unlock
386
        ret
387
endp
388
 
389
; Called from controller-specific ProcessDeferred,
390
; processes wait-pipe-done notifications,
391
; returns whether there are more items in wait queues.
392
; in: esi -> usb_controller
393
; out: eax = bitmask of pipe types with non-empty wait queue
394
proc usb_process_wait_lists
395
        xor     edx, edx
396
        push    edx
397
        call    usb_process_one_wait_list
398
        jnc     @f
399
        or      byte [esp], 1 shl CONTROL_PIPE
400
@@:
3598 clevermous 401
        movi    edx, 4
3520 clevermous 402
        call    usb_process_one_wait_list
403
        jnc     @f
404
        or      byte [esp], 1 shl INTERRUPT_PIPE
405
@@:
406
        xor     edx, edx
407
        call    usb_process_one_wait_list
408
        jnc     @f
409
        or      byte [esp], 1 shl CONTROL_PIPE
410
@@:
411
        pop     eax
412
        ret
413
endp
414
 
415
; Helper procedure for usb_process_wait_lists;
416
; does the same for one wait queue.
417
; in: esi -> usb_controller,
418
; edx=0 for *Async, edx=4 for *Periodic list
419
; out: CF = issue new request
420
proc usb_process_one_wait_list
421
; 1. Check whether there is a pending request. If so, do nothing.
422
        mov     ebx, [esi+usb_controller.WaitPipeRequestAsync+edx]
423
        cmp     ebx, [esi+usb_controller.ReadyPipeHeadAsync+edx]
424
        clc
425
        jnz     .nothing
426
; 2. Check whether there are new data. If so, issue a new request.
427
        cmp     ebx, [esi+usb_controller.WaitPipeListAsync+edx]
428
        stc
429
        jnz     .nothing
430
        test    ebx, ebx
431
        jz      .nothing
432
; 3. Clear all lists.
433
        xor     ecx, ecx
434
        mov     [esi+usb_controller.WaitPipeListAsync+edx], ecx
435
        mov     [esi+usb_controller.WaitPipeRequestAsync+edx], ecx
436
        mov     [esi+usb_controller.ReadyPipeHeadAsync+edx], ecx
437
; 4. Loop over all pipes from the wait list.
438
.pipe_loop:
439
; For every pipe:
440
; 5. Save edx and next pipe in the list.
441
        push    edx
442
        push    [ebx+usb_pipe.NextWait]
443
; 6. If USB_FLAG_EXTRA_WAIT is set, reinsert the pipe to the list and continue.
444
        test    [ebx+usb_pipe.Flags], USB_FLAG_EXTRA_WAIT
445
        jz      .process
446
        mov     eax, [esi+usb_controller.WaitPipeListAsync+edx]
447
        mov     [ebx+usb_pipe.NextWait], eax
448
        mov     [esi+usb_controller.WaitPipeListAsync+edx], ebx
449
        jmp     .continue
450
.process:
451
; 7. Call the handler depending on USB_FLAG_CLOSED.
452
        or      [ebx+usb_pipe.NextWait], -1
453
        test    [ebx+usb_pipe.Flags], USB_FLAG_CLOSED
454
        jz      .nodisconnect
455
        call    usb_pipe_closed
456
        jmp     .continue
457
.nodisconnect:
458
        call    usb_subscription_done
459
.continue:
460
; 8. Restore edx and next pipe saved in step 5 and continue the loop.
461
        pop     ebx
462
        pop     edx
463
        test    ebx, ebx
464
        jnz     .pipe_loop
465
.check_new_work:
466
; 9. Set CF depending on whether WaitPipeList* is nonzero.
467
        cmp     [esi+usb_controller.WaitPipeListAsync+edx], 1
468
        cmc
469
.nothing:
470
        ret
471
endp