分类: LINUX
2012-08-03 17:47:37
1、以IO内存方式控制LED
可以将IO端口映射到IO内存空间来访问,在设备驱动模块的加载函数或者open()函数中可以调用request_mem_region()函数来申请资源,使用ioremap()函数将IO端口所在的物理地址映射到虚拟地址上,之后,就可以调用readb(),readw(),readl()等函数读写寄存器中的内容了,不在使用IO内存时,可以使用iounmap()函数释放物理地址到虚拟地址的映射。最后使用release_mem_region()函数释放申请的资源。
对I/O内存的操作需按如下步骤完成:
1) 申请
n struct resource *request_mem_region(unsigned long start, unsigned long len, char *name)这个函数申请一个从start 开始,长度为len 字节的内存区。如果成功,返回非NULL;否则返回NULL,所有已经在使用的I/O内存在/proc/iomem 中列出。
2) 访问
在访问I/O内存之前, 必须进行物理地址到虚拟地址的映射,ioremap 函数具有此功能:
n void *ioremap(unsigned long phys_addr, unsigned long size)
ioremap接收一个物理地址和一个整个IO端口的大小,返回一个虚拟地址,这个虚拟地址对应一个size大小的物理地址空间。
3) 访问 I/O 内存的正确方法是通过一系列内核提供的函数:
a) 从 I/O 内存读, 使用下列之一:
n unsigned ioread8(void *addr)
n unsigned ioread16(void *addr)
n unsigned ioread32(void *addr)
b) 写I/O 内存, 使用下列之一:
n void iowrite8(u8 value, void *addr)
n void iowrite16(u16 value, void *addr)
n void iowrite32(u32 value, void *addr)
老版本的 I/O 内存访问函数:
c) 从 I/O 内存读, 使用下列之一:
n unsigned readb(address)
n unsigned readw(address)
n unsigned readl(address)
d) 写I/O 内存, 使用下列之一:
n unsigned writeb(unsigned value, address)
n unsigned writew(unsigned value, address)
n unsigned writel(unsigned value, address)
4) 释放内存
I/O内存不再需要使用时应当释放,步骤如下:
n void iounmap(void * addr)
iounmap()函数接收ioremap函数申请的虚拟地址作为参数,并取消物理地址到虚拟地址的映射。
n void release_mem_region(unsigned longstart, unsigned long len)
release_mem_region()函数释放申请的资源。
2、IO内存方式控制LED实例
本实例硬件原理见4.1.7节讲述,。
#include
#include
#include
#include
//#include
//#include
#include
#include
#include
#include
#include
#include
#include
#include
#define VIRTUALMEM_MAJOR 0 /*预设的ioport的主设备号*/
static unsigned int gpfcon_old = 0;
static unsigned int gpfdat_old = 0;
static unsigned int gpfup_old = 0;
static int Virtualmem_major = VIRTUALMEM_MAJOR;
struct resource *IO_mem_resource;
unsigned long io_addr;
/*Virtualmem设备结构体*/
struct Virtualmem_dev
{
struct cdev cdev; /*cdev结构体*/
};
struct Virtualmem_dev *Virtualmem_devp; /*设备结构体指针*/
/*文件打开函数*/
int Virtualmem_open(struct inode *inode, struct file *filp)
{
/*将设备结构体指针赋值给文件私有数据指针*/
filp->private_data = Virtualmem_devp;
printk("In the open process! turn off the led!\n");
iowrite32(0x5500 | ioread32 (io_addr),io_addr);//GPFCON
iowrite32(0xF0 | ioread32 (io_addr+8),(io_addr+8));//GPFUP
iowrite32(0xF0 | ioread32 (io_addr+4),(io_addr+4));//GPFDAT
return 0;
}
/*文件释放函数*/
int Virtualmem_release(struct inode *inode, struct file *filp)
{
iowrite32(gpfcon_old,io_addr);
iowrite32(gpfup_old,(io_addr+8));
iowrite32(gpfdat_old,(io_addr+4));
return 0;
}
/*读函数*/
static ssize_t Virtualmem_read(struct file *filp, char __user *buf, size_t size,
loff_t *ppos)
{
int ret = 0;
struct Virtualmem_dev *dev = filp->private_data; /*获得设备结构体指针*/
return ret;
}
/*写函数*/
static ssize_t Virtualmem_write(struct file *filp, const char __user *buf,
size_t size, loff_t *ppos)
{
unsigned int count = size;
int ret = 0;
struct Virtualmem_dev *dev = filp->private_data; /*获得设备结构体指针*/
unsigned char *userbuf;
/*用户空间->内核空间*/
if (copy_from_user(userbuf, (unsigned char *)buf, count))
ret = - EFAULT;
else
{
iowrite32((*userbuf),(io_addr+4));
printk("write data from user to iomem!\n");
}
return ret;
}
/*文件操作结构体*/
static const struct file_operations Virtualmem_fops =
{
.owner = THIS_MODULE,
.read = Virtualmem_read,
.write = Virtualmem_write,
.open = Virtualmem_open,
.release = Virtualmem_release,
};
/*初始化并注册cdev*/
static void Virtualmem_setup_cdev(struct Virtualmem_dev *dev, int index)
{
int err, devno = MKDEV(Virtualmem_major, index);
cdev_init(&dev->cdev, &Virtualmem_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &Virtualmem_fops;
err = cdev_add(&dev->cdev, devno, 1);
if (err)
printk(KERN_ALERT "Error %d adding Virtualmem%d", err, index);
}
/*设备驱动模块加载函数*/
int Virtualmem_init(void)
{
int result;
dev_t devno = MKDEV(Virtualmem_major, 0);
/* 申请设备号*/
if (Virtualmem_major)
result = register_chrdev_region(devno, 1, "iomem");
else /* 动态申请设备号 */
{
result = alloc_chrdev_region(&devno, 0, 1, "iomem");
Virtualmem_major = MAJOR(devno);
}
if (result < 0)
return result;
/* 动态申请设备结构体的内存*/
Virtualmem_devp = kmalloc(sizeof(struct Virtualmem_dev), GFP_KERNEL);
if (!Virtualmem_devp) /*申请失败*/
{
result = - ENOMEM;
goto fail;
}
memset(Virtualmem_devp, 0, sizeof(struct Virtualmem_dev));
Virtualmem_setup_cdev(Virtualmem_devp, 0);
if ((IO_mem_resource=request_mem_region(0x56000050, 0x0c,"iomem"))==NULL)
goto fail;
else
{
printk("In the init process! \n");
io_addr =(unsigned long) ioremap(0x56000050 , 0x0c);
printk("io_addr : %lx \n", io_addr);
gpfcon_old = ioread32 (io_addr);
gpfdat_old = ioread32 (io_addr+4);
gpfup_old = ioread32 (io_addr+8);
printk("S3C2410_GPFCON is %8X\n",gpfcon_old);
printk("S3C2410_GPFUP is %8X\n",gpfup_old);
printk("S3C2410_GPFDAT is %8X\n",gpfdat_old);
return 0;
}
fail: unregister_chrdev_region(devno, 1);
return result;
}
/*模块卸载函数*/
void Virtualmem_exit(void)
{
if (IO_mem_resource!=NULL) release_mem_region(0x56000050, 0x0c);
cdev_del(&Virtualmem_devp->cdev); /*注销cdev*/
kfree(Virtualmem_devp); /*释放设备结构体内存*/
unregister_chrdev_region(MKDEV(Virtualmem_major, 0), 1); /*释放设备号*/
}
MODULE_AUTHOR("AK-47");
MODULE_LICENSE("Dual BSD/GPL");
module_param(Virtualmem_major, int, S_IRUGO);
module_init(Virtualmem_init);
module_exit(Virtualmem_exit);
3、IO内存方式测试代码
#include
#include
#include
#include
#include
#include
#define DEVICE_FILENAME "/dev/iomem"
int main ( )
{
int dev;
int loop;
char buf[128];
dev = open(DEVICE_FILENAME,O_RDWR|O_NDELAY);
if (dev >= 0)
{
printf("\nwait? input\n");
printf("write the 0x5f to the Port F!\n");
buf[0]=0x5f;
write (dev, buf, 1);
sleep (2);
printf("write the 0xaf to the Port F!\n");
buf[0]=0xaf;
write (dev, buf, 1);
sleep (1);
}
else
{
printf("open failure!\n");
}
close (dev);
return 0;
}
4、驱动程序测试
加载驱动程序后,创建设备节点,然后运行测试程序,由于向LED所在端口先后写了0x5f和0xaf,因此会观察到4个发光二极管交替闪亮一次。