dma.c
上传用户:hepax88
上传日期:2007-01-03
资源大小:1101k
文件大小:10k
源码类别:

TCP/IP协议栈

开发平台:

Visual C++

  1. /* PC DMA support functions. Copyright 1992 Phil Karn
  2.  *
  3.  * The functions dma_map() and dma_unmap()
  4.  * support the virtual->physical address translations required for
  5.  * PC DMA, either in a real mode machine or on a 386/486 running
  6.  * a memory manager with VDS (Virtual DMA Services) like Quarterdeck's
  7.  * QEMM386.
  8.  *
  9.  * Only the "lock" and "unlock" VDS calls are used, and only to
  10.  * check the suitability of the user's buffer for DMA. If it is not
  11.  * suitable, the automatic DMA buffer allocation/copy mechanism in VDS,
  12.  * as nice as it is, is *not* used since QEMM seems to have only one such
  13.  * buffer. It would be begging for deadlock were we to use it heavily in
  14.  * multitasking, especially for HDLC receive operations that always have
  15.  * to have a buffer allocated.
  16.  *
  17.  * So we implement our own dynamic DMA buffering, using conventional
  18.  * memory on the heap that is checked for DMA "correctness", and
  19.  * without limiting the number of simultaneous DMA operations.
  20.  */
  21. #include <stdio.h>
  22. #include <dos.h>
  23. #include "global.h"
  24. #include "dma.h"
  25. #include "nospc.h"
  26. /* I/O port addresses for DMA page registers on the PC/AT */
  27. static uint16 Page_regs[] = {
  28. 0x87,0x83,0x81,0x82,0x8f,0x8b,0x89,0x8a
  29. };
  30. /* Address to check to see if QEMM or other VDS manager is running.
  31.  * God, I hate peeking at absolute memory, but Microsoft says I have to...
  32.  */
  33. char *Vds_run = MK_FP(0x40,0x7b);
  34. /* Structure used to communicate with Virtual DMA Service (VDS) in
  35.  * QEMM, etc
  36.  */
  37. struct dds {
  38. unsigned long rsize;
  39. unsigned long offset;
  40. unsigned short seg;
  41. unsigned short buf_id;
  42. unsigned long physaddr;
  43. };
  44. /* Descriptor used to keep track of the auxiliary buffer when the user's
  45.  * own buffer isn't suitable for hardware DMA
  46.  */
  47. struct dma {
  48. struct dma *next; /* Linked list pointers */
  49. struct dma *prev;
  50. void *p; /* virtual address of user's original buffer */
  51. unsigned short len;
  52. void *aux; /* Virtual address of aux buffer, if any */
  53. unsigned long physaddr; /* Physical address of actual buffer */
  54. };
  55. #define NULLDMA (struct dma *)0
  56. struct dma *Dma; /* List of active DMA descriptors */
  57. /* Allocate a block of memory suitable for DMA */
  58. void *
  59. dma_malloc(physaddr,len)
  60. int32 *physaddr; /* Return physical address thru here */
  61. unsigned short len;
  62. {
  63. void *bufs[20],*aux;
  64. int i,tries;
  65. for(tries=0;tries<20;tries++){
  66. if((aux = bufs[tries] = malloc(len)) == NULL)
  67. break;
  68. if((*physaddr = dmalock(aux,len)) != 0)
  69. break; /* Good buffer */
  70. if((*physaddr >> 24) == 0){
  71. /* Good buffer outside bus-accessible memory
  72.  * (can this happen?)
  73.  */
  74. dmaunlock(*physaddr,len);
  75. *physaddr = 0;
  76. }
  77. }
  78. /* At this point, we've either got a good buffer (physaddr != 0) or
  79.  * we gave up or failed (physaddr == 0). Free all unsuitable buffers.
  80.  */
  81. for(i=0;i<tries;i++)
  82. free(bufs[i]);
  83. if(*physaddr == 0){
  84. /* Failure, clean up */
  85. free(aux);
  86. return NULL;
  87. }
  88. /* Success */
  89. return aux;
  90. }
  91. /* Convert user's virtual address to a physical address suitable for DMA */
  92. unsigned long
  93. dma_map(p,len,copy)
  94. void *p; /* User's virtual address */
  95. unsigned short len; /* Length of user's buffer */
  96. int copy; /* !0 => Copy user buffer to aux buffer if allocated */
  97. {
  98. void *bufs[20],*aux;
  99. int i,tries;
  100. unsigned long physaddr;
  101. struct dma *dmap;
  102. /* Create descriptor */
  103. dmap = calloc(1,sizeof(struct dma));
  104. dmap->p = p;
  105. dmap->len = len;
  106. dmap->next = Dma;
  107. if(Dma->next != NULL)
  108. Dma->next->prev = dmap;
  109. Dma = dmap;
  110. if((physaddr = dmalock(p,len)) != 0){
  111. dmap->physaddr = physaddr;
  112. return physaddr; /* User's buffer is OK for DMA */
  113. }
  114. /* Try to malloc a suitable buffer. Hold onto the unsuitable
  115.  * ones to make sure malloc doesn't give them right back to us.
  116.  */
  117. for(tries=0;tries<20;tries++){
  118. if((aux = bufs[tries] = malloc(len)) == NULL)
  119. break;
  120. if((physaddr = dmalock(aux,len)) != 0)
  121. break; /* Good buffer */
  122. }
  123. /* At this point, we've either got a good buffer (physaddr != 0) or
  124.  * we gave up or failed (physaddr == 0). Free all unsuitable buffers.
  125.  */
  126. for(i=0;i<tries;i++)
  127. free(bufs[i]);
  128. if(physaddr == 0){
  129. /* Failure, clean up */
  130. free(aux);
  131. if(dmap->next != NULL)
  132. dmap->next->prev = dmap->prev;
  133. if(dmap->prev != NULL)
  134. dmap->prev->next = dmap->next;
  135. else
  136. Dma = dmap->next;
  137. free(dmap);
  138. return 0;
  139. }
  140. /* Success */
  141. if(copy)
  142. memcpy(aux,p,len);
  143. dmap->aux = aux;
  144. dmap->physaddr = physaddr;
  145. return physaddr;
  146. }
  147. /* When done with DMA, the user calls this function, again with his
  148.  * buffer's virtual address. We free any auxiliary buffers, after copying them
  149.  * out (if requested) and then if QEMM is running, call the unlock function.
  150.  */
  151. void
  152. dma_unmap(p,copy)
  153. void *p;
  154. int copy; /* !0 => Copy aux buffer to user buffer, if mapped */
  155. {
  156. struct dma *dmap;
  157. for(dmap = Dma;dmap != NULLDMA;dmap = dmap->next){
  158. if(dmap->p == p)
  159. break;
  160. }
  161. if(dmap == NULLDMA)
  162. return; /* Unknown */
  163. if((*Vds_run & 0x20) != 0)
  164. dmaunlock(dmap->physaddr,dmap->len); /* VDS call */
  165. /* Copy aux buffer to user's buffer, if requested, and free */
  166. if(dmap->aux != NULL){
  167. if(copy)
  168. memcpy(dmap->p,dmap->aux,dmap->len);
  169. free(dmap->aux);
  170. }
  171. /* Free DMA descriptor */
  172. if(dmap->next != NULLDMA)
  173. dmap->next->prev = dmap->prev;
  174. if(dmap->prev != NULLDMA)
  175. dmap->prev->next = dmap->next;
  176. else
  177. Dma = dmap->next;
  178. free(dmap);
  179. }
  180. /* Translate a virtual address and length into a physical address for DMA.
  181.  * If QEMM or another VDS service is available, use it. Otherwise
  182.  * assume real mode and do the translation ourselves.
  183.  *
  184.  * If the physical buffer crosses a physical 64K memory
  185.  * boundary, or if it does not sit in contiguous 4K 386/486 pages, then
  186.  * return an error.
  187.  *
  188.  * Since we set the flag that says "don't remap", we don't need to keep
  189.  * the DDS structure as modified by the lock call. The only information
  190.  * needed to unlock is the address and length, which the user will supply.
  191.  */
  192. unsigned long
  193. dmalock(p,len)
  194. void *p;
  195. unsigned short len;
  196. {
  197. struct dds ddsp;
  198. union REGS regs;
  199. struct SREGS segregs;
  200. unsigned long physaddr;
  201. if((*Vds_run & 0x20) == 0){
  202. /* VDS not available, assume real mode. Convert
  203.  * to flat address and check for 64K alignment
  204.  */
  205. physaddr = ((unsigned long)FP_SEG(p) << 4) + FP_OFF(p);
  206. if((((physaddr + len) ^ physaddr) & ~0xffff) != 0)
  207. physaddr = 0; /* NFG, crosses 64K segment */
  208. return physaddr;
  209. }
  210. /* VDS available, use it */
  211. ddsp.rsize = len;
  212. ddsp.offset = FP_OFF(p);
  213. ddsp.seg = FP_SEG(p);
  214. regs.x.ax = 0x8103;
  215. /* Region must not cross 64K, be contiguous in physical memory,
  216.  * and do *not* allocate DMA buffer if it fails
  217.  */
  218. regs.x.dx = 20;
  219. segregs.es = FP_SEG(&ddsp);
  220. regs.x.di = FP_OFF(&ddsp);
  221. int86x(0x4b,&regs,&regs,&segregs);
  222. if(regs.x.cflag)
  223. return 0;
  224. return ddsp.physaddr;
  225. }
  226. /* Release memory that has been locked for DMA */
  227. unsigned long
  228. dmaunlock(physaddr,len)
  229. unsigned long physaddr;
  230. unsigned short len;
  231. {
  232. union REGS regs;
  233. struct SREGS segregs;
  234. struct dds dds;
  235. if((*Vds_run & 0x20) == 0)
  236. return 0; /* Nothing required */
  237. dds.rsize = len;
  238. dds.physaddr = physaddr;
  239. dds.buf_id = 0;
  240. dds.seg = 0; /* We assume these are don't cares? */
  241. dds.offset = 0;
  242. regs.x.ax = 0x8104;
  243. regs.x.dx = 0;
  244. segregs.es = FP_SEG(&dds);
  245. regs.x.di = FP_OFF(&dds);
  246. int86x(0x4b,&regs,&regs,&segregs);
  247. if(regs.x.cflag)
  248. return -1;
  249. return 0;
  250. }
  251. /* Disable QEMM DMA translation */
  252. int
  253. dis_dmaxl(chan)
  254. int chan; /* DMA channel number */
  255. {
  256. union REGS regs;
  257. struct SREGS segregs;
  258. if((*Vds_run & 0x20) == 0)
  259. return 0; /* QEMM not running */
  260. regs.x.ax = 0x810b;
  261. regs.x.bx = chan;
  262. regs.x.dx = 0;
  263. int86x(0x4b,&regs,&regs,&segregs);
  264. if(regs.x.cflag)
  265. return -1;
  266. return 0;
  267. }
  268. /* Re-enable QEMM DMA translation */
  269. int
  270. ena_dmaxl(chan)
  271. int chan;
  272. {
  273. union REGS regs;
  274. struct SREGS segregs;
  275. if((*Vds_run & 0x20) == 0)
  276. return 0; /* QEMM not running */
  277. regs.x.ax = 0x810c;
  278. regs.x.bx = chan;
  279. regs.x.dx = 0;
  280. int86x(0x4b,&regs,&regs,&segregs);
  281. if(regs.x.cflag)
  282. return -1;
  283. return 0;
  284. }
  285. /* Set up a 8237 DMA controller channel to point to a specified buffer */
  286. int
  287. setup_dma(chan,physaddr,length,mode)
  288. int chan;
  289. int32 physaddr;
  290. uint16 length;
  291. int mode; /* Read/write, etc */
  292. {
  293. int dmaport;
  294. int i_state;
  295. if(length == 0 || chan < 0 || chan > 7 || chan == 4)
  296. return -1;
  297. i_state = dirps();
  298. dma_disable(chan);
  299. outportb(Page_regs[chan],physaddr >> 16); /* Store in 64K DMA page */
  300. if(chan < 4){
  301. /* 8-bit DMA */
  302. length--;
  303. outportb(DMA1BASE+DMA_MODE,mode|chan); /* Select mode */
  304. outportb(DMA1BASE+DMA_RESETFF,0);  /* reset byte pointer flipflop */
  305. /* Output buffer start (dest) address */
  306. dmaport = DMA1BASE + 2*chan;
  307. outportb(dmaport,(uint8)physaddr);
  308. outportb(dmaport,(uint8)(physaddr >> 8));
  309. /* output DMA maximum byte count */
  310. dmaport++;
  311. outportb(dmaport,(uint8)length);
  312. outportb(dmaport,(uint8)(length >> 8));
  313. } else {
  314. /* 16-bit DMA */
  315. length >>= 1; /* count is 16-bit words */
  316. length--;
  317. physaddr >>= 1;
  318. outportb(DMA2BASE+2*DMA_MODE,mode|(chan & 3));/* Select mode */
  319. outportb(DMA2BASE+2*DMA_RESETFF,0);  /* reset byte pointer flipflop */
  320. /* Output buffer start (dest) address */
  321. dmaport = DMA2BASE + 4*(chan & 3);
  322. outportb(dmaport,(uint8)physaddr);
  323. outportb(dmaport,(uint8)(physaddr >> 8));
  324. /* output DMA maximum byte count */
  325. dmaport += 2;
  326. outportb(dmaport,(uint8)length);
  327. outportb(dmaport,(uint8)(length >> 8));
  328. }
  329. /* Unmask channel (start DMA) */
  330. dma_enable(chan);
  331. restore(i_state);
  332. return 0;
  333. }
  334. /* Return current count on specified DMA channel */
  335. uint16
  336. dma_cnt(chan)
  337. int chan;
  338. {
  339. int dmaport;
  340. uint16 bytecount;
  341. if(chan < 4){
  342. outportb(DMA1BASE+DMA_RESETFF,0); /* reset firstlast ff */
  343. dmaport = DMA1BASE + 2*chan + 1;
  344. } else {
  345. outportb(DMA2BASE+2*DMA_RESETFF,0);
  346. dmaport = DMA2BASE + 4*(chan&3) + 2;
  347. }
  348. bytecount = inportb(dmaport);
  349. bytecount += inportb(dmaport) << 8;
  350. return bytecount;
  351. }
  352. /* Disable DMA on specified channel, return previous status */
  353. int
  354. dma_disable(chan)
  355. int chan;
  356. {
  357. if(chan < 4){
  358. outportb(DMA1BASE+DMA_MASK, DMA_DISABLE|chan);
  359. } else {
  360. outportb(DMA2BASE+2*DMA_MASK, DMA_DISABLE|(chan & 3));
  361. }
  362. return 0;
  363. }
  364. /* Enable DMA on specified channel */
  365. int
  366. dma_enable(chan)
  367. int chan;
  368. {
  369. if(chan < 4){
  370. outportb(DMA1BASE+DMA_MASK, DMA_ENABLE|chan);
  371. } else {
  372. outportb(DMA2BASE+2*DMA_MASK,DMA_ENABLE|(chan & 3));
  373. }
  374. return 0;
  375. }