I/O内存与端口访问硬件--led
ARM只支持内存空间
对I/O端口的操作步骤
1、I/O端口函数操作I/O
- 申请I/O端口
内核提供了一套函数来允许驱动申请它需要的I/O端口,其中核心的函数是:
Struct resource *request_mem_region(unsigned long fisrt,unsigned long n,const char *name)
这个函数告诉内核,你要使用从first开始的n个端口,name是设备的名字,如果申请成功,返回非NULL,申请失败,返回NULL。
系统中端口的分配情况记录在/proc/iomem中。
- 访问I/O端口
Linux 内核头文件(体系依赖的头文件<asm/io.h>) 定义了下列内联函数来存取I/O端口:
/* inb/outb:读/写字节端口(8位宽)。有些体系将port参数定义为unsigned long;而有些平台则将它定义为unsigned short。inb的返回类型也是依赖体系的 */
unsigned inb(unsigned port);
void outb(unsigned char byte, unsigned port);
/* inw/outw:读/写字端口(16位宽) */
unsigned inw(unsigned port);
void outw(unsigned short word, unsigned port);
/* inl/outl:读/写32位端口。longword也是依赖体系的,有的体系为unsigned long;而有的为unsigned int */
unsigned inl(unsigned port);
void outl(unsigned longword, unsigned port);
- 释放I/O端口
/* 用完I/O端口后(可能在模块卸载时),应当调用release_region将I/O端口返还给系统。参数start和n应与之前传递给request_region一致 */
void release_region(unsigned long start, unsigned long n);
流程如图所示:
2、映射为I/O内存访问I/O
首先是调用 request_mem_region()申请资源,接着将寄存器地址通过 ioremap()映射到内核空间虚拟地址, 之后就可以通过 Linux 设备访问编程接口访问这些设备的寄存器了。 访问完成后, 应对 ioremap()申请的虚拟地址进行释放,并释放 release_mem_ region()申请的 I/O 内存资源。
- 申请I/O内存
I/O 内存区在使用前必须先分配。分配内存区的函数接口在<linux/ioport.h>定义中:
/* request_mem_region分配一个开始于start,len字节的I/O内存区。分配成功,返回一个非NULL指针;否则返回NULL。系统当前所有I/O内存分配信 息都在/proc/iomem文件中列出,你分配失败时,可以看看该文件,看谁先占用了该内存区 */
struct resource *request_mem_region(unsigned long start, unsigned long len, char *name);
- 映射
/* ioremap用于将I/O内存区映射到虚拟地址。参数phys_addr为要映射的I/O内存起始地址,参数size为要映射的I/O内存的大小,返回值为被映射到的虚拟地址 */
void *ioremap(unsigned long phys_addr, unsigned long size);
- 访问
经过 ioremap之后,设备驱动就可以存取任何I/O内存地址。注意,ioremap返回的地址不可以直接解引用;相反,应当使用内核提供的访问函数。访问I/O内存的正确方式是通过一系列专门用于实现此目的的函数:
#include <asm/io.h>
/* I/O内存读函数。参数addr应当是从ioremap获得的地址(可能包含一个整型偏移); 返回值是从给定I/O内存读取到的值 */
unsigned int ioread8(void *addr);
unsigned int ioread16(void *addr);
unsigned int ioread32(void *addr);
/* I/O内存写函数。参数addr同I/O内存读函数,参数value为要写的值 */
void iowrite8(u8 value, void *addr);
void iowrite16(u16 value, void *addr);
void iowrite32(u32 value, void *addr);
/* 以下这些函数读和写一系列值到一个给定的 I/O 内存地址,从给定的buf读或写count个值到给定的addr。参数count表示要读写的数据个数,而不是字节大小 */
void ioread8_rep(void *addr, void *buf, unsigned long count);
void ioread16_rep(void *addr, void *buf, unsigned long count);
void ioread32_rep(void *addr, void *buf, unsigned long count);
void iowrite8_rep(void *addr, const void *buf, unsigned long count);
void iowrite16_rep(void *addr, const void *buf, unsigned long count);
void iowrite32_rep(void *addr,,onst void *buf,,nsigned long count);
/* 需要操作一块I/O 地址时,使用下列函数(这些函数的行为类似于它们的C库类似函数): */
void memset_io(void *addr, u8 value, unsigned int count);
void memcpy_fromio(void *dest, void *source, unsigned int count);
void memcpy_toio(void *dest, void *source, unsigned int count);
u 释放
void iounmap(void * addr); /* iounmap用于释放不再需要的映射 */
void release_mem_region(unsigned long start, unsigned long len); /* iounmap用于释放不再需要的映射 */
实例:
#include <asm/io.h>
#include <asm/sizes.h>
#include <linux/ioport.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <asm/atomic.h>
volatile unsigned long virt, phys;
volatile unsigned long *GPBCON, *GPBDAT, *GPBUP;
unsigned long reg;
struct resource *led_resource;
dev_t my_major=0;//主设备号
#define MY_DEVNAME " leds" //设备名称
//设备结构体
struct my_led {
struct cdev my_cdev;
};
struct my_led *my_ledp;
struct class *my_class;
void led_device_init(void)
{
//0x56000000=GPACON 0x56000000 + 0xd0 包揽所有的IO引脚寄存器地址
phys = 0x56000000;
//在虚拟地址空间中申请一块长度为0xd0的连续空间
//这样,物理地址phys到phys+0xd0对应虚拟地址virt到virt+0xd0
virt =(unsigned long)ioremap(phys, 0xd0);
GPBCON = (unsigned long *)(virt + 0x10);
GPBDAT = (unsigned long *)(virt + 0x14);
GPBUP = (unsigned long *)(virt + 0x18);
}
void led_configure(void)
{
reg=ioread32(GPBCON);
reg &= ~(3 << 10)&~(3<<12)&~(3 << 14)&~(3<<16);
reg |=(1 << 10)|(1<<12)|(1<<14)|(1<<16); //output
iowrite32(reg,GPBCON);
reg=ioread32(GPBUP);
reg |= (1 << 5)|(1 <<6)|(1 <<7)|(1 <<8);
iowrite32(reg,GPBUP);
}
void led_on(void)
{
//*GPBDAT &= ~(1 << 5)&~(1 << 6)&~(1 << 7)&~(1 << 8);
reg=ioread32(GPBDAT);
reg &= ~(1 << 5)&~(1 <<6)&~(1 << 7)&~(1 <<8);
iowrite32(reg,GPBDAT);
}
static int leds_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
switch(cmd) {
case 0:
case 1:
if (arg > 4)
{
return -EINVAL;
}
reg=ioread32(GPBDAT);
if(cmd==1)
reg &= ~(1 << (arg+5));
if(cmd==0)
reg |= (1 << (arg+5));
iowrite32(reg,GPBDAT);
return 0;
default:
return -EINVAL;
}
}
void led_off(void)
{
reg=ioread32(GPBDAT);
reg |= (1 << 5)|(1 << 6)|(1 << 7)|(1 << 8);
iowrite32(reg,GPBDAT);
}
static const struct file_operations my_led_fops={
.owner=THIS_MODULE,
.ioctl=leds_ioctl,
};
static int __init test_init(void)
{
dev_t devno=MKDEV(my_major,0);
int result;
led_device_init();
led_resource=request_mem_region(phys,0xd0,"LED_MEM");
if(led_resource==NULL){
printk("request mem for led error!\n");
return -ENOMEM;
}
led_configure();
led_on();
//设备号申请
if(my_major) {
result=register_chrdev_region(devno,1,MY_DEVNAME);
}
else {
result=alloc_chrdev_region(&devno,0,1,MY_DEVNAME);
my_major=MAJOR(devno);
}
if(result<0)
return result;
//设备结构体内存申请并初始化
my_ledp=kmalloc(sizeof(struct my_led),GFP_KERNEL);
if(!my_ledp) {
result=-ENOMEM;
goto fail_malloc;
}
memset(my_ledp,0,sizeof(struct my_led));
//添加led的cdev
cdev_init(&my_ledp->my_cdev,&my_led_fops);
my_ledp->my_cdev.owner=THIS_MODULE;
cdev_add(&my_ledp->my_cdev,devno,1);
//创建设备文件
my_class=class_create(THIS_MODULE,"my_class");
device_create(my_class,NULL,MKDEV(my_major,0),NULL,"my_led""%d",0);
printk("hello led!\n");
return 0;
fail_malloc:
unregister_chrdev_region(devno,1);
return 0;
}
static void __exit test_exit(void)
{
if(led_resource!=NULL){
led_off();
iounmap((void *)virt);
release_mem_region(phys,0xd0);
}
cdev_del(&my_ledp->my_cdev);
kfree(my_ledp);
unregister_chrdev_region(MKDEV(my_major,0),1);
device_destroy(my_class,MKDEV(my_major,0));
class_destroy(my_class);
printk("bye\n");
}
module_init(test_init);
module_exit(test_exit);