I/O 内存访问流程:
1. request_mem_region() 申请IO内存
2.ioremap() 将物理地址映射到虚拟地址
3.ioread8() 、ioread16()、ioread32()、iowrite8()、iowrite16()、iowrite32() 读写
4.iounmap() 释放虚拟内存
5.release_mem_region() 释放IO内存
注意:
1、2 在模块初始化或打开设备时调用
4、5 在模块卸载或关闭设备时调用
request_mem_region()不是必须使用,但建议使用。任务是检查申请的资源是否可用,如果可用则申请成功,并标志为已经使用,其他驱动想再次申请该资源就会失败。
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <linux/ioport.h>
#define DEVICE_NAME "led_decly"
#define LED1_ON ~(1<<5) //低电平点亮LED
#define LED2_ON ~(1<<6)
#define LED3_ON ~(1<<7)
#define LED4_ON ~(1<<8)
#define LED1_OFF (1<<5)
#define LED2_OFF (1<<6)
#define LED3_OFF (1<<7)
#define LED4_OFF (1<<8)
#define GPBCON 0x56000010 //寄存器地址(物理地址)
#define GPBDAT 0x56000014
static volatile unsigned long *gpbcon_addr; //经过ioremap映射后的虚拟地址
static volatile unsigned long *gpbdat_addr;
static void Led_port_init(void)
{
//设置GPB5-GPB8为输出端口
*gpbcon_addr &= ~((3<<10)|(3<<12)|(3<<14)|(3<<16));
*gpbcon_addr |= (1<<10)|(1<<12)|(1<<14)|(1<<16);
//全亮
*gpbdat_addr &= LED1_ON & LED2_ON & LED3_ON & LED4_ON;
}
static void led_turn_on(unsigned int led_nu)
{
switch (led_nu)
{
case 1:
*gpbdat_addr &= LED1_ON;
break;
case 2:
*gpbdat_addr &= LED2_ON;
break;
case 3:
*gpbdat_addr &= LED3_ON;
break;
case 4:
*gpbdat_addr &= LED4_ON;
break;
default:
break;
}
}
static void led_turn_off(unsigned int led_nu)
{
switch (led_nu)
{
case 1:
*gpbdat_addr |= LED1_OFF;
break;
case 2:
*gpbdat_addr |= LED2_OFF;
break;
case 3:
*gpbdat_addr |= LED3_OFF;
break;
case 4:
*gpbdat_addr |= LED4_OFF;
break;
default:
break;
}
}
static int led_open(struct inode * inode , struct file * filp)
{
return 0;
}
static int led_release(struct inode * inode, struct file *filp)
{
return 0;
}
static int led_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret = 0;
switch (cmd)
{
case 0:
led_turn_off(arg);
break;
case 1:
led_turn_on(arg);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static const struct file_operations led_fops =
{
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
.ioctl = led_ioctl,
};
static struct miscdevice led_dev =
{
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &led_fops,
};
static int __init led_init(void)
{
int ret;
//申请IO内存,不是必须的。
if (!request_mem_region(GPBCON, 8, "leds"))
{
ret = -EBUSY;
goto request_mem_failed;
}
gpbcon_addr = ioremap(GPBCON, 4);//将物理地址映射为虚拟地址
if (NULL == gpbcon_addr)
{
ret = -EIO;
printk("gpbcon remap failed\n");
goto con_map_failed;
}
gpbdat_addr = ioremap(GPBDAT, 4);
if (NULL == gpbdat_addr)
{
ret = -EIO;
printk("gpbdat remap failed\n");
goto dat_map_failed;
}
printk("gpbcon_addr remap on %p\n", gpbcon_addr);
printk("gpbdat_addr remap on %p\n", gpbdat_addr);
Led_port_init();
ret = misc_register(&led_dev);
if (ret)
{
printk("misc_register failed\n");
goto failed;
}
printk("leds init\n");
return 0;
failed:
iounmap(gpbdat_addr);
dat_map_failed:
iounmap(gpbcon_addr);
con_map_failed:
release_mem_region(GPBCON, 8);
request_mem_failed:
return ret;
}
static void __exit led_exit(void)
{
iounmap(gpbdat_addr); //取消映射
iounmap(gpbcon_addr);
release_mem_region(GPBCON, 8); //释放I/O内存
misc_deregister(&led_dev);
printk("leds exit\n");
}
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Decly");
module_init(led_init);
module_exit(led_exit);