Linux驱动之----硬件操作

来源:互联网 发布:如何打通淘宝人工客服 编辑:程序博客网 时间:2024/05/17 02:16

这里对IO端口和IO内存做一下简单的总结:

设备通常会提供一组寄存器用来控制设备读写设备获取设备状态,即控制寄存器数据寄存器状态寄存器。

这些寄存器可能位于IO空间,也可能位于内存空间。当位于IO空间时,通常被称为IO端口,位于内存空间时,对应的内存空间被称为IO内存。

X86体系同时支持IO空间和IO内存。而ARM架构处理器则只支持IO内存。

IO内存访问流程:

------>申请IO内存:request_mem_region(start,n,name);

------>映射到虚拟地址:ioremap(unsigned long phys_addr,unsigned long size);

------>访问硬件:readb(),writeb(),readw(),writew(),readl(),writel()

------>接触映射:iounmap();

------>释放IO内存: release_mem_region()

详细解析:

1、申请IO内存资源 :

struct resource* request_mem_region(unsigned long start,unsigned long len,char* name);

参数含义:start IO内存资源的其实物理地址

len:IO内存资源所占内存地址的空间的长度

name:IO内存资源名称

成功函数返回resource结构,失败返回NULL。内核中resource结构是用来描述IO资源的,并通过resource_list结构将它们串起来。resource结构体如下:

struct resource {
resource_size_t start;//
资源的起始物理地址
resource_size_t end;//资源的结束物理地址
const char *name;//资源的名称
unsigned long flags;//描述标志
struct resource *parent, *sibling, *child;
};

2、映射IO内存到虚拟地址:

viod *ioremap(unsigned long phys_addr,unsigned long size);

参数意义: phys_addr::---->IO内存起始物理地址

size--------》映射内存的长度

成功返回指向虚拟地址的指针,失败返回NULL

3、对设备的访问,读取和写入

从给定地址读取数据、写入数据 b、w、l后缀在32位系统中分别表示读1、2、4、个字节

readb(addr);       readw(addr);    readl(addr);

addr----->制定地址

返回督导的数据

写入数据-----》向给定地址写入数据

writeb(val,addr);  writew(val,addr); writel(val,addr);

val---->写入的数值  addr--->制定地址

4、解除映射,并释放内存

iounmap(void* addr);

addr:  指向映射内存的指针


release_mem_region(unsigned long start ,unsigned long len);

start::IO内存资源起始物理地址

len:IO内存资源所占用内存地址空间的长度

贴出:led驱动的硬件操作函数:

#include<linux/module.h>
#include<linux/miscdevice.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/fs.h>
#include<linux/types.h>
#include<linux/errno.h>
#include<linux/ioctl.h>
#include<linux/cdev.h>
#include<linux/device.h>
#include<linux/ioport.h>
#include<asm/io.h>
#include<asm/uaccess.h>
/*要了解各个头文件中所包含的函数*/

#include"led.h"

/*led对应GPIOK虚拟地址*/
unsigned long GPIOK_VA_BASE;

#define GPIOK_CON0_VA GPIOK_VA_BASE
#define GPIOK_CON1_VA GPIOK_VA_BASE+0x4
#define GPIOK_DAT_VA GPIOK_VA_BASE+0x8
#define GPIO_PUD_VA GPIOK_VA_BASE+0xc

/*led对应的物理地址*/
#define GPIOK_PA_BASE 0x7f008800//定义实际的物理地址


/*linux 采用resource描述挂接在cpu总线上的结构实体
*/
struct resource tiny6410_led_resource =
{
.name = "led io-mem",
.start = GPIOK_PA_BASE,//
起始物理地址
.end = GPIOK_PA_BASE + 0x10,//结束物理地址
.flags = IORESOURCE_MEM, //
};
/*6410管脚的设置初始化*/
static void tiny6410_led_pin_setup(void)
{

/*这个函数中就是 硬件操作的详细步骤,步骤还是非常固定的

1、申请IO内存 request_mem_region()

2、映射到虚拟地址

3、然后访问IO地址的硬件

*/
unsigned long start = tiny6410_led_resource.start;
unsigned long size = tiny6410_led_resource.end - tiny6410_led_resource.start;
unsigned long tmp;

/*申请io内存*/
request_mem_region(start,size,tiny6410_led_resource.name);

/*映射io内存*/
GPIOK_VA_BASE = (unsigned long)ioremap(start,size);//映射的地址 由系统自动分配
printk("映射的虚拟地址 GPIOK_VA_BASE = 0x%lx\n",GPIOK_VA_BASE);

/*对应管教设置为输出*/
tmp = readl(GPIOK_CON0_VA);
tmp = (tmp & ((0xffffU<<16)|(0x1111U<<16)));
writel(tmp,GPIOK_CON0_VA);

/*对应管脚置高--使led全灭*/
tmp = readl(GPIOK_DAT_VA);//将寄存器在内存中的虚拟地址的数据读出来
tmp |= (0xf<<4);
writel(tmp,GPIOK_DAT_VA);
}

/****************************************************/
static void tiny6410_led_pin_release(void)
{
/**首先要解除映射**/
iounmap((void*)GPIOK_VA_BASE);//参数是 映射到内存的地址
release_mem_region(tiny6410_led_resource.start,tiny6410_led_resource.end - tiny6410_led_resource.start);
}

/**************************************************************************************************/
static unsigned long tiny6410_led_getdata(void)
{
return ((readl(GPIOK_DAT_VA)>>4)&0xF);//返回虚拟io内存中寄存器的值
}
/*设置led对应的GPIO数据寄存器的值*/
static void tiny6410_led_setdata(int data)
{
unsigned long tmp;
tmp = readl(GPIOK_DAT_VA);
tmp = ~(0xF<<4) | ((data&0xF)<<4);
writel(tmp,GPIOK_DAT_VA);
}
/*****************************************************************************************************/

static long led_ioctl(struct file* filp,unsigned int cmd,unsigned long arg)
{
int ioarg,ret;
/*检测命令的有效性*/
if(_IOC_TYPE(cmd) != LED_IOC_MAGIC)//检测命令
return -EINVAL;
if(_IOC_NR(cmd) > LED_IOC_MAXNR)//检测命令号 如果大于最大的命令号
return -EINVAL;

/*根据命令来执行不同的操作*/

switch(cmd) {
case LED_IOCGETDATA:{

ioarg = tiny6410_led_getdata();
ret = put_user(ioarg,(int*)arg);
break;
}
case LED_IOCSETDATA:{

ret = get_user(ioarg,(int*) arg);
tiny6410_led_setdata(ioarg);
break;
}
default:
return -EINVAL;
}

return ret;
}
static ssize_t led_read(struct file* filp,char __user* buf,size_t size,loff_t *ppos)
{

}

/********************************************************************************************/
static struct file_operations dev_fops =
{
.owner = THIS_MODULE,
.unlocked_ioctl = led_ioctl,
.read = led_read,
};
static struct miscdevice misc =
{
.minor = MISC_DYNAMIC_MINOR,//次设备号由系统动态分配
.name = DEVICE_NAME,
.fops = &dev_fops,//文件操作
};
/****************************************************************************************/
static int __init dev_init(void)
{
int ret;

tiny6410_led_pin_setup();
ret = misc_register(&misc);

printk("initialized minor =%d\n",misc.minor);

return ret;
}
static void __exit dev_exit(void)
{
tiny6410_led_pin_release();
misc_deregister(&misc);//混杂字符设备驱动的解除
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("kong-hua-sheng");

0 0