Subversion Repositories Kolibri OS

Rev

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

Rev Author Line No. Line
3520 clevermous 1
; Memory management for USB structures.
2
; Protocol layer uses the common kernel heap malloc/free.
3
; Hardware layer has special requirements:
4
; * memory blocks should be properly aligned
5
; * memory blocks should not cross page boundary
6
; Hardware layer allocates fixed-size blocks.
7
; Thus, the specific allocator is quite easy to write:
8
; allocate one page, split into blocks, maintain the single-linked
9
; list of all free blocks in each page.
10
 
11
; Note: size must be a multiple of required alignment.
12
 
13
; Data for one pool: dd pointer to the first page, MUTEX lock.
14
 
15
uglobal
16
; Structures in UHCI and OHCI have equal sizes.
17
; Thus, functions and data for allocating/freeing can be shared;
18
; we keep them here rather than in controller-specific files.
19
align 4
20
; Data for UHCI and OHCI endpoints pool.
21
usb1_ep_first_page      dd      ?
22
usb1_ep_mutex           MUTEX
23
; Data for UHCI and OHCI general transfer descriptors pool.
24
usb_gtd_first_page      dd      ?
25
usb_gtd_mutex           MUTEX
26
endg
27
 
28
; sanity check: structures in UHCI and OHCI should be the same for allocation
3653 clevermous 29
if (sizeof.ohci_pipe = sizeof.uhci_pipe)
3520 clevermous 30
 
31
; Allocates one endpoint structure for UHCI/OHCI.
32
; Returns pointer to software part (usb_pipe) in eax.
33
proc usb1_allocate_endpoint
34
        push    ebx
35
        mov     ebx, usb1_ep_mutex
3653 clevermous 36
        stdcall usb_allocate_common, (sizeof.ohci_pipe + sizeof.usb_pipe + 0Fh) and not 0Fh
3520 clevermous 37
        test    eax, eax
38
        jz      @f
3653 clevermous 39
        add     eax, sizeof.ohci_pipe
3520 clevermous 40
@@:
41
        pop     ebx
42
        ret
43
endp
44
 
45
; Free one endpoint structure for UHCI/OHCI.
46
; Stdcall with one argument, pointer to software part (usb_pipe).
47
proc usb1_free_endpoint
3653 clevermous 48
        sub     dword [esp+4], sizeof.ohci_pipe
3520 clevermous 49
        jmp     usb_free_common
50
endp
51
 
52
else
53
; sanity check continued
54
.err allocate_endpoint/free_endpoint must be different for OHCI and UHCI
55
end if
56
 
57
; sanity check: structures in UHCI and OHCI should be the same for allocation
3653 clevermous 58
if (sizeof.ohci_gtd = sizeof.uhci_gtd)
3520 clevermous 59
 
60
; Allocates one general transfer descriptor structure for UHCI/OHCI.
61
; Returns pointer to software part (usb_gtd) in eax.
62
proc usb1_allocate_general_td
63
        push    ebx
64
        mov     ebx, usb_gtd_mutex
3653 clevermous 65
        stdcall usb_allocate_common, (sizeof.ohci_gtd + sizeof.usb_gtd + 0Fh) and not 0Fh
3520 clevermous 66
        test    eax, eax
67
        jz      @f
3653 clevermous 68
        add     eax, sizeof.ohci_gtd
3520 clevermous 69
@@:
70
        pop     ebx
71
        ret
72
endp
73
 
74
; Free one general transfer descriptor structure for UHCI/OHCI.
75
; Stdcall with one argument, pointer to software part (usb_gtd).
76
proc usb1_free_general_td
3653 clevermous 77
        sub     dword [esp+4], sizeof.ohci_gtd
3520 clevermous 78
        jmp     usb_free_common
79
endp
80
 
81
else
82
; sanity check continued
83
.err allocate_general_td/free_general_td must be different for OHCI and UHCI
84
end if
85
 
86
; Allocator for fixed-size blocks: allocate a block.
87
; [ebx-4] = pointer to the first page, ebx = pointer to MUTEX structure.
88
proc usb_allocate_common
89
        push    edi     ; save used register to be stdcall
90
virtual at esp
91
        dd      ?       ; saved edi
92
        dd      ?       ; return address
93
.size   dd      ?
94
end virtual
95
; 1. Take the lock.
96
        mov     ecx, ebx
