Allocate async buffer pages with dma_alloc_coherent, and map them
authorFrank Mori Hess <fmhess@speakeasy.net>
Sun, 29 Apr 2007 21:41:33 +0000 (21:41 +0000)
committerFrank Mori Hess <fmhess@speakeasy.net>
Sun, 29 Apr 2007 21:41:33 +0000 (21:41 +0000)
into continguous virtual addresses with vmap.

comedi/comedi_fops.c
comedi/drivers.c
comedi/drivers/mite.c
comedi/drivers/mite.h
comedi/drivers/ni_mio_common.c
comedi/drivers/ni_pcidio.c
comedi/drivers/ni_pcimio.c
comedi/drivers/ni_stc.h
include/linux/comedidev.h

index f9aceb0ba8e5fd7ebd02d971b010cbc819914dc6..cdea1bf0120a3cde950c867acb9adc28069b25db 100644 (file)
@@ -1309,8 +1309,8 @@ static int comedi_mmap(struct file * file, struct vm_area_struct *vma)
                return -EFAULT;
 
        n_pages = size >> PAGE_SHIFT;
-       for(i=0;i<n_pages;i++){
-               if(remap_pfn_range(vma, start, __pa(async->buf_page_list[i]) >> PAGE_SHIFT,
+       for(i = 0; i < n_pages; ++i){
+               if(remap_pfn_range(vma, start, page_to_pfn(virt_to_page(async->prealloc_buf + PAGE_SIZE * i)),
                                PAGE_SIZE, PAGE_SHARED)){
                        return -EAGAIN;
                }
index 08d17089debdd3cc6192d4e28f219d10e518871f..c678a9cde659b6b9d9df9b5ef0b22a7f2849f9bd 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/highmem.h>  /* for SuSE brokenness */
 #include <linux/vmalloc.h>
 #include <linux/cdev.h>
+#include <linux/dma-mapping.h>
 
 #include <asm/io.h>
 #include <asm/system.h>
@@ -92,6 +93,7 @@ static void cleanup_device(comedi_device *dev)
                kfree(dev->private);
                dev->private = NULL;
        }
+       module_put(dev->driver->module);
        dev->driver = 0;
        dev->board_name = NULL;
        dev->board_ptr = NULL;
@@ -101,6 +103,7 @@ static void cleanup_device(comedi_device *dev)
        dev->write_subdev = NULL;
        dev->open = NULL;
        dev->close = NULL;
+       comedi_set_hw_dev(dev, NULL);
 }
 
 static int __comedi_device_detach(comedi_device *dev)
@@ -111,7 +114,6 @@ static int __comedi_device_detach(comedi_device *dev)
        }else{
                printk("BUG: dev->driver=NULL in comedi_device_detach()\n");
        }
-       module_put(dev->driver->module);
        cleanup_device(dev);
        return 0;
 }
