wawava
发表于 2012-07-10 21:21
linuxfellow 发表于 2012-06-28 08:44 static/image/common/back.gif
回复 7# bensenq
不好意思,你是对的:
第一种方法,用现成驱动“/dev/mem"的mmap()来映射物理空间、I ...
第一种方法不需要在kernel中编写FPGA的驱动吗?
linuxfellow
发表于 2012-07-11 11:04
回复 11# wawava
不需要,直接用系统提供的 /dev/mem驱动
wawava
发表于 2012-07-11 11:50
那在应用程序中mmap的参数:star和fd的值怎么填写?
linuxfellow
发表于 2012-07-11 12:33
回复 13# wawava
帮你找了一个例子:
http://stackoverflow.com/questions/9662193/how-to-access-kernel-space-from-user-spacein-linux
wawava
发表于 2012-07-11 13:16
使用mem访问地址已搞定,谢谢LZ。现在还有几个问题:
1 看网上说中断在用户空间无法使用,我要在应用程序中使用fpga的中断,使用mem映射后可行吗,应用程序中使用request_irq就可以了吗?
2 看网上说新内核只支持1m内存映射,我用的kernel是2.6.27,我映射的物理地址是0xe3000000,好像没问题。是我这个版本低了吗?新版本就不行了吗?
linuxfellow
发表于 2012-07-11 13:54
回复 15# wawava
上面那个例子是从把内禾地址映射到用户空间,从而通过用户空间访问kernel address.
request_irq()不能从用户空间调用;如果你的FPGA触发中断,第一种方法就不适用;你可能要写内核空间的驱动。用第二或第三种方法。
内核空间和用户空间地址比例是可以配置的: (1G/3G)/(2G/2G)/(3G/1G)
wawava
发表于 2012-07-11 17:15
从网上找了则linux下的fpga驱动,有几个地方不懂,请大侠给讲解下:
/*
* FPGA support
*/
MODULE_LICENSE("Dual BSD/GPL");
#define FPGA_PHY_START 0xe3000000
#define FPGA_PHY_SIZE (0x01000000)
#define FPGA_MAJOR 211 //主设备号211
#define FPGA_MINOR 0 //从设备号0
unsigned long fpga_vp; //映射的虚拟地址
#define fpga_dma_channel 0
#define DCG_GPIO1_BASE 0xB1900000
#define DCG_GOIO1_MODE 0x002C
#define DMA_DDMA0_BASE 0xB4002000
#define DMA_GLOBAL_BASE 0xB4003000
struct fpga_dma_device{
unsigned int dma_irq;
unsigned int dma_channel;
};
static int fpga_open(struct inode *,struct file*);
static int fpga_release(struct inode *,struct file *);
static ssize_t fpga_read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos);
static ssize_t fpga_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos);
static int fpga_mmap( struct file *,struct vm_area_struct *);
static int fpga_ioctl(struct inode *,struct file *);
struct file_operations fpga_fops={
.open=fpga_open,
.release=fpga_release,
.read=fpga_read,
.write=fpga_write,
.ioctl=fpga_ioctl,
.mmap=fpga_mmap,
.owner=THIS_MODULE
};
irqreturn_t fpga_dma_interrupt(int irq, void *dev_id)
{
struct motion_key_dev * motiondev = (struct motion_key_dev *)dev_id;
//u32 reg_value = 0;
(motion_key_device->count)++;
motion_key_device->int_come = 1;
printk(KERN_WARNING "Enter motion_key_interrup %d \n", motion_key_device->count);
printk(KERN_WARNING "reg value = 0x%x \n", __raw_readl(S3C24XX_EXTINT2));
wake_up_interruptible(&motiondev->key_queue);
printk(KERN_INFO "fpga_dma_interrupt \n");
return IRQ_HANDLED;
}
static int fpga_open(struct inode *ip,struct file *fp)
{
struct fpga_dma_device *fpga_device;
int error = 0;
if((error=request_irq(fpga_device->dma_irq, fpga_dma_interrupt, IRQF_DISABLED, "fpga", NULL)))
return error;
if((error=request_dma(fpga_device.dma_channel,"fpga"))){
free_irq(fpga_device.dma_irq,NULL);
return error;
}
fp->f_op=&fpga_fops;
fpga_vp=(unsigned long)ioremap(FPGA_PHY_START,FPGA_PHY_SIZE);
if(!fpga_vp){
printk("ioremapfpga failed\n");
return -EINVAL;
}
printk(KERN_INFO"fpga_vp is 0x%.8x \n",fpga_vp);
return(0);
}
static int fpga_release(struct inode *ip,struct file *fp)
{
if(fpga_vp)
iounmap(fpga_vp);
struct fpga_dma_device *fpga_device;
free_dma(fpga_dma_device->dma_channel);
free_irq(fpga_device->dma_irq,NULL);
return(0);
}
static ssize_t fpga_read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos)
{
unsigned char *p = (unsigned char *)fpga_vp;
ssize_t retval = 0;
if(copy_to_user(buf, p, count))
{
retval = -EFAULT;
goto out;
}
retval = count;
out:
return retval;
}
static ssize_t fpga_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
{
//unsigned char *p = (unsigned char *)fpga_vp;
ssize_t retval = 0;
//printk("Enter write\n");
if(copy_from_user(fpga_vp, buf, count))
{
retval = -EFAULT;
printk("Write error\n");
goto out;
}
retval = count;
out:
return retval;
}
void fpga_vma_open(struct vm_area_struct *vma)
{ return; }
void fpga_vma_close(struct vm_area_struct *vma)
{ return; }
static struct vm_operations_struct fpga_remap_vm_ops = {
.open =fpga_vma_open,
.close = fpga_vma_close,
};
static int fpga_mmap( struct file *filp,struct vm_area_struct *vma)
{
unsigned int len;
unsigned long start=0, off;
if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
{
printk(" Error vma->vm_pgoff > !OUL PAGE_SHIFT \n");
return -EINVAL;
}
start =FPGA_PHY_START;
len =FPGA_PHY_SIZE;
off = vma->vm_pgoff << PAGE_SHIFT;
if ((vma->vm_end - vma->vm_start + off) > len)
{
printk(" Error vma->vm_end-vma->vm_start\n");
return -EINVAL;
}
off += start;
vma->vm_pgoff = off >> PAGE_SHIFT;
pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK;
pgprot_val(vma->vm_page_prot) |= PAGE_CACHABLE_DEFAULT;
vma->vm_flags |= VM_IO;
if (remap_pfn_range(vma,vma->vm_start, off,
vma->vm_end - vma->vm_start,
vma->vm_page_prot))
{
return -EAGAIN;
}
}
static int fpga_ioctl(struct inode *ip,struct file *fp)
{
printk("use fpga_ioctl\n");
return 0;
}
void fpga_dma_init(void)
{
unsigned int gpio1_mode=0x00000010;
au_writel( gpio1_mode , DCG_GPIO1_BASE + 0x0100 );//sys_triout--->GPIO4
gpio1_mode=au_readl(DCG_GPIO1_BASE + 0x0100);
printk(KERN_ALERT "sys_triout--->GPIO4: %.8x \n",gpio1_mode);
unsigned int dma_val=au_readl(DMA_GLOBAL_BASE+0x0000);//ddma_config
dma_val |=0x00000000;
au_writel(dma_val,DMA_GLOBAL_BASE+0x0000);
dma_val=au_readl(DMA_GLOBAL_BASE+0x0008);//ddma_throttle
dma_val |= 0x80000000;
au_writel(dma_val,DMA_GLOBAL_BASE+0x0008);
dma_val=au_readl(DMA_GLOBAL_BASE+0x000c);//ddma_inten
dma_val |= 0x00000001;
au_writel(dma_val,DMA_GLOBAL_BASE+0x000c);
}
static int __init fpga_init(void)
{
int fpga_base;
int fpgamajor;
unsigned int mac0_phy;
printk(KERN_INFO "-----fpga----\n");
fpga_dma_init();
fpgamajor=register_chrdev(FPGA_MAJOR,"fpga",&fpga_fops);
if(fpgamajor < 0)
{
printk(KERN_ERR "register_chrdev fault! \n");
return 0;
}
printk(KERN_INFO "fpga:fpgamajor=0x%x or %d\n",fpgamajor,fpgamajor);
printk(KERN_INFO "fpga:init_module() OUT \n");
return 0;
}
static void __exit fpga_exit(void)
{
unregister_chrdev(FPGA_MAJOR,"fpga");
printk(KERN_INFO "fpga cleanup_module OUT \n");
}
module_init(fpga_init);
module_exit(fpga_exit);
/*
* END FPGA support
*/
上面这段代码中有一下几个问题不懂
1 fpga_dma_init函数起什么作用?fpga驱动需要用到dma?
2 那个fpga_mmap函数不懂,PAGE_SHIFT ,_CACHE_MASK ,PAGE_CACHABLE_DEFAULT这几个参数什么意思?
3 注册中断究竟要怎么用?先在kernel中request_irq再在应用程序中使用Request_irq?
linuxfellow
发表于 2012-07-12 12:29
本帖最后由 linuxfellow 于 2012-07-12 12:34 编辑
回复 17# wawava
我正在写自己的FPGA驱动,比你找的这个简单的多。没有DMA, 没有中断,写起来相对容易。
你找的这个驱动是老式驱动,设备注册方式也是老式的, 没有用新驱动框架。
下面我试着回答你的问题:
1 fpga_dma_init函数起什么作用?fpga驱动需要用到dma?
这个FPGA里实现了DMA功能,如果系统里有用到DMA, 就要对其进行初始化或配置。
2 那个fpga_mmap函数不懂,PAGE_SHIFT ,_CACHE_MASK ,PAGE_CACHABLE_DEFAULT这几个参数什么意思?
给定了FPGA的物理地址,驱动运行在内核,不能直接操作物理地址,就要将FPGA的物理地址映射到内核地址空间,在进行操作。mmap的作用就是将FPGA的物理地址映射到内核地址空间。映射后有些保护标志要进行设置。主要是保证DMA的memory是不被cache
PAGE_SHIFT =12页面大小为4K
_CACHE_MASK 页表中与保护有关的标志,用于DMA的memory是不能cache的,这些标志要清除
PAGE_CACHABLE_DEFAULT是页面缺省的标志, 一般为_CACHE_UNCACHED
3 注册中断究竟要怎么用?先在kernel中request_irq再在应用程序中使用Request_irq?
例子中的FPGA会产生中断,FPGA作为中断源,有中断输出信号接到处理器中断控制器的某个中断信号。中断发生时,处理器就会中断目前正在运行的程序,转而执行该中断的处理程序。
Linux实现了两级中断表,第一级中断表在系统初始化时就填好,第二级中断表只填了一个简单的default的处理程序。Request_irq就是在二级中断表里相应的表项里填该FPGA特殊的中断处理程序。在内核里调用Request_irq后该中断就设置好了。FPGA请求中断时,这一处理程序就会被调用来处理相应中断。用户空间不需再作任何事情
wawava
发表于 2012-07-12 13:03
/*
* FPGA support
*/
MODULE_LICENSE("GPL");
#define FPGA_PHY_START 0xe3000000
#define FPGA_PHY_SIZE (0x01000000)
#define FPGA_MAJOR 211 //主设备号211
#define FPGA_MINOR 0 //从设备号0
unsigned long fpga_vp; //映射的虚拟地址
#define DEVICE_NAME "FPGA_Driver"
static int FPGA_Major = FPGA_MAJOR;
static int FPGA_Minor = 0;
struct cdev* FPGA_cdev = NULL;
static int fpga_open(struct inode *,struct file*);
static int fpga_release(struct inode *,struct file *);
static ssize_t fpga_read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos);
static ssize_t fpga_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos);
static int fpga_mmap( struct file *,struct vm_area_struct *);
static int fpga_ioctl(struct inode *,struct file *);
struct file_operations fpga_fops={
.open=fpga_open,
.release=fpga_release,
.read=fpga_read,
.write=fpga_write,
.ioctl=fpga_ioctl,
.mmap=fpga_mmap,
.owner=THIS_MODULE
};
irqreturn_t fpga_interrupt(int irq, void *dev_id)
{
printk(KERN_INFO "fpga_dma_interrupt \n");
return IRQ_HANDLED;
}
static int fpga_open(struct inode *ip,struct file *fp)
{
int error = 0;
if((error=request_irq(IRQ_GPI_03, fpga_interrupt, IRQF_DISABLED, "fpga", NULL)))
{
printk(KERN_INFO" request_irq fault!\n");
}
fp->f_op=&fpga_fops;
fpga_vp=(unsigned long)ioremap(FPGA_PHY_START,FPGA_PHY_SIZE);
if(!fpga_vp){
printk("ioremapfpga failed\n");
return -EINVAL;
}
printk(KERN_INFO"fpga_vp is 0x%.8x \n",fpga_vp);
return(0);
}
static int fpga_release(struct inode *ip,struct file *fp)
{
if(fpga_vp)
iounmap(fpga_vp);
struct fpga_dma_device *fpga_device;
free_irq(IRQ_GPI_03,NULL);
return(0);
}
static ssize_t fpga_read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos)
{
unsigned char *p = (unsigned char *)fpga_vp;
ssize_t retval = 0;
if(copy_to_user(buf, p, count))
{
retval = -EFAULT;
goto out;
}
retval = count;
out:
return retval;
}
static ssize_t fpga_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
{
//unsigned char *p = (unsigned char *)fpga_vp;
ssize_t retval = 0;
//printk("Enter write\n");
if(copy_from_user(fpga_vp, buf, count))
{
retval = -EFAULT;
printk("Write error\n");
goto out;
}
retval = count;
out:
return retval;
}
static int fpga_mmap( struct file *filp,struct vm_area_struct *vma)
{
unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
unsigned long physical = FPGA_PHY_START + off;
unsigned long vsize = vma->vm_end - vma->vm_start;
unsigned long psize = FPGA_PHY_SIZE - off;
if(vsize > psize)
return -EINVAL; /*spans too high*/
vma->vm_flags |= VM_IO|VM_RESERVED;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
/*remap physic addr to kernel virtual addr*/
remap_pfn_range(vma, vma->vm_start, physical, vsize, vma->vm_page_prot);
printk(KERN_INFO "memory mapped to user space\n");
return 0;
}
static int fpga_ioctl(struct inode *ip,struct file *fp)
{
printk("use fpga_ioctl\n");
return 0;
}
static int __init fpga_init(void)
{
int ret;
dev_t dev = 0;
FPGA_cdev = cdev_alloc();
printk(DEVICE_NAME"start init\n");
/*register device, if FPGA_Major is not 0, it will get this major number, or allocated by kernel
* register_chrdev_region instead of register_chrdev for v2.6 kernel*/
if (FPGA_Major)
{
dev = MKDEV(FPGA_Major, FPGA_Minor);
ret = register_chrdev_region(dev, 1, DEVICE_NAME);
}
else
{
ret = alloc_chrdev_region(&dev, FPGA_Minor, 1, DEVICE_NAME);
FPGA_Major = MAJOR(dev);
}
if (ret < 0)
{
printk(KERN_WARNING "FPGA: can't get major %d\n", FPGA_Major);
return ret;
}
cdev_init(FPGA_cdev, &fpga_fops);
FPGA_cdev->owner = THIS_MODULE;
FPGA_cdev->ops = &fpga_fops;
ret = cdev_add (FPGA_cdev, MKDEV(FPGA_Major, FPGA_Minor), 1);
/* Fail gracefully if need be */
if (ret)
{
printk(KERN_NOTICE "Error %d adding FPGA", ret);
return ret;
}
printk(DEVICE_NAME"inited\n");
return 0;
}
static void __exit fpga_exit(void)
{
dev_t dev;
dev = MKDEV(FPGA_Major, FPGA_Minor);
unregister_chrdev_region(dev, 1);
if(FPGA_cdev)
{
cdev_del(FPGA_cdev);
printk(DEVICE_NAME"delete cdev\n");
}
printk(DEVICE_NAME"exit\n");
return;
}
module_init(fpga_init);
module_exit(fpga_exit);
/*
* END FPGA support
*/
驱动又改了下,没用dma了,先用register_chrdev_region注册设备,现在这个mmap函数参照网上改的,我下载到板子上试了下,能映射到地址了。
我在fpga_open函数中挂载中断,为什么失败啊?
我的fpga_read和fpga_write函数写的对吗,使用copy_from_user、 copy_to_user就可以了吗?我看你说的方法2中是不是不用在内核中写write和read函数啊?
我从应用程序中加载fpga,直接写到对应得地址就可以了吗?
您说的那个中断我还是不太明白,那个中断处理程序是我应用程序中的一部分,我怎么注册这个中断处理程序呢?
小弟菜鸟一个,问题有点多啊,真的太谢谢lz了,如果方便的话留个联系方式吧
linuxfellow
发表于 2012-07-13 12:46
本帖最后由 linuxfellow 于 2012-07-13 13:21 编辑
回复 19# wawava
把中断request的语句稍微改一下,并把这部分代码移到module_init里面,不要放在open函数里
error=request_irq(IRQ_GPI_03, fpga_interrupt, IRQF_DISABLED, "fpga", NULL);
if(error)
{
printk(KERN_INFO" request_irq fault!\n");
}
中断请求错误信息是什么?
copy_from_user、 copy_to_user只是用于用户空间和内核空间传递参数;read/write函数一般是向设备io口或寄存器里读写数据。
你的read()读了fpga base开始的几个字节(count), write()也只是向fpga base开始的几个字节里写; 这是你想要的操作?
从应用程序中加载fpga,更不能直接写到对应得地址。用第一种方法,把物理地址映射到用户空间,然后加载。
写驱动,关键得懂硬件设备如何工作;不知道硬件设备如何工作,就开始写驱动,无论如何也写不好。
你应该多和你们的硬件工程师聊聊,让他告诉你FPGA实现了那些功能,FPGA有那些寄存器, 那些用于功能控制,那些用于状态监测。你的板子的功能又是什么,FPGA的作用,等等。整体功能清楚了,驱动写起来就是水到渠成。
中断如何工作? 最好还是请教FPGA设计人员,他会给你讲明白什么时候发生中断,中断发生后,如何进行处理。他不一定知道如何写软件,但他肯定知道FPGA如何工作,中断应该如何被处理。
还有你既然刚开始学写驱动,花点时间把新的驱动框架弄明白。这很花时间, 但这个时间你无论如何要花。你在网上找的这个驱动模式已经有点过时,在目前kernel release的驱动里已经找不到这样的代码了。以后如果要找驱动的例子,就直接到最新release的linux/drivers里找