1 /*
2     Implementation of DMA routines on DOS
3     Copyright (C) 1999 by Andrew Zabolotny, <bit@eltech.ru>
4 
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Library General Public
7     License as published by the Free Software Foundation; either
8     version 2 of the License, or (at your option) any later version.
9 
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Library General Public License for more details.
14 
15     You should have received a copy of the GNU Library General Public
16     License along with this library; if not, write to the Free
17     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 
20 #include "dosdma.h"
21 
22 #include <go32.h> /* includes sys/version.h (djgpp >= 2.02) */
23 #include <dos.h>
24 #include <dpmi.h>
25 #include <sys/nearptr.h>
26 #include <malloc.h>
27 #include "mikmod.h" /* for MikMod_malloc() & co */
28 
29 /* BUG WARNING:  there is an error in DJGPP libraries <= 2.01:
30  * src/libc/dpmi/api/d0102.s loads the selector and allocsize
31  * arguments in the wrong order.  DJGPP >= 2.02 have it fixed. */
32 #if (!defined(__DJGPP_MINOR__) || (__DJGPP_MINOR__+0) < 2)
33 #warning __dpmi_resize_dos_memory() from DJGPP <= 2.01 is broken!
34 #endif
35 
36 __dma_regs dma[8] = {
37 /* *INDENT-OFF* */
38 	{DMA_ADDR_0, DMA_PAGE_0, DMA_SIZE_0,
39 	 DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG},
40 	{DMA_ADDR_1, DMA_PAGE_1, DMA_SIZE_1,
41 	 DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG},
42 
43 	{DMA_ADDR_2, DMA_PAGE_2, DMA_SIZE_2,
44 	 DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG},
45 	{DMA_ADDR_3, DMA_PAGE_3, DMA_SIZE_3,
46 	 DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG},
47 
48 	{DMA_ADDR_4,          0, DMA_SIZE_4,
49 	 DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG},
50 	{DMA_ADDR_5, DMA_PAGE_5, DMA_SIZE_5,
51 	 DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG},
52 
53 	{DMA_ADDR_6, DMA_PAGE_6, DMA_SIZE_6,
54 	 DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG},
55 	{DMA_ADDR_7, DMA_PAGE_7, DMA_SIZE_7,
56 	 DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG}
57 /* *INDENT-ON* */
58 };
59 
60 static int __initialized = 0;
61 static int __buffer_count = 0;
62 static __dpmi_meminfo __locked_data;
63 
dma_initialize()64 int dma_initialize()
65 {
66 	if (!__djgpp_nearptr_enable())
67 		return 0;
68 
69 	/* Trick: Avoid re-setting DS selector limit on each memory allocation
70 	   call */
71 	__djgpp_selector_limit = 0xffffffff;
72 
73 	__locked_data.address = __djgpp_base_address + (unsigned long)&dma;
74 	__locked_data.size = sizeof(dma);
75 	if (__dpmi_lock_linear_region(&__locked_data))
76 		return 0;
77 
78 	return (__initialized = 1);
79 }
80 
dma_finalize()81 void dma_finalize()
82 {
83 	if (!__initialized)
84 		return;
85 	__dpmi_unlock_linear_region(&__locked_data);
86 	__djgpp_nearptr_disable();
87 }
88 
dma_allocate(unsigned int channel,unsigned int size)89 dma_buffer *dma_allocate(unsigned int channel, unsigned int size)
90 {
91 	int parsize = (size + 15) >> 4;	/* size in paragraphs */
92 	int par = 0;				/* Real-mode paragraph */
93 	int selector = 0;			/* Protected-mode selector */
94 	int mask = channel <= 3 ? 0xfff : 0x1fff;	/* Alignment mask in para. */
95 	int allocsize = parsize;	/* Allocated size in paragraphs */
96 	int count;					/* Try count */
97 	int bound = 0;				/* Nearest bound address */
98 	int maxsize;				/* Maximal possible block size */
99 	dma_buffer *buffer = NULL;
100 	__dpmi_meminfo buff_info, struct_info;
101 
102 	if (!dma_initialize())
103 		return NULL;
104 
105 	/* Loop until we'll get a properly aligned memory block */
106 	for (count = 8; count; count--) {
107 		int resize = (selector != 0);
108 
109 		/* Try first to resize (possibly previously) allocated block */
110 		if (resize) {
111 			maxsize = (bound + parsize) - par;
112 			if (maxsize > parsize * 2)
113 				maxsize = parsize * 2;
114 			if (maxsize == allocsize)
115 				resize = 0;
116 			else {
117 				allocsize = maxsize;
118 				if (__dpmi_resize_dos_memory(selector, allocsize, &maxsize) !=
119 					0) resize = 0;
120 			}
121 		}
122 
123 		if (!resize) {
124 			if (selector)
125 				__dpmi_free_dos_memory(selector), selector = 0;
126 			par = __dpmi_allocate_dos_memory(allocsize, &selector);
127 		}
128 
129 		if ((par == 0) || (par == -1))
130 			goto exit;
131 
132 		/* If memory block contains a properly aligned portion, quit loop */
133 		bound = (par + mask + 1) & ~mask;
134 		if (par + parsize <= bound)
135 			break;
136 		if (bound + parsize <= par + allocsize) {
137 			par = bound;
138 			break;
139 		}
140 	}
141 	if (!count) {
142 		__dpmi_free_dos_memory(selector);
143 		goto exit;
144 	}
145 
146 	buffer = (dma_buffer *) MikMod_malloc(sizeof(dma_buffer));
147 	buffer->linear = (unsigned char *)(__djgpp_conventional_base + bound * 16);
148 	buffer->physical = bound * 16;
149 	buffer->size = parsize * 16;
150 	buffer->selector = selector;
151 	buffer->channel = channel;
152 
153 	buff_info.address = buffer->physical;
154 	buff_info.size = buffer->size;
155 	/*
156 	   Don't pay attention to return code since under plain DOS it often
157 	   returns error (at least under HIMEM/CWSDPMI and EMM386/DPMI)
158 	 */
159 	__dpmi_lock_linear_region(&buff_info);
160 
161 	/* Lock the DMA buffer control structure as well */
162 	struct_info.address = __djgpp_base_address + (unsigned long)buffer;
163 	struct_info.size = sizeof(dma_buffer);
164 	if (__dpmi_lock_linear_region(&struct_info)) {
165 		__dpmi_unlock_linear_region(&buff_info);
166 		__dpmi_free_dos_memory(selector);
167 		MikMod_free(buffer);
168 		buffer = NULL;
169 		goto exit;
170 	}
171 
172   exit:
173 	if (buffer)
174 		__buffer_count++;
175 	else if (--__buffer_count == 0)
176 		dma_finalize();
177 	return buffer;
178 }
179 
dma_free(dma_buffer * buffer)180 void dma_free(dma_buffer * buffer)
181 {
182 	__dpmi_meminfo buff_info;
183 
184 	if (!buffer)
185 		return;
186 
187 	buff_info.address = buffer->physical;
188 	buff_info.size = buffer->size;
189 	__dpmi_unlock_linear_region(&buff_info);
190 
191 	__dpmi_free_dos_memory(buffer->selector);
192 	MikMod_free(buffer);
193 
194 	if (--__buffer_count == 0)
195 		dma_finalize();
196 }
197 
dma_start(dma_buffer * buffer,unsigned long count,unsigned char mode)198 void dma_start(dma_buffer * buffer, unsigned long count, unsigned char mode)
199 {
200 	/* Disable interrupts */
201 	int old_ints = disable();
202 	dma_disable(buffer->channel);
203 	dma_set_mode(buffer->channel, mode);
204 	dma_clear_ff(buffer->channel);
205 	dma_set_addr(buffer->channel, buffer->physical);
206 	dma_clear_ff(buffer->channel);
207 	dma_set_count(buffer->channel, count);
208 	dma_enable(buffer->channel);
209 	/* Re-enable interrupts */
210 	if (old_ints)
211 		enable();
212 }
213 
214 /* ex:set ts=4: */
215