Rev 1063 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
2 | pj | 1 | /* Project: HARTIK 3.0 Sound Library */ |
2 | /* Description: Hard Real TIme Kernel for 8086 compatible */ |
||
3 | /* Author: Luca Abeni */ |
||
4 | /* Date: 5/12/1997 */ |
||
5 | |||
6 | /* File: DMA.C */ |
||
7 | /* Revision: 3.0 */ |
||
8 | |||
9 | /* |
||
1063 | tullio | 10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License as published by |
||
12 | * the Free Software Foundation; either version 2 of the License, or |
||
13 | * (at your option) any later version. |
||
14 | * |
||
15 | * This program is distributed in the hope that it will be useful, |
||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
18 | * GNU General Public License for more details. |
||
19 | * |
||
20 | * You should have received a copy of the GNU General Public License |
||
21 | * along with this program; if not, write to the Free Software |
||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||
23 | * |
||
24 | */ |
||
25 | |||
26 | /* |
||
2 | pj | 27 | DMAC functions and structures. This module was developed for using some |
28 | sound card's DMA operations, will become part of the HARTIK Kernel, for |
||
29 | providing support to all applications that needs DMA |
||
30 | */ |
||
31 | |||
32 | #include <kernel/kern.h> |
||
33 | #include <drivers/dma.h> |
||
34 | #include "sbio.h" |
||
35 | |||
36 | #define appl2linear(x) (x) |
||
37 | |||
38 | /* This does not work at 16 bits!! I'm sorry */ |
||
39 | /* Solution: Place them into a separate segment, perhaps it works... */ |
||
40 | BYTE buff2[0xFFFF]; |
||
41 | BYTE buff3[0xFFFF]; |
||
42 | |||
43 | void dma_stop(BYTE channel) |
||
44 | { |
||
45 | ll_out(0x0A, 0x04 | channel); |
||
46 | } |
||
47 | |||
48 | void dma16_stop(BYTE channel) |
||
49 | { |
||
50 | ll_out(0xD4, 0x04 | (channel - 4)); |
||
51 | } |
||
52 | |||
53 | void dma_reset(void) |
||
54 | { |
||
55 | ll_out(0x0C,0x00); |
||
56 | } |
||
57 | |||
58 | void dma16_reset(void) |
||
59 | { |
||
60 | ll_out(0xD8,0x00); |
||
61 | } |
||
62 | |||
63 | void dma_start(BYTE channel) |
||
64 | { |
||
65 | ll_out(0x0A, channel); |
||
66 | } |
||
67 | |||
68 | void dma16_start(BYTE channel) |
||
69 | { |
||
70 | ll_out(0xD4, channel- 4); |
||
71 | } |
||
72 | |||
73 | void dma_setmode(BYTE channel, BYTE mode) |
||
74 | { |
||
75 | ll_out(0x0B,mode | channel); |
||
76 | } |
||
77 | |||
78 | void dma16_setmode(BYTE channel, BYTE mode) |
||
79 | { |
||
80 | ll_out(0xD6,mode | (channel - 4)); |
||
81 | } |
||
82 | |||
83 | /* |
||
84 | Program the DMAC to transfert bytes to/from a buffer with logical |
||
85 | address addr and lenght len using the specified DMA channel |
||
86 | */ |
||
87 | void dma_setbuff(BYTE channel, BYTE *addr, WORD len) |
||
88 | { |
||
89 | DWORD ph_addr; |
||
90 | WORD offset_port, page_port, len_port; |
||
91 | |||
92 | switch (channel) { |
||
93 | case 0: offset_port = 0; |
||
94 | page_port = 0x87; |
||
95 | len_port = 1; |
||
96 | break; |
||
97 | case 1: offset_port = 0x02; |
||
98 | page_port = 0x83; |
||
99 | len_port = 0x03; |
||
100 | break; |
||
101 | case 3: offset_port = 0x06; |
||
102 | page_port = 0x82; |
||
103 | len_port = 0x07; |
||
104 | break; |
||
105 | default: cprintf("dma_setbuff channel error!!!\n"); |
||
927 | pj | 106 | exit(1); |
2 | pj | 107 | return; |
108 | } |
||
109 | ph_addr = appl2linear(addr); |
||
110 | ll_out(offset_port, (ph_addr & 0xFF)); |
||
111 | ll_out(offset_port, (ph_addr >> 8) & 0xFF); |
||
112 | ll_out(page_port, (ph_addr >> 16) & 0xFF); |
||
113 | ll_out(len_port,(BYTE)(len&0xFF)); |
||
114 | ll_out(len_port,(BYTE)((len>>8)&0xFF)); |
||
115 | } |
||
116 | |||
117 | /* |
||
118 | Program the DMAC to transfert words to/from a buffer with logical |
||
119 | address addr and lenght len using the specified DMA channel |
||
120 | */ |
||
121 | void dma16_setbuff(BYTE channel, BYTE *addr, WORD len) |
||
122 | { |
||
123 | DWORD ph_addr; |
||
124 | WORD offset_port, page_port, len_port; |
||
125 | |||
126 | switch (channel) { |
||
127 | case 5: offset_port = 0xC4; |
||
128 | page_port = 0x8B; |
||
129 | len_port = 0xC6; |
||
130 | break; |
||
131 | case 6: offset_port = 0xC8; |
||
132 | page_port = 0x89; |
||
133 | len_port = 0xCA; |
||
134 | break; |
||
135 | case 7: offset_port = 0xCC; |
||
136 | page_port = 0x8A; |
||
137 | len_port = 0xCE; |
||
138 | break; |
||
139 | /* It does not seem too much clean */ |
||
140 | default: cprintf("16 bit DMA?????\n"); |
||
927 | pj | 141 | exit(1); |
2 | pj | 142 | return; |
143 | } |
||
144 | ph_addr = appl2linear(addr); |
||
145 | ll_out(offset_port, (ph_addr >> 1) & 0xFF); |
||
146 | ll_out(offset_port, (ph_addr >> 9) & 0xFF); |
||
147 | ll_out(page_port, (ph_addr >> 16) & 0xFE); |
||
148 | ll_out(len_port,(BYTE)((len >> 1) & 0xFF)); |
||
149 | ll_out(len_port,(BYTE)((len >> 9) & 0xFF)); |
||
150 | } |
||
151 | |||
152 | /* |
||
153 | Program the 8 bit DMAC to transer bytes from the buffer specified by |
||
154 | dma_buff using double buffering |
||
155 | */ |
||
156 | void dma_out(BYTE channel, struct dma_buff *buff) |
||
157 | { |
||
158 | DWORD len, i; |
||
159 | |||
160 | buff->page = 0; |
||
161 | len = buff->dma_bufflen -1; |
||
162 | for(i = 0; i < buff->dma_bufflen; i++) { |
||
163 | buff->dma_buff[i] = buff->p[i]; |
||
164 | } |
||
165 | buff->count = buff->dma_bufflen; |
||
166 | |||
167 | dma_stop(channel); |
||
168 | dma_reset(); |
||
169 | dma_setmode(channel, 0x58); |
||
170 | dma_setbuff(channel, buff->dma_buff, len); |
||
171 | dma_start(channel); |
||
172 | } |
||
173 | |||
174 | /* |
||
175 | Program the 8 bit DMAC to transer bytes to the buffer specified by |
||
176 | dma_buff using double buffering |
||
177 | */ |
||
178 | void dma_in(BYTE channel, struct dma_buff *buff) |
||
179 | { |
||
180 | DWORD len; |
||
181 | |||
182 | buff->page = 0; |
||
183 | len = buff->dma_bufflen - 1; |
||
184 | buff->count = 0; |
||
185 | |||
186 | dma_stop(channel); |
||
187 | dma_reset(); |
||
188 | dma_setmode(channel, 0x54); |
||
189 | dma_setbuff(channel, buff->dma_buff, len); |
||
190 | dma_start(channel); |
||
191 | } |
||
192 | |||
193 | /* |
||
194 | Program the 8 bit DMAC to transer bytes from the buffer specified by |
||
195 | dma_buff using double buffering |
||
196 | */ |
||
197 | void dma16_out(BYTE channel, struct dma_buff *buff) |
||
198 | { |
||
199 | DWORD len, i; |
||
200 | |||
201 | buff->page = 0; |
||
202 | len = buff->dma_bufflen - 1; |
||
203 | for(i = 0; i < buff->dma_bufflen; i++) { |
||
204 | buff->dma_buff[i] = buff->p[i]; |
||
205 | } |
||
206 | buff->count = buff->dma_bufflen; |
||
207 | |||
208 | dma16_stop(channel); |
||
209 | dma16_reset(); |
||
210 | dma16_setmode(channel, 0x58); |
||
211 | dma16_setbuff(channel, buff->dma_buff, len); |
||
212 | dma16_start(channel); |
||
213 | } |
||
214 | |||
215 | /* |
||
216 | Program the 8 bit DMAC to transer bytes to the buffer specified by |
||
217 | dma_buff using double buffering |
||
218 | */ |
||
219 | void dma16_in(BYTE channel, struct dma_buff *buff) |
||
220 | { |
||
221 | DWORD len; |
||
222 | |||
223 | buff->page = 0; |
||
224 | len = buff->dma_bufflen -1; |
||
225 | buff->count = 0; |
||
226 | |||
227 | dma16_stop(channel); |
||
228 | dma16_reset(); |
||
229 | dma16_setmode(channel, 0x54); |
||
230 | dma16_setbuff(channel, buff->dma_buff, len); |
||
231 | dma16_start(channel); |
||
232 | } |
||
233 | |||
234 | /* |
||
235 | The DMAC can use only buffers that don't cross a 64K boundary (the |
||
236 | value (0xFFFF0000 & address) must be the same for every address in the |
||
237 | buffer). We call this kind of buffers "aligned buffers": it can be a |
||
238 | problem to allocate an aligned buffer, so we provide the dma_getalignbuff |
||
239 | function |
||
240 | */ |
||
241 | |||
242 | /* Allocate an aligned buffer for DMA transfer */ |
||
243 | void dma_getalignbuff(struct dma_buff *buff, WORD len) |
||
244 | { |
||
245 | // BYTE *p; |
||
246 | // DWORD phys; |
||
247 | // BYTE done = 0; |
||
248 | |||
249 | if (len > 0x8000) { |
||
250 | cprintf("Don' t allocate too big buffers!!!!!\n"); |
||
251 | /* exc_raise(TOO_BIG_BUFFER);*/ |
||
252 | } |
||
253 | buff->dma_bufflen = len; |
||
254 | |||
255 | // while (!done) |
||
256 | // { |
||
257 | /* get a buffer */ |
||
258 | // p = VM_alloc(len); |
||
259 | /* compute its phisical address */ |
||
260 | // phys = appl2linear(p); |
||
261 | /* Is it aligned? */ |
||
262 | // if ((phys & 0x0F0000) != ((phys + len) & 0x0F0000)) |
||
263 | /* If no, try again */ |
||
264 | // done = 0; |
||
265 | // else done = 1; |
||
266 | // } |
||
267 | // buff->dma_buff = p; |
||
268 | |||
269 | /* NB this function returns a page aligned on a 64k boundary |
||
270 | ... this is not what it have to be, but it works */ |
||
271 | buff->dma_buff = kern_alloc_aligned(len, MEMORY_UNDER_16M, 16, 0); |
||
272 | } |
||
273 | |||
274 | /* |
||
275 | Allocate a buffer starting from an address with the rightmost 16 bits equal |
||
276 | to 0 (it's the simpler way to obtain an aligned buffer |
||
277 | */ |
||
278 | BYTE *dma_getpage(DWORD dim) |
||
279 | { |
||
280 | /* Get a buffer of dimension dim+64K...*/ |
||
281 | return kern_alloc_aligned(dim, MEMORY_UNDER_16M, 16, 0); |
||
282 | } |
||
283 | |||
284 | /* |
||
285 | Copy a part of the user buffer in half DMA buffer (used for |
||
286 | double buffering) |
||
287 | */ |
||
288 | int outfun(struct dma_buff *b) |
||
289 | { |
||
290 | int i; |
||
291 | int result = 0; |
||
292 | |||
293 | /* Is this the last cycle of the DMA output operation?*/ |
||
294 | if (b->len > (b->dma_bufflen >> 1) + b->count) { |
||
295 | /*No */ |
||
296 | for(i = 0; i < (b->dma_bufflen >> 1); i++) |
||
297 | b->dma_buff[i+ ((b->dma_bufflen>>1) * b->page)] = b->p[b->count + i]; |
||
298 | } else { |
||
299 | /* Yes */ |
||
300 | for(i = 0; i < (b->len - b->count); i++) |
||
301 | b->dma_buff[i + ((b->dma_bufflen>>1) * b->page)] = b->p[b->count + i]; |
||
302 | /* return 1 to comunicate that the operation is finished */ |
||
303 | result = 1; |
||
304 | } |
||
305 | b->count += (b->dma_bufflen >> 1); |
||
306 | b->page = !b->page; |
||
307 | return result; |
||
308 | } |
||
309 | |||
310 | /* Copy half DMA buffer in the user buffer (used for double buffering) */ |
||
311 | int infun(struct dma_buff *b) |
||
312 | { |
||
313 | int i; |
||
314 | int result = 0; |
||
315 | |||
316 | /* Is this the last cycle of the DMA outpu operation? */ |
||
317 | if (b->len > (b->dma_bufflen >> 1) + b->count) { |
||
318 | for(i = 0; i < (b->dma_bufflen >> 1); i++) |
||
319 | b->p[b->count+ i] = b->dma_buff[i + ((b->dma_bufflen>>1) * b->page)]; |
||
320 | } else { |
||
321 | for(i = 0; i < (b->len - b->count); i++) |
||
322 | b->p[b->count+ i] = b->dma_buff[i+ ((b->dma_bufflen>>1) * b->page)]; |
||
323 | /* return 2 to comunicate that the operation is finished */ |
||
324 | result = 2; |
||
325 | } |
||
326 | b->count += (b->dma_bufflen >> 1); |
||
327 | b->page = !b->page; |
||
328 | return result; |
||
329 | } |