97
        call    mutex_lock
98
; 2. Find the first allocated page with a free block, if any.
99
; 2a. Initialize for the loop.
100
        mov     edx, ebx
101
.pageloop:
102
; 2b. Get the next page, keeping the current in eax.
103
        mov     eax, edx
104
        mov     edx, [edx-4]
105
; 2c. If there is no next page, we're out of luck; go to 4.
106
        test    edx, edx
107
        jz      .newpage
108
        add     edx, 0x1000
109
@@:
110
; 2d. Get the pointer to the first free block on this page.
111
; If there is no free block, continue to 2b.
112
        mov     eax, [edx-8]
113
        test    eax, eax
114
        jz      .pageloop
115
; 2e. Get the pointer to the next free block.
116
        mov     ecx, [eax]
117
; 2f. Update the pointer to the first free block from eax to ecx.
118
; Normally [edx-8] still contains eax, if so, atomically set it to ecx
119
; and proceed to 3.
120
; However, the price of simplicity of usb_free_common (in particular, it
121
; doesn't take the lock) is that [edx-8] could (rarely) be changed while
122
; we processed steps 2d+2e. If so, return to 2d and retry.
123
        lock cmpxchg [edx-8], ecx
124
        jnz     @b
125
.return:
126
; 3. Release the lock taken in step 1 and return.
127
        push    eax
128
        mov     ecx, ebx
129
        call    mutex_unlock
130
        pop     eax
131
        pop     edi     ; restore used register to be stdcall
132
        ret     4
133
.newpage:
134
; 4. Allocate a new page.
135
        push    eax
136
        stdcall kernel_alloc, 0x1000
137
        pop     edx
138
; If failed, say something to the debug board and return zero.
139
        test    eax, eax
140
        jz      .nomemory
141
; 5. Add the new page to the tail of list of allocated pages.
142
        mov     [edx-4], eax
143
; 6. Initialize two service dwords in the end of page:
144
; first free block is (start of page) + (block size)
145
; (we will return first block at (start of page), so consider it allocated),
146
; no next page.
147
        mov     edx, eax
148
        lea     edi, [eax+0x1000-8]
149
        add     edx, [.size]
150
        mov     [edi], edx
151
        and     dword [edi+4], 0
152
; 7. All blocks starting from edx are free; join them in a single-linked list.
153
@@:
154
        mov     ecx, edx
155
        add     edx, [.size]
156
        mov     [ecx], edx
157
        cmp     edx, edi
158
        jbe     @b
159
        sub     ecx, [.size]
160
        and     dword [ecx], 0
161
; 8. Return (start of page).
162
        jmp     .return
163
.nomemory:
164
        dbgstr 'no memory for USB descriptor'
165
        xor     eax, eax
166
        jmp     .return
167
endp
168
 
169
; Allocator for fixed-size blocks: free a block.
170
proc usb_free_common
171
        push    ecx edx
172
virtual at esp
173
        rd      2       ; saved registers
174
        dd      ?       ; return address
175
.block  dd      ?
176
end virtual
177
; Insert the given block to the head of free blocks in this page.
178
        mov     ecx, [.block]
179
        mov     edx, ecx
180
        or      edx, 0xFFF
181
@@:
182
        mov     eax, [edx+1-8]
183
        mov     [ecx], eax
184
        lock cmpxchg [edx+1-8], ecx
185
        jnz     @b
186
        pop     edx ecx
187
        ret     4
188
endp
189
 
190
; Helper procedure for OHCI: translate physical address in ecx
191
; of some transfer descriptor to linear address.
192
proc usb_td_to_virt
193
; Traverse all pages used for transfer descriptors, looking for the one
194
; with physical address as in ecx.
195
        mov     eax, [usb_gtd_first_page]
196
@@:
197
        test    eax, eax
198
        jz      .zero
199
        push    eax
200
        call    get_pg_addr
201
        sub     eax, ecx
202
        jz      .found
203
        cmp     eax, -0x1000
204
        ja      .found
205
        pop     eax
206
        mov     eax, [eax+0x1000-4]
207
        jmp     @b
208
.found:
209
; When found, combine page address from eax with page offset from ecx.
210
        pop     eax
211
        and     ecx, 0xFFF
212
        add     eax, ecx
213
.zero:
214
        ret
215
endp