上次我们搭了一个驱动的框架,并且使用了文件接口,去控制驱动。但是我们只是搭了一个框架,申明都没有做。
一、应用控制内核(与内核实现数据交互) 1)交互原理 我们将实现在应用层的用户空间去控制内核空间,并且通过内核空间去控制硬件。这就是一个简单的驱动 我们将做一个led点灯的实验(学软硬件结合(单片机,嵌入式,物联网),点灯实验就跟helloworld一样,虽然很基础,但是你可以有无数个方法去实现)。 我们在创建设备号的时候,的这个函数就为我们提供了一个用文件控制的接口。 这个my_opes是一个文件控制结构体,一切皆文件,当我们创建了一个设备节点的时候,其实就是在磁盘的DEV目录下挂载了一个文件,那么,它挂载了这个文件它就有一个独有的文件描述符, 在应用层我们就可以用OPEN 去打开对应的/dev/“XXX”,你创建节点的时候起的什么名字,它也会创建的挂载一个一样名字的文件XXX。open的时候就会得到它独有的文件描述符,当你对通过文件描述符去文件操作的时候,文件结构控制结构体的对应函数就会操作。 EG:我在应用程序写了一个READ操作,那么,内核就会运行,结构体的write,并执行这个函数。 所以我们可以在通过,文件从应用层控制内核,实现数据交互。 2)交互实现 通过copy_to_user(函数把内核数据传递给用户) read操作 通过copy_from_user(把数据从用户写打内核)write操作 //利用文件描述符,和各自的buff缓冲区完成/(缓冲区可以之定义,随便用数组,指针,。。。。都可以)。
应用程序需要传递数据给驱动
int copy_to_user(void __user * to, const void * from, unsigned long n)
//将数据从内核空间拷贝到用户空间,一般是在驱动中chr_drv_read()用
参数1:应用驱动中的一个buffer
参数2:内核空间到一个buffer
参数3:个数
返回值:大于0,表示出错,剩下多少个没有拷贝成功
等于0,表示正确
int copy_from_user(void * to, const void __user * from, unsigned long n)
//将数据从用户空间拷贝到内核空间,一般是在驱动中chr_drv_write()用
参数1:内核驱动中的一个buffer
参数2:应用空间到一个buffer
参数3:个数
——————————————————————————————————————————————————————
二、内核控制底层硬件。 之前我们学过ARM的裸机驱动。 我们通过芯片手册通过物理地址,去控制寄存器然后实现我们想要的操作。 我们用内核控制底层硬件其实也差不多,但是我们控制的不是物理地址,而是虚拟地址。 这个虚拟地址是实时的,你想把一段物理地址赋给虚拟地址的时候就赋,不想的时候呢,就解除。
所以我们要用内核去控制LED灯 就先要知道你的物理地址 然后用函数把物理地址转化成虚拟地址,进行控制 控制外设,其实就是控制地址,内核驱动中是通过虚拟地址操作
void *ioremap(cookie, size)
参数1: 物理地址
参数2: 长度
返回值: 虚拟地址
去映射--解除映射
void iounmap(void __iomem *addr)
参数1: 映射之后到虚拟地址
——————————————————————————————————————
代码:
内核模块代码
/*********** 头文件***********/
#include<linux/init.h>
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/device.h>
#include<asm/uaccess.h>
#include<asm/io.h>
static unsigned int kernel_val = 0;
//物理地址
#define GPX2_CON 0x11000C40
#define GPX2_SIZE 8
//虚拟地址指针
volatile unsigned long *gpx2conf;
volatile unsigned long *gpx2dat;
static unsigned int my_dev = 256; //主设备号
static struct class *devcls; //返回的类指针 DEVCLS = DEV CLASS
static struct device *dev; //返回的设备指针
ssize_t chr_dev_read (struct file *filp, char __user *buff, size_t count, loff_t *fops)
{
int ret;
printk("-------%s-------",__FUNCTION__);
if((ret = copy_to_user(buff, &kernel_val, count)) > 0){
printk("travel failed\n");
return -EFAULT;}
return 0;
}
ssize_t chr_dev_write (struct file *filp,char __user *buff, size_t count, loff_t *fops)
{
int ret;
printk("-------%s-------",__FUNCTION__);
if((ret = copy_from_user(&kernel_val, buff, count)) == 0){
printk("user read:%d\n",kernel_val);}
else{
printk("travel failed\n");
return -EFAULT;}
if(kernel_val > 0){
*gpx2dat = *gpx2dat | (0x1 << 7);
}else{
*gpx2dat = *gpx2dat & (~(1 << 7));}
return 0;
}
int chr_dev_open (struct inode *inode, struct file *filp)
{
printk("-------%s-------",__FUNCTION__);
return 0;
}
int chr_dev_close(struct inode *inode, struct file *filp)
{
printk("-------%s-------",__FUNCTION__);
return 0;
}
//文件操作结构体
const struct file_operations my_fops ={
.read = chr_dev_read,
.write = chr_dev_write,
.open = chr_dev_open,
.release = chr_dev_close,
};
/**********模块装载和卸载入口的实现********/
static int chr_drv_init (void){
int ret;
//申请设备号
ret = register_chrdev(my_dev, "led",&my_fops);
if( ret == 0){
printk("resgister ok!!\n");}
else{
printk("resgister failed!!\n");
return -EFAULT;}//申请设备节点
//创建类
devcls = class_create(THIS_MODULE,"chr_clas");
//申请节点
dev = device_create(devcls, NULL,MKDEV(my_dev, 0), NULL, "LED");
//对地址进行映射
gpx2conf = ioremap(GPX2_CON,GPX2_SIZE);
gpx2dat = gpx2conf + 1; //DTA寄存器首地址
//GPX1 INIT 设置为输出功能
*gpx2conf = *gpx2conf & (~(0xf << 28)) | (0x1 << 28);
return 0;
}
static void chr_drv_exit(void){
device_destroy(devcls,MKDEV(my_dev, 0));
class_destroy(devcls); //销毁类
unregister_chrdev(my_dev, "led"); //销毁设备号
}
/********模块装载和卸载入口的申明*************/
module_init(chr_drv_init);
module_exit(chr_drv_exit);
/***********GPL申明**********************/
MODULE_LICENSE("GPL");
————————————————(应用层代码)
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main(int argc, const char *argv[])
{
int fd;
int vaule = 0;
if((fd = open("/dev/LED",O_RDWR)) < 0){
perror("open");
exit(0);}
while(1){
printf("input 0 or 1\n");
scanf("%d",&vaule);
write(fd,&vaule,4);
read(fd,&vaule,4);
printf("vaule = %d\n",vaule);
//sleep(5);
/*
vaule = 0;
write(fd, &vaule , 4);
sleep(2);
vaule = 1;
write(fd, &vaule , 4);
sleep(2);
*/
}
close(fd);
return 0;
}
|