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