Main Page   Modules   Alphabetical List   Data Structures   File List   Data Fields   Globals   Related Pages  

dma.c

Go to the documentation of this file.
00001 /*!     \file drivers/dma/dma.c
00002  *      \brief DMA (Direct Memory Access) driver.
00003  *      \author Andrea Righi <drizzt@inwind.it>
00004  *      \date Last update: 2003-11-08
00005  *
00006  *      This driver is based on the Fabian Nunez DMA driver.
00007  *      The author can be reached by email at: <fabian@cs.uct.ac.za>.
00008  */
00009 
00010 #include <const.h>
00011 
00012 #include <arch/i386.h>
00013 #include <arch/mem.h>
00014 #include <arch/paging.h>
00015 
00016 #include <kernel/kernel_map.h>
00017 #include <kernel/kmalloc.h>
00018 #include <kernel/task.h>
00019 
00020 #include <kernel/dma.h>
00021 
00022 // --- DMA transfers -------------------------------------------------- //
00023 
00024 /** \ingroup Drivers
00025  *  \defgroup DMADriver DMA (Direct Memory Access) driver
00026  *  The DMA (Direct Memory Access) low level driver
00027  *  (based on i386-architecture).
00028  *  @{
00029  */
00030 
00031 //! Definition of DMA channels.
00032 static dma_channel_t dmainfo[] =
00033 {
00034         { 0x87, 0x00, 0x01 },
00035         { 0x83, 0x02, 0x03 },
00036         { 0x81, 0x04, 0x05 },
00037         { 0x82, 0x06, 0x07 }
00038 };
00039 
00040 //! \brief Set up a DMA transfer between a device and memory.
00041 //! \param channel The 8-bit channel number (0..3).
00042 //! \param physaddr The physical address of the buffer.
00043 //! \param length The size of the buffer.
00044 //! \param read
00045 //!     If it is #TRUE the transfer will be from memory to device,
00046 //!     otherwise will be from device to memory.
00047 void dma_xfer(unsigned channel, addr_t physaddr, size_t length, bool read)
00048 {
00049         int page, offset;
00050 
00051         if (channel > 3) return;
00052 
00053         // calculate dma page and offset //
00054         page = physaddr >> 16;
00055         offset = physaddr & 0xFFFF;
00056         length -= 1;  // with dma, if you want k bytes, you ask for k-1 //
00057 
00058         disable();
00059 
00060         // set the mask bit for the channel //
00061         outportb(0x0A, channel | 0x04);
00062 
00063         // clear flipflop //
00064         outportb(0x0C, 0x00);
00065 
00066         // set DMA mode (write+single+r/w) //
00067         outportb(0x0B, (read ? 0x48 : 0x44) + channel);
00068 
00069         // set DMA page //
00070         outportb(dmainfo[channel].page, page);
00071 
00072         // set DMA offset //
00073         outportb(dmainfo[channel].offset, offset & 0xFF);  // low byte //
00074         outportb(dmainfo[channel].offset, offset >> 8);    // high byte //
00075 
00076         // set DMA length //
00077         outportb(dmainfo[channel].length, length & 0xFF);  // low byte //
00078         outportb(dmainfo[channel].length, length >> 8);    // high byte //
00079 
00080         // clear DMA mask bit //
00081         outportb(0x0A, channel);
00082 
00083         enable();
00084 }
00085 
00086 /** @} */ // end of DMADriver
00087 
00088 // --- DMA memory allocator ------------------------------------------- //
00089 
00090 /** \ingroup Memory
00091  *  \defgroup KDMAAllocator The DMA (Direct Memory Access) Allocator
00092  *  The allocator for the DMA memory area.
00093  *  @{
00094  */
00095 
00096 //! The size of the DMA memory area.
00097 #define DMA_MEMORY_DIM (DMA_MEMORY_END-DMA_MEMORY_START)
00098 
00099 //! DMA free frames vector. It maps every frame into the DMA memory
00100 //! area. On i386 we have to reserve the first 16MB of physical memory
00101 //! to the DMA, because on the bus we have a 24-bit register dedicated
00102 //! to the addressing (2^24=16MB).
00103 /** \todo
00104  *      Yes I know! A vector is ugly... a bitmap should be much better.
00105  *      It requests some bit twiddling, but we can gain a lot of memory!
00106  *      Unfortunately for now I've not much time to spent here! :-(
00107  */
00108 byte *dma_free_frames;
00109 
00110 //! The DMA-memory frame is marked as free.
00111 #define DMA_FREE_FRAME  0
00112 //! The DMA-memory frame is marked as busy.
00113 #define DMA_BUSY_FRAME  1
00114 
00115 //! \brief Initialize the DMA memory allocator.
00116 void dma_alloc_init()
00117 {
00118         uint32_t i;
00119         extern dword *K_VIR_END;
00120 
00121         // Initialize the DMA free frames vector                        //
00122         dma_free_frames = kmalloc(DMA_MEMORY_DIM/PAGE_SIZE);
00123         memset(dma_free_frames, DMA_FREE_FRAME, DMA_MEMORY_DIM/PAGE_SIZE);
00124 
00125         // Set the kernel physical space busy                           //
00126         for (i=0; i<=(uint32_t)K_VIR_END-K_VIR_START; i+=PAGE_SIZE)
00127                 dma_free_frames[K_PHYS_ADDR+i] = DMA_BUSY_FRAME;
00128 }
00129 
00130 //! \brief
00131 //!     Return a pointer of \p len contiguous bytes into the
00132 //!     DMA address space.
00133 //! \param len The size of the buffer we want to allocate.
00134 //! \return
00135 //!     A pointer to the allocated buffer.
00136 //! \note
00137 //!     The pointer is the <b>physical address</b> of the buffer
00138 //!     and not the <b>virtual address</b>!!!
00139 //! \exception
00140 //!     NULL Out of DMA memory area!
00141 void *dma_phys_alloc(size_t len)
00142 {
00143         dword i, j, ret;
00144         char flag=0;
00145 
00146         // len must be not null!                                        //
00147         if (!len) return(NULL);
00148 
00149         // Round up len to frames boundary                              //
00150         len = PAGE_ALIGN_UP(len);
00151 
00152         sched_enter_critical_region();
00153 
00154         // Search for a free dma page                                   //
00155         for(i=0; i<(DMA_MEMORY_DIM/PAGE_SIZE); i++)
00156         {
00157                 if (dma_free_frames[i] == DMA_FREE_FRAME)
00158                 {
00159                         // Found a free frame!                          //
00160                         // OK, now I require (len/PAGE_SIZE-1)          //
00161                         // others free frames                           //
00162                         for(j=1, flag=0; j<len/PAGE_SIZE; j++)
00163                         {
00164                                 if (dma_free_frames[i+j] == DMA_BUSY_FRAME)
00165                                 {
00166                                         // Nooo! Found a busy frame!    //
00167                                         flag = 1;
00168                                         break;
00169                                 }
00170                         }
00171                         if (flag == 1)
00172                         {
00173                                 // Not a valid contiguous free space.   //
00174                                 // Continue after j position...         //
00175                                 i += j;
00176                         }
00177                         else
00178                         {
00179                                 // Found a valid contiguous free        //
00180                                 // space, so mark it                    //
00181                                 for(j=0; j<len/PAGE_SIZE; j++)
00182                                         dma_free_frames[i+j] = DMA_BUSY_FRAME;
00183                                 break;
00184                         }
00185                 }
00186         }
00187         // Check if we have found the free DMA location                 //
00188         if (flag == 0)
00189                 // We found it!!!                                       //
00190                 ret = (i * PAGE_SIZE + DMA_MEMORY_START);
00191         else
00192                 // Not found!!!                                         //
00193                 ret = NULL;
00194 
00195         sched_leave_critical_region();
00196         return((void *)ret);
00197 }
00198 
00199 //! \brief
00200 //!     Free the DMA memory area from \p dma_start to \p dma_start+len
00201 //!     address.
00202 //! \param dma_start The beginning of the area to free.
00203 //! \param len The size of the buffer to free.
00204 //! \return
00205 //!     \li #TRUE on success;
00206 //!     \li #FALSE if an error occurs.
00207 //! \note
00208 //!     The \p dma_start address is the <b>physical address</b>
00209 //!     of the buffer to free and not the <b>virtual address</b>!!!
00210 bool dma_phys_free(size_t dma_start, size_t len)
00211 {
00212         int i;
00213 
00214         if (!len) return(FALSE);
00215 
00216         // Round up len to frames boundary                              //
00217         len = PAGE_ALIGN_UP(len);
00218 
00219         if (
00220                 (dma_start >= DMA_MEMORY_START) && (dma_start < DMA_MEMORY_END) &&
00221                 ((dma_start + len) < DMA_MEMORY_END)
00222         )
00223         {
00224                 sched_enter_critical_region();
00225 
00226                 // Mark the DMA region as free                          //
00227                 for(i=(dma_start-DMA_MEMORY_START)/PAGE_SIZE; i<DMA_MEMORY_DIM/PAGE_SIZE; i++)
00228                         dma_free_frames[i] = DMA_FREE_FRAME;
00229 
00230                 sched_leave_critical_region();
00231 
00232                 return(TRUE);
00233         }
00234         else
00235                 // Invalid DMA memory range                             //
00236                 return(FALSE);
00237 }
00238 
00239 /** @} */ // end of KDMAAllocator

Generated on Fri Feb 20 15:32:15 2004 for Minirighi by doxygen1.2.18