ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / sound / oss / emu10k1 / emuadxmg.c
1
2 /*     
3  **********************************************************************
4  *     emuadxmg.c - Address space manager for emu10k1 driver 
5  *     Copyright 1999, 2000 Creative Labs, Inc. 
6  * 
7  ********************************************************************** 
8  * 
9  *     Date                 Author          Summary of changes 
10  *     ----                 ------          ------------------ 
11  *     October 20, 1999     Bertrand Lee    base code release 
12  * 
13  ********************************************************************** 
14  * 
15  *     This program is free software; you can redistribute it and/or 
16  *     modify it under the terms of the GNU General Public License as 
17  *     published by the Free Software Foundation; either version 2 of 
18  *     the License, or (at your option) any later version. 
19  * 
20  *     This program is distributed in the hope that it will be useful, 
21  *     but WITHOUT ANY WARRANTY; without even the implied warranty of 
22  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
23  *     GNU General Public License for more details. 
24  * 
25  *     You should have received a copy of the GNU General Public 
26  *     License along with this program; if not, write to the Free 
27  *     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 
28  *     USA. 
29  * 
30  ********************************************************************** 
31  */
32
33 #include "hwaccess.h"
34
35 /* Allocates emu address space */
36
37 int emu10k1_addxmgr_alloc(u32 size, struct emu10k1_card *card)
38 {
39         u16 *pagetable = card->emupagetable;
40         u16 index = 0;
41         u16 numpages;
42         unsigned long flags;
43
44         /* Convert bytes to pages */
45         numpages = (size / EMUPAGESIZE) + ((size % EMUPAGESIZE) ? 1 : 0);
46
47         spin_lock_irqsave(&card->lock, flags);
48
49         while (index < (MAXPAGES - 1)) {
50                 if (pagetable[index] & 0x8000) {
51                         /* This block of pages is in use, jump to the start of the next block. */
52                         index += (pagetable[index] & 0x7fff);
53                 } else {
54                         /* Found free block */
55                         if (pagetable[index] >= numpages) {
56
57                                 /* Block is large enough */
58
59                                 /* If free block is larger than the block requested
60                                  * then adjust the size of the block remaining */
61                                 if (pagetable[index] > numpages)
62                                         pagetable[index + numpages] = pagetable[index] - numpages;
63
64                                 pagetable[index] = (numpages | 0x8000); /* Mark block as used */
65
66                                 spin_unlock_irqrestore(&card->lock, flags);
67
68                                 return index;
69                         } else {
70                                 /* Block too small, jump to the start of the next block */
71                                 index += pagetable[index];
72                         }
73                 }
74         }
75
76         spin_unlock_irqrestore(&card->lock, flags);
77
78         return -1;
79 }
80
81 /* Frees a previously allocated emu address space. */
82
83 void emu10k1_addxmgr_free(struct emu10k1_card *card, int index)
84 {
85         u16 *pagetable = card->emupagetable;
86         u16 origsize = 0;
87         unsigned long flags;
88
89         spin_lock_irqsave(&card->lock, flags);
90
91         if (pagetable[index] & 0x8000) {
92                 /* Block is allocated - mark block as free */
93                 origsize = pagetable[index] & 0x7fff;
94                 pagetable[index] = origsize;
95
96                 /* If next block is free, we concat both blocks */
97                 if (!(pagetable[index + origsize] & 0x8000))
98                         pagetable[index] += pagetable[index + origsize] & 0x7fff;
99         }
100
101         spin_unlock_irqrestore(&card->lock, flags);
102
103         return;
104 }