into continguous virtual addresses with vmap.
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;
}
#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>
kfree(dev->private);
dev->private = NULL;
}
+ module_put(dev->driver->module);
dev->driver = 0;
dev->board_name = NULL;
dev->board_ptr = NULL;
dev->write_subdev = NULL;
dev->open = NULL;
dev->close = NULL;
+ comedi_set_hw_dev(dev, NULL);
}
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;
}
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;
if(!mite)return;
- pci_disable_device(mite->pcidev);
if(mite->mite_io_addr){
iounmap(mite->mite_io_addr);
mite->mite_io_addr=NULL;
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));
}
};
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)
{
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;
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();
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();
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);
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);
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);
{
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];
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
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);
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;
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);
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));
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);
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 */
#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>
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 */
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;
for(i = 0; i < num_subdevices; ++i)
{
dev->subdevices[i].device = dev;
+ dev->subdevices[i].async_dma_dir = DMA_NONE;
}
return 0;
}
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);