@@ -430,48 +432,106 @@ int comedi_buf_alloc(comedi_device *dev, comedi_subdevice *s,
        unsigned long new_size)
 {
        comedi_async *async = s->async;
-
+       unsigned long adr;
+       
        /* if no change is required, do nothing */
        if(async->prealloc_buf && async->prealloc_bufsz == new_size){
                return 0;
        }
-
-       if(async->prealloc_bufsz){
+       // cleanup old buffer
+       if(async->prealloc_bufsz)
+       {
                int i;
-               int n_pages = async->prealloc_bufsz >> PAGE_SHIFT;
+               unsigned n_pages = async->prealloc_bufsz >> PAGE_SHIFT;
 
-               for(i=0;i<n_pages;i++){
-                       mem_map_unreserve(virt_to_page(__va(__pa(async->buf_page_list[i]))));
+               adr = (unsigned long) async->prealloc_buf;
+               for(i = 0; i < n_pages; i++){
+                       mem_map_unreserve(virt_to_page(adr));
+                       adr += PAGE_SIZE;
+               }
+               async->prealloc_bufsz = 0;
+       }
+       if(async->prealloc_buf)
+       {
+               if(s->async_dma_dir != DMA_NONE)
+               {
+                       vunmap(async->prealloc_buf);
+               }else
+               {
+                       vfree(async->prealloc_buf);
                }
-
-               vfree(async->prealloc_buf);
                async->prealloc_buf = NULL;
-               kfree(async->buf_page_list);
+       }
+       if(async->buf_page_list)
+       {
+               unsigned i;
+               for(i = 0; i < async->n_buf_pages; ++i)
+               {
+                       if(async->buf_page_list[i].virt_addr)
+                       {
+                               dma_free_coherent(dev->hw_dev, PAGE_SIZE,
+                                       async->buf_page_list[i].virt_addr, async->buf_page_list[i].dma_addr);
+                       }
+               }
+               vfree(async->buf_page_list);
                async->buf_page_list = NULL;
+               async->n_buf_pages = 0;
        }
-
+       // allocate new buffer
        if(new_size){
-               unsigned long adr;
-               int n_pages = new_size >> PAGE_SHIFT;
                int i;
+               unsigned n_pages = (new_size + PAGE_SIZE - 1) >> PAGE_SHIFT;
 
-               async->buf_page_list = kmalloc(sizeof(unsigned long)*n_pages, GFP_KERNEL);
-               async->prealloc_buf = vmalloc_32(new_size);
-               if(async->prealloc_buf == NULL){
-                       async->prealloc_bufsz = 0;
-                       kfree(async->buf_page_list);
-                       return -ENOMEM;
+               // size is rounded up to nearest multiple of PAGE_SIZE
+               new_size = n_pages << PAGE_SHIFT;
+
+               if(s->async_dma_dir != DMA_NONE)
+               {
+                       struct page** pages = NULL;
+
+                       async->buf_page_list = vmalloc(sizeof(struct comedi_buf_page) * n_pages);
+                       if(async->buf_page_list == NULL)
+                       {
+                               return -ENOMEM;
+                       }
+                       memset(async->buf_page_list, 0, sizeof(struct comedi_buf_page) * n_pages);
+                       async->n_buf_pages = n_pages;
+
+                       pages = vmalloc(sizeof(struct page*) * n_pages);
+                       if(pages == NULL)
+                       {
+                               return -ENOMEM;
+                       }
+                       for(i = 0; i < n_pages; i++)
+                       {
+                               async->buf_page_list[i].virt_addr = dma_alloc_coherent(dev->hw_dev,
+                                       PAGE_SIZE, &async->buf_page_list[i].dma_addr, GFP_KERNEL | __GFP_COMP);
+                               if(async->buf_page_list[i].virt_addr == NULL)
+                               {
+                                       vfree(pages);
+                                       return -ENOMEM;
+                               }
+                               pages[i] = virt_to_page(async->buf_page_list[i].virt_addr);
+                       }
+                       async->prealloc_buf = vmap(pages, n_pages, VM_MAP, PAGE_KERNEL_NOCACHE);
+                       vfree(pages);
+                       if(async->prealloc_buf == NULL) return -ENOMEM;
+               }else
+               {
+                       async->prealloc_buf = vmalloc(new_size);
+                       if(async->prealloc_buf == NULL)
+                       {
+                               return -ENOMEM;
+                       }
                }
-               memset(async->prealloc_buf,0,new_size);
 
                adr = (unsigned long)async->prealloc_buf;
-               for(i=0;i<n_pages;i++){
-                       async->buf_page_list[i] = kvirt_to_kva(adr);
-                       mem_map_reserve(virt_to_page(__va(__pa(async->buf_page_list[i]))));
+               for(i = 0; i < n_pages; i++)
+               {
+                       mem_map_reserve(virt_to_page(adr));
                        adr += PAGE_SIZE;
                }
        }
-
        async->prealloc_bufsz = new_size;
 
        return 0;
index 7849f8f85f567da903c2a27a5493b557b976a952..29a980b0bf49ade1d373d11778053743a6b7d641 100644 (file)
@@ -196,7 +196,6 @@ void mite_unsetup(struct mite_struct *mite)
 
        if(!mite)return;
 
-       pci_disable_device(mite->pcidev);
        if(mite->mite_io_addr){
                iounmap(mite->mite_io_addr);
                mite->mite_io_addr=NULL;
@@ -308,8 +307,8 @@ int mite_buf_change(struct mite_dma_descriptor_ring *ring, comedi_async *async)
 
        for(i = 0; i < n_links; i++){
                ring->descriptors[i].count = cpu_to_le32(PAGE_SIZE);
-               ring->descriptors[i].addr = cpu_to_le32(virt_to_bus(
-                       (void *)async->buf_page_list[i]));
+               ring->descriptors[i].addr = cpu_to_le32(async->buf_page_list[i].dma_addr);
+               // FIXME: virt_to_bus is deprecated
                ring->descriptors[i].next = cpu_to_le32(virt_to_bus(
                        ring->descriptors + i + 1));
        }
index 15933f2a25fcdfea0bd36f306b71f317db805789..6a2a4248eebf514f28ff93b988e19928f46289f8 100644 (file)
@@ -350,11 +350,14 @@ enum ConfigRegister_bits
 };
 static inline int CR_REQS(int source)
 {
-       return (source & 0x7) <<16;
+       return (source & 0x7) << 16;
 };
-static inline int CR_REQSDRQ(int drq_line)
+static inline int CR_REQSDRQ(unsigned drq_line)
 {
-       return CR_REQS(drq_line + 0x4);
+       /* m-series are supposed to have 6 dma channels, but
+       I not sure how to set the drq line on the mite when
+       using channels 4 or 5. */
+       return CR_REQS((drq_line & 0x3) | 0x4);
 }
 static inline int CR_RL(unsigned int retry_limit)
 {
index 54e26920f5fd010a4120c21f145f02325bd5135c..54c38465afea85cf754441483b50f3447989f41a 100644 (file)
@@ -311,6 +311,7 @@ static void get_last_sample_6143( comedi_device *dev );
 static int ni_ai_drain_dma(comedi_device *dev );
 
 /* DMA channel setup */
+
 static inline void ni_set_ai_dma_channel(comedi_device *dev, int channel)
 {
        unsigned long flags;
@@ -319,7 +320,7 @@ static inline void ni_set_ai_dma_channel(comedi_device *dev, int channel)
        devpriv->ai_ao_select_reg &= ~AI_DMA_Select_Mask;
        if(channel >= 0)
        {
-               devpriv->ai_ao_select_reg |= (1 << (channel + AI_DMA_Select_Shift)) & AI_DMA_Select_Mask;
+               devpriv->ai_ao_select_reg |= (ni_stc_dma_channel_select_bitfield(channel) << AI_DMA_Select_Shift) & AI_DMA_Select_Mask;
        }
        ni_writeb(devpriv->ai_ao_select_reg, AI_AO_Select);
        mmiowb();
@@ -334,7 +335,7 @@ static inline void ni_set_ao_dma_channel(comedi_device *dev, int channel)
        devpriv->ai_ao_select_reg &= ~AO_DMA_Select_Mask;
        if(channel >= 0)
        {
-               devpriv->ai_ao_select_reg |= (1 << (channel + AO_DMA_Select_Shift)) & AO_DMA_Select_Mask;
+               devpriv->ai_ao_select_reg |= (ni_stc_dma_channel_select_bitfield(channel) << AO_DMA_Select_Shift) & AO_DMA_Select_Mask;
        }
        ni_writeb(devpriv->ai_ao_select_reg, AI_AO_Select);
        mmiowb();
@@ -344,6 +345,8 @@ static inline void ni_set_ao_dma_channel(comedi_device *dev, int channel)
 static int ni_request_ai_mite_channel(comedi_device *dev)
 {
        unsigned long flags;
+       /* really should be 5 for m-series, but I don't know how to get
+       mite dma channels 4 and 5 working */
        static const unsigned max_dma_channel = 3;
 
        comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
@@ -363,6 +366,8 @@ static int ni_request_ai_mite_channel(comedi_device *dev)
 static int ni_request_ao_mite_channel(comedi_device *dev)
 {
        unsigned long flags;
+       /* really should be 5 for m-series, but I don't know how to get
+       mite dma channels 4 and 5 working */
        static const unsigned max_dma_channel = 3;
 
        comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
@@ -1239,7 +1244,7 @@ static int ni_ai_setup_MITE_dma(comedi_device *dev,comedi_cmd *cmd)
 
        retval = ni_request_ai_mite_channel(dev);
        if(retval) return retval;
-       //rt_printk("comedi_debug: using mite channel %i for ai.\n", devpriv->ai_mite_chan->channel);
+//     rt_printk("comedi_debug: using mite channel %i for ai.\n", devpriv->ai_mite_chan->channel);
 
        /* write alloc the entire buffer */
        comedi_buf_write_alloc(s->async, s->async->prealloc_bufsz);
@@ -2513,7 +2518,7 @@ static int ni_ao_insn_write(comedi_device *dev,comedi_subdevice *s,
 {
        unsigned int chan = CR_CHAN(insn->chanspec);
        unsigned int invert;
-
+       
        invert = ni_ao_config_chanlist(dev,s,&insn->chanspec, 1, 0);
 
        devpriv->ao[chan] = data[0];
@@ -3392,29 +3397,33 @@ static int ni_E_init(comedi_device *dev,comedi_devconfig *it)
                s->cancel = &ni_ai_reset;
                s->poll = &ni_ai_poll;
                s->munge = &ni_ai_munge;
+#ifdef PCIDMA
+               s->async_dma_dir = DMA_FROM_DEVICE;
+#endif
        }else{
                s->type=COMEDI_SUBD_UNUSED;
        }
 
        /* analog output subdevice */
 
-       s=dev->subdevices+1;
+       s = dev->subdevices + 1;
        if(boardtype.n_aochan){
-               s->type=COMEDI_SUBD_AO;
-               s->subdev_flags=SDF_WRITABLE|SDF_DEGLITCH|SDF_GROUND;
+               s->type = COMEDI_SUBD_AO;
+               s->subdev_flags = SDF_WRITABLE | SDF_DEGLITCH | SDF_GROUND;
                if(boardtype.reg_type & ni_reg_m_series_mask)
                        s->subdev_flags |= SDF_SOFT_CALIBRATED;
-               s->n_chan=boardtype.n_aochan;
-               s->maxdata=(1<<boardtype.aobits)-1;
+               s->n_chan = boardtype.n_aochan;
+               s->maxdata = (1 << boardtype.aobits) - 1;
                s->range_table = boardtype.ao_range_table;
-               s->insn_read=ni_ao_insn_read;
+               s->insn_read = &ni_ao_insn_read;
                if(boardtype.reg_type & ni_reg_6xxx_mask){
-                       s->insn_write=ni_ao_insn_write_671x;
+                       s->insn_write = &ni_ao_insn_write_671x;
                }else{
-                       s->insn_write=ni_ao_insn_write;
+                       s->insn_write = &ni_ao_insn_write;
                }
 #ifdef PCIDMA
                if(boardtype.n_aochan){
+                       s->async_dma_dir = DMA_TO_DEVICE;
 #else
                if(boardtype.ao_fifo_depth){
 #endif
@@ -3427,9 +3436,9 @@ static int ni_E_init(comedi_device *dev,comedi_devconfig *it)
                        if((boardtype.reg_type & ni_reg_m_series_mask) == 0)
                                s->munge=ni_ao_munge;
                }
-               s->cancel=ni_ao_reset;
+               s->cancel = &ni_ao_reset;
        }else{
-               s->type=COMEDI_SUBD_UNUSED;
+               s->type = COMEDI_SUBD_UNUSED;
        }
        if((boardtype.reg_type & ni_reg_67xx_mask))
                init_ao_67xx(dev, s);
@@ -3586,6 +3595,7 @@ static int ni_E_init(comedi_device *dev,comedi_devconfig *it)
                s->do_cmd = ni_gpct_cmd;
                s->do_cmdtest = ni_gpct_cmdtest;
                s->cancel = ni_gpct_cancel;
+               s->async_dma_dir = DMA_BIDIRECTIONAL;
                s->private = &devpriv->counters[j];
 
                devpriv->counters[j].dev = dev;
index a00040886c753c1cb41a956f8031bea90df97357..9f9f1eaed8d7f1799dac2e06d9e86086a29879d8 100644 (file)
@@ -1141,6 +1141,7 @@ static int nidio_attach(comedi_device *dev,comedi_devconfig *it)
                printk("error setting up mite\n");
                return ret;
        }
+       comedi_set_hw_dev(dev, &devpriv->mite->pcidev->dev);
 
        dev->board_name=this_board->name;
        irq=mite_irq(devpriv->mite);
@@ -1184,6 +1185,7 @@ static int nidio_attach(comedi_device *dev,comedi_devconfig *it)
                s->cancel = ni_pcidio_cancel;
                s->len_chanlist=32;             /* XXX */
                s->buf_change = ni_pcidio_change;
+               s->async_dma_dir = DMA_BIDIRECTIONAL;
 
                writel(0,devpriv->mite->daq_io_addr+Port_IO(0));
                writel(0,devpriv->mite->daq_io_addr+Port_Pin_Directions(0));
index 3e87320475ddd58e78fe878ee490e117f38838fa..b1a38dee04bc1a4272577ab4470d2ad528d8d56f 100644 (file)
@@ -1472,6 +1472,7 @@ static int pcimio_attach(comedi_device *dev,comedi_devconfig *it)
                printk(" error setting up mite\n");
                return ret;
        }
+       comedi_set_hw_dev(dev, &devpriv->mite->pcidev->dev);
 
        if(boardtype.reg_type & ni_reg_m_series_mask)
                m_series_init_eeprom_buffer(dev);
index 6390c384c2d5217a78b989a459718a37494908eb..86699b46fce0055e7e246d4bd79bcdde4e600bf8 100644 (file)
@@ -699,6 +699,14 @@ enum AI_AO_Select_Bits
        AO_DMA_Select_Mask = 0xf << AO_DMA_Select_Shift
 };
 #define G0_G1_Select                   0x0b
+static inline unsigned ni_stc_dma_channel_select_bitfield(unsigned channel)
+{
+       if(channel < 4) return 1 << channel;
+       if(channel == 4) return 0x3;
+       if(channel == 5) return 0x5;
+       BUG();
+       return 0;
+}
 
 /* 16 bit registers */
 
index 8ad1e99e83f6598efb5e8e7f9d53bf76e8cc933c..eb1f6205e559acb9731562f21996618b9ed23d9d 100644 (file)
@@ -38,7 +38,7 @@
 #include <linux/vmalloc.h>
 #include <linux/mm.h>
 #include <linux/interrupt.h>
-
+#include <linux/dma-mapping.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
 
@@ -143,18 +143,27 @@ struct comedi_subdevice_struct{
 
        void (*munge)( comedi_device *dev, comedi_subdevice *s, void *data,
                unsigned int num_bytes, unsigned int start_chan_index );
+       enum dma_data_direction async_dma_dir;
 
        unsigned int state;
 
        struct class_device *class_dev;
 };
 
+struct comedi_buf_page
+{
+       void *virt_addr;
+       dma_addr_t dma_addr;
+};
+
 struct comedi_async_struct{
        comedi_subdevice *subdevice;
 
        void            *prealloc_buf;          /* pre-allocated buffer */
        unsigned int    prealloc_bufsz;         /* buffer size, in bytes */
-       unsigned long   *buf_page_list;         /* physical address of each page */
+       struct comedi_buf_page *buf_page_list;          /* virtual and dma address of each page */
+       unsigned n_buf_pages;   /* num elements in buf_page_list */
+
        unsigned int    max_bufsize;            /* maximum buffer size, bytes */
        unsigned int    mmap_count;     /* current number of mmaps of prealloc_buf */
 
@@ -210,6 +219,9 @@ struct comedi_device_struct{
 
        struct class_device *class_dev;
        unsigned minor;
+       /* hw_dev is passed to dma_alloc_coherent when allocating async buffers for subdevices
+       that have async_dma_dir set to something other than DMA_NONE */
+       struct device *hw_dev;
 
        const char *board_name;
        const void * board_ptr;
@@ -386,6 +398,7 @@ static inline int alloc_subdevices(comedi_device *dev, unsigned int num_subdevic
        for(i = 0; i < num_subdevices; ++i)
        {
                dev->subdevices[i].device = dev;
+               dev->subdevices[i].async_dma_dir = DMA_NONE;
        }
        return 0;
 }
@@ -407,6 +420,19 @@ static inline unsigned int bytes_per_sample( const comedi_subdevice *subd )
                return sizeof( sampl_t );
 }
 
+static inline void comedi_set_hw_dev(comedi_device *dev, struct device *hw_dev)
+{
+       if(dev->hw_dev)
+       {
+               put_device(dev->hw_dev);
+       }
+       dev->hw_dev = hw_dev;
+       if(dev->hw_dev)
+       {
+               get_device(dev->hw_dev);
+       }
+}
+
 int comedi_buf_put(comedi_async *async, sampl_t x);
 int comedi_buf_get(comedi_async *async, sampl_t *x);