Subversion Repositories Kolibri OS

Rev

Rev 3653 | 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
; Allocator for fixed-size blocks: allocate a block.
16
; [ebx-4] = pointer to the first page, ebx = pointer to MUTEX structure.
17
proc usb_allocate_common
18
        push    edi     ; save used register to be stdcall
19
virtual at esp
20
        dd      ?       ; saved edi
21
        dd      ?       ; return address
22
.size   dd      ?
23
end virtual
24
; 1. Take the lock.
25
        mov     ecx, ebx
26
        call    mutex_lock
27
; 2. Find the first allocated page with a free block, if any.
28
; 2a. Initialize for the loop.
29
        mov     edx, ebx
30
.pageloop:
31
; 2b. Get the next page, keeping the current in eax.
32
        mov     eax, edx
33
        mov     edx, [edx-4]
34
; 2c. If there is no next page, we're out of luck; go to 4.
35
        test    edx, edx
36
        jz      .newpage
37
        add     edx, 0x1000
38
@@:
39
; 2d. Get the pointer to the first free block on this page.
40
; If there is no free block, continue to 2b.
41
        mov     eax, [edx-8]
42
        test    eax, eax
43
        jz      .pageloop
44
; 2e. Get the pointer to the next free block.
45
        mov     ecx, [eax]
46
; 2f. Update the pointer to the first free block from eax to ecx.
47
; Normally [edx-8] still contains eax, if so, atomically set it to ecx
48
; and proceed to 3.
49
; However, the price of simplicity of usb_free_common (in particular, it
50
; doesn't take the lock) is that [edx-8] could (rarely) be changed while
51
; we processed steps 2d+2e. If so, return to 2d and retry.
52
        lock cmpxchg [edx-8], ecx
53
        jnz     @b
54
.return:
55
; 3. Release the lock taken in step 1 and return.
56
        push    eax
57
        mov     ecx, ebx
58
        call    mutex_unlock
59
        pop     eax
60
        pop     edi     ; restore used register to be stdcall
61
        ret     4
62
.newpage:
63
; 4. Allocate a new page.
64
        push    eax
65
        stdcall kernel_alloc, 0x1000
66
        pop     edx
67
; If failed, say something to the debug board and return zero.
68
        test    eax, eax
69
        jz      .nomemory
70
; 5. Add the new page to the tail of list of allocated pages.
71
        mov     [edx-4], eax
72
; 6. Initialize two service dwords in the end of page:
73
; first free block is (start of page) + (block size)
74
; (we will return first block at (start of page), so consider it allocated),
75
; no next page.
76
        mov     edx, eax
77
        lea     edi, [eax+0x1000-8]
78
        add     edx, [.size]
79
        mov     [edi], edx
80
        and     dword [edi+4], 0
81
; 7. All blocks starting from edx are free; join them in a single-linked list.
82
@@:
83
        mov     ecx, edx
84
        add     edx, [.size]
85
        mov     [ecx], edx
86
        cmp     edx, edi
87
        jbe     @b
88
        sub     ecx, [.size]
89
        and     dword [ecx], 0
90
; 8. Return (start of page).
91
        jmp     .return
92
.nomemory:
93
        dbgstr 'no memory for USB descriptor'
94
        xor     eax, eax
95
        jmp     .return
96
endp
97
 
98
; Allocator for fixed-size blocks: free a block.
99
proc usb_free_common
100
        push    ecx edx
101
virtual at esp
102
        rd      2       ; saved registers
103
        dd      ?       ; return address
104
.block  dd      ?
105
end virtual
106
; Insert the given block to the head of free blocks in this page.
107
        mov     ecx, [.block]
108
        mov     edx, ecx
109
        or      edx, 0xFFF
110
@@:
111
        mov     eax, [edx+1-8]
112
        mov     [ecx], eax
113
        lock cmpxchg [edx+1-8], ecx
114
        jnz     @b
115
        pop     edx ecx
116
        ret     4
117
endp
118
 
4418 clevermous 119
; Helper procedure: translate physical address in ecx
3520 clevermous 120
; of some transfer descriptor to linear address.
4418 clevermous 121
; in: eax = address of first page
3520 clevermous 122
proc usb_td_to_virt
123
; Traverse all pages used for transfer descriptors, looking for the one
124
; with physical address as in ecx.
125
@@:
126
        test    eax, eax
127
        jz      .zero
128
        push    eax
129
        call    get_pg_addr
130
        sub     eax, ecx
131
        jz      .found
132
        cmp     eax, -0x1000
133
        ja      .found
134
        pop     eax
135
        mov     eax, [eax+0x1000-4]
136
        jmp     @b
137
.found:
138
; When found, combine page address from eax with page offset from ecx.
139
        pop     eax
140
        and     ecx, 0xFFF
141
        add     eax, ecx
142
.zero:
143
        ret
144
endp