有这种需求的同行自然明白这2个为什么需要配合起来用。简单说说,我的需求是dma位于pci设备侧,pci主的cpu上应用程序直接mmap获取dma发来的数据。
猜测显卡的dma一般位于pci主,所以我在内核里没有找到pci_alloc_consistent 配合mmap的例子。
这个代码在loongson 3A这个极品芯片上都能通过,相信其他平台毫无障碍。
pci_alloc_consistent得到的是申请到的dma一致性缓冲区的内核态虚拟地址以及pci总线地址。
内核态虚拟地址转成物理地址后需要传递给应用做mmap的最后一个参数。
-
#include <linux/module.h>
-
#include <linux/kernel.h>
-
#include <linux/pci.h>
-
#include <linux/init.h>
-
#include <linux/ioport.h>
-
#include <linux/netdevice.h>
-
#include <linux/etherdevice.h>
-
#include <linux/delay.h>
-
#include <linux/ethtool.h>
-
#include <linux/mii.h>
-
#include <linux/crc32.h>
-
#include <linux/io.h>
-
#include <linux/interrupt.h>
-
#include <linux/module.h>
-
#include <linux/kernel.h>
-
#include <linux/types.h>
-
#include <linux/miscdevice.h>
-
#include <linux/ioport.h>
-
#include <linux/fcntl.h>
-
#include <linux/init.h>
-
#include <linux/poll.h>
-
#include <linux/proc_fs.h>
-
#include <linux/seq_file.h>
-
#include <linux/spinlock.h>
-
#include <linux/sched.h>
-
#include <linux/sysctl.h>
-
#include <linux/wait.h>
-
#include <linux/cdev.h>
-
#include <linux/fs.h>
-
#include <linux/delay.h>
-
#include <linux/uaccess.h>
-
#include <linux/device.h>
-
#include <linux/err.h>
-
#include <linux/fs.h>
-
#include <asm/io.h>
-
#include <asm/current.h>
-
#include <asm/system.h>
-
#include <linux/mm.h>
-
#include <linux/mman.h>
-
#include <linux/miscdevice.h>
-
#include <linux/proc_fs.h>
-
#include <linux/device.h>
-
#include <linux/fs.h>
-
#include <linux/slab.h>
-
#include <linux/mm.h>
-
#include <linux/slab.h>
-
-
#define DEVICE_NAME "testc"
-
#define MODNAME "pmc-test"
-
#define MMAPBUF_LEN (16*1024*1024)
-
-
struct my_testdev{
-
dev_t testc_dev_num;
-
struct class * testc_class;
-
unsigned
long testc_kernel_virt_addr;
-
-
struct cdev test_cdev;
-
unsigned
int current_pointer;
/*char device offset ,目前同时只能一个程序读取 */
-
unsigned
long mmap_phyaddr;
-
dma_addr_t mmap_pcibus_addr;
-
unsigned
long mmap_kvirt_addr;
-
};
-
-
//不得已的一个全局变量
-
struct my_testdev *priv;
-
-
//仅仅是为了测试程序 手头只有这个卡
-
static DEFINE_PCI_DEVICE_TABLE(netdrv_pci_tbl) = {
-
{
0x8086,
0x10b9, PCI_ANY_ID, PCI_ANY_ID,
0,
0,
0 },
-
{
0,}
-
};
-
MODULE_DEVICE_TABLE(pci, netdrv_pci_tbl);
-
-
-
static int testc_open(struct inode *inode, struct file *file)
-
{
-
return
0;
-
}
-
-
static int testc_release(struct inode *inode, struct file *file)
-
{
-
-
return
0;
-
}
-
-
-
static ssize_t testc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
-
{
-
-
-
return
0;
-
}
-
-
static ssize_t testc_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
-
{
-
-
return
0;
-
}
-
-
static loff_t testc_llseek(struct file * file,loff_t offset,int orig)
-
{
-
return
0;
-
}
-
-
-
static int testc_mmap(struct file *file, struct vm_area_struct *vma)
-
{
-
-
unsigned
long size = vma->vm_end - vma->vm_start;
-
struct my_testdev *pri=priv;
-
printk(
"inmmap ,mmap_phyaddr %lx , mmap_kvirt_addr %lx \n",pri->mmap_phyaddr,pri->mmap_kvirt_addr);
-
printk(
"start %lx end %lx off %lx \n",vma->vm_start,vma->vm_end,vma->vm_pgoff);
-
//pci_alloc_consistent申请到的内存已经是物理地址连续的 ,只要一个remap_pfn_range
-
remap_pfn_range(vma,vma->vm_start,(pri->mmap_phyaddr)>> PAGE_SHIFT,size,vma->vm_page_prot);
-
-
vma->vm_flags |= VM_RESERVED;
/* avoid to swap out this VMA */
-
return
0;
-
-
}
-
-
static
const
struct file_operations testc_fops = {
-
.read = testc_read,
-
//.aio_read = generic_file_aio_read,
-
.write = testc_write,
-
//.aio_write = blkdev_aio_write,
-
//.fsync = blkdev_fsync,
-
.mmap = testc_mmap,
-
.open = testc_open,
-
.release = testc_release,
-
//.unlocked_ioctl = raw_ioctl,
-
.llseek = testc_llseek,
-
.owner = THIS_MODULE,
-
};
-
-
-
static
int __
devinit pmc_probe(struct pci_dev *pdev,const struct pci_device_id *ent)
-
{
-
int ret;
-
int i;
-
struct device *x;
-
struct my_testdev *pri;
-
printk(
"--------%s %d\n",__FUNCTION__,__LINE__);
-
pri=kmalloc(
sizeof(struct my_testdev),GFP_KERNEL);
-
memset(pri,
0,
sizeof(struct my_testdev));
-
priv=pri;
-
pci_set_drvdata(pdev, pri);
-
-
-
-
//char device
-
if(alloc_chrdev_region(&(pri->testc_dev_num),
0,
1,DEVICE_NAME))
-
{
-
printk(
"err----%s %d \n",__FILE__,__LINE__);
-
return
-1;
-
}
-
pri->testc_class=class_create(THIS_MODULE,DEVICE_NAME);
-
cdev_init(&(pri->test_cdev),&testc_fops);
-
pri->test_cdev.owner=THIS_MODULE;
-
-
ret=cdev_add(&(pri->test_cdev),pri->testc_dev_num,
1);
-
if(ret)
-
{
-
printk(
"err----%s %d \n",__FILE__,__LINE__);
-
return
-1;
-
}
-
//设备节点自动生成
-
x=device_create(pri->testc_class,
NULL,MKDEV(MAJOR(pri->testc_dev_num),
0),pri,DEVICE_NAME);
-
if(x==
NULL)
-
{
-
printk(
"err----%s %d \n",__FILE__,__LINE__);
-
return
-1;
-
}
-
priv=pri;
-
priv->current_pointer=
0;
-
-
-
//mmap
-
pri->mmap_kvirt_addr=(
unsigned
long )pci_alloc_consistent(pdev,MMAPBUF_LEN,&(pri->mmap_pcibus_addr));
-
if((
void *)pri->mmap_kvirt_addr==
NULL)
-
{
-
return
-1;
-
}
-
memset((
void *)pri->mmap_kvirt_addr,
0x0,MMAPBUF_LEN);
-
//写入有意义的数据,方便在应用里验证确实映射了16MB
-
for(i=
0;i<MMAPBUF_LEN/
4;i++)
-
{
-
*(((u32 *)pri->mmap_kvirt_addr)+i)=i;
-
}
-
-
pri->mmap_phyaddr=virt_to_phys((
void *)pri->mmap_kvirt_addr);
-
printk(
"mmap_phyaddr %lx , mmap_kvirt_addr %lx \n",pri->mmap_phyaddr,pri->mmap_kvirt_addr);
-
-
return
0;
-
}
-
static
void __
devexit pmc_remove(struct pci_dev *pdev)
-
{
-
struct my_testdev *pri = pci_get_drvdata(pdev);
-
pci_free_consistent(pdev, MMAPBUF_LEN,(
void *)pri->mmap_kvirt_addr, pri->mmap_pcibus_addr);
-
-
}
-
-
-
static
struct pci_driver netdrv_pci_driver = {
-
.name = MODNAME,
-
.id_table = netdrv_pci_tbl,
-
.probe = pmc_probe,
-
.remove = __devexit_p(pmc_remove),
-
-
};
-
-
static
int __
init pmc_init_module(void)
-
{
-
return pci_register_driver(&netdrv_pci_driver);
-
}
-
-
-
static
void __
exit pmc_cleanup_module(void)
-
{
-
pci_unregister_driver(&netdrv_pci_driver);
-
}
-
-
module_init(pmc_init_module);
-
module_exit(pmc_cleanup_module);
-
-
-
MODULE_AUTHOR(
"deep_pro");
-
MODULE_LICENSE(
"GPL");
简单的应用程序,注意mmap的最后一个参数是dma缓冲区的物理地址,演示程序里根据驱动的打印写的硬编码,最终还是要靠ioctl等机制实现自动从驱动取得。
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <string.h>
-
#include <unistd.h>
-
#include <errno.h>
-
#include <limits.h>
-
#include <linux/kernel.h>
-
-
#include <byteswap.h>
-
#include <unistd.h>
-
#include <sys/types.h>
-
#include <sys/mman.h>
-
#include <sys/types.h>
-
#include <sys/stat.h>
-
#include <fcntl.h>
-
-
#include <sys/ioctl.h>
-
#define MMAP_LEN 16*1024*1024
-
-
int main()
-
{
-
int fd,i,ret;
-
-
char buf[
0x20]={
0};
-
unsigned
int *mmap_data;
-
-
fd=open(
"/dev/testc",O_RDWR);
-
if(fd<
0)
-
{
-
perror(
"open:");
-
return
-1;
-
}
-
//0xf7000000 这个是缓冲区的物理地址,要从驱动里得到
-
mmap_data=(
unsigned
int *)mmap(
NULL,MMAP_LEN,PROT_READ|PROT_WRITE,MAP_SHARED,fd,
0xf7000000);
-
if(mmap_data==MAP_FAILED)
-
{
-
perror(
"mmap");
-
return
-1;
-
}
-
//测试mmap后的缓冲区读写 ,先写再读,这样执行第二遍应用就能验证写入成功
-
for(i=
14*
1024*
1024/
4;i<(
14*
1024*
1024+
0x120)/
4;i++)
-
{
-
//mmap_buf[i]=0;
-
printf(
"mmap_buf %02x :%x \n",i,*(mmap_data+i));
-
*(mmap_data+i)+=
1;
-
}
-
-
munmap(mmap_data,MMAP_LEN);
-
-
close(fd);
-
}
<script>
(function(){
function setArticleH(btnReadmore,posi){
var winH = $(window).height();
var articleBox = $("div.article_content");
var artH = articleBox.height();
if(artH > winH*posi){
articleBox.css({
'height':winH*posi+'px',
'overflow':'hidden'
})
btnReadmore.click(function(){
articleBox.removeAttr("style");
$(this).parent().remove();
})
}else{
btnReadmore.parent().remove();
}
}
var btnReadmore = $("#btn-readmore");
if(btnReadmore.length>0){
if(currentUserName){
setArticleH(btnReadmore,3);
}else{
setArticleH(btnReadmore,1.2);
}
}
})()
</script>
</article>