linux I/O内存驱动设备--学习笔记

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 shortinb的返回类型也是依赖体系的 */

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_regionI/O端口返还给系统。参数startn应与之前传递给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内存写函数。参数addrI/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的连续空间  

       //这样,物理地址physphys+0xd0对应虚拟地址virtvirt+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));    

     

       //添加ledcdev    

       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);  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值