Subversion Repositories Kolibri OS

Rev

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

Rev Author Line No. Line
4923 Serge 1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2
;;                                                              ;;
3
;; Copyright (C) KolibriOS team 2013-2014. All rights reserved. ;;
4
;; Distributed under terms of the GNU General Public License    ;;
5
;;                                                              ;;
6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7
 
8
$Revision: 4850 $
9
 
4429 Serge 10
; Memory management for USB structures.
11
; Protocol layer uses the common kernel heap malloc/free.
12
; Hardware layer has special requirements:
13
; * memory blocks should be properly aligned
14
; * memory blocks should not cross page boundary
15
; Hardware layer allocates fixed-size blocks.
16
; Thus, the specific allocator is quite easy to write:
17
; allocate one page, split into blocks, maintain the single-linked
18
; list of all free blocks in each page.
19
 
20
; Note: size must be a multiple of required alignment.
21
 
22
; Data for one pool: dd pointer to the first page, MUTEX lock.
23
 
24
; Allocator for fixed-size blocks: allocate a block.
25
; [ebx-4] = pointer to the first page, ebx = pointer to MUTEX structure.
26
proc usb_allocate_common
27
        push    edi     ; save used register to be stdcall
28
virtual at esp
29
        dd      ?       ; saved edi
30
        dd      ?       ; return address
31
.size   dd      ?
32
end virtual
33
; 1. Take the lock.
34
        mov     ecx, ebx
35
        call    mutex_lock
36
; 2. Find the first allocated page with a free block, if any.
37
; 2a. Initialize for the loop.
38
        mov     edx, ebx
39
.pageloop:
40
; 2b. Get the next page, keeping the current in eax.
41
        mov     eax, edx
42
        mov     edx, [edx-4]
43
; 2c. If there is no next page, we're out of luck; go to 4.
44
        test    edx, edx
45
        jz      .newpage
46
        add     edx, 0x1000
47
@@:
48
; 2d. Get the pointer to the first free block on this page.
49
; If there is no free block, continue to 2b.
50
        mov     eax, [edx-8]
51
        test    eax, eax
52
        jz      .pageloop
53
; 2e. Get the pointer to the next free block.
54
        mov     ecx, [eax]
55
; 2f. Update the pointer to the first free block from eax to ecx.
56
; Normally [edx-8] still contains eax, if so, atomically set it to ecx
57
; and proceed to 3.
58
; However, the price of simplicity of usb_free_common (in particular, it
59
; doesn't take the lock) is that [edx-8] could (rarely) be changed while
60
; we processed steps 2d+2e. If so, return to 2d and retry.
61
        lock cmpxchg [edx-8], ecx
62
        jnz     @b
63
.return:
64
; 3. Release the lock taken in step 1 and return.
65
        push    eax
66
        mov     ecx, ebx
67
        call    mutex_unlock
68
        pop     eax
69
        pop     edi     ; restore used register to be stdcall
70
        ret     4
71
.newpage:
72
; 4. Allocate a new page.
73
        push    eax
74
        stdcall kernel_alloc, 0x1000
75
        pop     edx
76
; If failed, say something to the debug board and return zero.
77
        test    eax, eax
78
        jz      .nomemory
79
; 5. Add the new page to the tail of list of allocated pages.
80
        mov     [edx-4], eax
81
; 6. Initialize two service dwords in the end of page:
82
; first free block is (start of page) + (block size)
83
; (we will return first block at (start of page), so consider it allocated),
84
; no next page.
85
        mov     edx, eax
86
        lea     edi, [eax+0x1000-8]
87
        add     edx, [.size]
88
        mov     [edi], edx
89
        and     dword [edi+4], 0
90
; 7. All blocks starting from edx are free; join them in a single-linked list.
91
@@:
92
        mov     ecx, edx
93
        add     edx, [.size]
94
        mov     [ecx], edx
95
        cmp     edx, edi
96
        jbe     @b
97
        sub     ecx, [.size]
98
        and     dword [ecx], 0
99
; 8. Return (start of page).
100
        jmp     .return
101
.nomemory:
102
        dbgstr 'no memory for USB descriptor'
103
        xor     eax, eax
104
        jmp     .return
105
endp
106
 
107
; Allocator for fixed-size blocks: free a block.
108
proc usb_free_common
109
        push    ecx edx
110
virtual at esp
111
        rd      2       ; saved registers
112
        dd      ?       ; return address
113
.block  dd      ?
114
end virtual
115
; Insert the given block to the head of free blocks in this page.
116
        mov     ecx, [.block]
117
        mov     edx, ecx
118
        or      edx, 0xFFF
119
@@:
120
        mov     eax, [edx+1-8]
121
        mov     [ecx], eax
122
        lock cmpxchg [edx+1-8], ecx
123
        jnz     @b
124
        pop     edx ecx
125
        ret     4
126
endp
127
 
128
; Helper procedure: translate physical address in ecx
129
; of some transfer descriptor to linear address.
130
; in: eax = address of first page
131
proc usb_td_to_virt
132
; Traverse all pages used for transfer descriptors, looking for the one
133
; with physical address as in ecx.
134
@@:
135
        test    eax, eax
136
        jz      .zero
137
        push    eax
138
        call    get_pg_addr
139
        sub     eax, ecx
140
        jz      .found
141
        cmp     eax, -0x1000
142
        ja      .found
143
        pop     eax
144
        mov     eax, [eax+0x1000-4]
145
        jmp     @b
146
.found:
147
; When found, combine page address from eax with page offset from ecx.
148
        pop     eax
149
        and     ecx, 0xFFF
150
        add     eax, ecx
151
.zero:
152
        ret
153
endp