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