目录
1. 应用程序将数据传递给驱动
1.1. 函数分析
1.2. 编写驱动.c文件
1.3. 编写编译驱动的makefile文件
1.4. 执行make命令,并安装驱动,生成设备文件
1.5. 写应用层.c文件
1.6. 执行可执行文件验证
2. 驱动操作LED灯
2.1. 函数分析
2.2. 手册和原理图
2.3. 编写驱动.c文件
2.4. 编写驱动的makefile文件
2.5. 执行make命令,生成.ko文件
2.6. 将驱动文件放到nfs同步到开发板中
2.7. 在开发板上安装此驱动
//记忆技巧:从用户角度看
》1.函数原型:int copy_from_user(void *to, const void __user *from, int n)
功能:从用户空间拷贝数据到内核空间(from user)
参数:
to:内核空间首地址
from:用户空间首地址
n:拷贝数据的长度
返回值:
成功:0
失败:未拷贝字符个数
》2.函数原型:int copy_to_user(void __user *to,const void *from,int n)
功能:从内核空间到用户空间(to user)
参数:
to:用户空间
from:内核空间
n:拷贝数据长度
返回值:
成功:0
失败:未拷贝字符个数
//应用程序和驱动程序传参
#include
#include
#include
#include
#include //添加头文件
unsigned int major =0;
#define NAME "hello"
int error_num=0;//定义变量用来判错
char kbuf[128]={0};//定义数组用来传输数据
ssize_t mycdev_read (struct file *file, char __user *ubuf, size_t size, loff_t * offs)
{
if(size>sizeof(kbuf)) //添加判断让数据不过大
{
size=sizeof(kbuf);
}
//站在用户角度,数据到用户
error_num=copy_to_user(ubuf,kbuf,size);
if(error_num)
{
printk("copy_to_user error\n");
return -1;
}
return 0;
}
ssize_t mycdev_write (struct file *file, const char __user *ubuf, size_t size, loff_t *offs)
{
if(size>sizeof(kbuf)) //添加判断让数据不过大
{
size=sizeof(kbuf);
}
//站在用户角度,用户写是数据来自用户
error_num=copy_from_user(kbuf,ubuf,size);
if(error_num)
{
printk("copy_from_user error\n");
return -1;
}
return 0;
}
int mycdev_open(struct inode *inode, struct file *file)
{
printk("hello open\n");
return 0;
}
int mycdev_release (struct inode *inode, struct file *file)
{
printk("hello close\n");
return 0;
}
struct file_operations fops={
.open=mycdev_open,
.write=mycdev_write,
.release=mycdev_release,
.read=mycdev_read,
};
static int __init hello_init(void)
{
major= register_chrdev(major, NAME, &fops);
if(major<0)
{
printk("register_chrdev error\n");
return major;
}
return 0;
}
static void __exit hello_exit(void)
{
unregister_chrdev(major, NAME);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
KERNELDIR:=/lib/modules/$(shell uname -r)/build
#KERNELDIR:=/home/hq/temp/kernel-3.4.39/
PWD:=$(shell pwd)
all:
make -C $(KERNELDIR) M=$(PWD) modules
clean:
make -C $(KERNELDIR) M=$(PWD) clean
obj-m:=hello.o
》1.执行make命令
》2.安装驱动
sudo insmod hello.ko 安装驱动命令
cat /proc/devices 查看主设备号
》3.生成设备文件
sudo mknod hello c 244 1 生成设备文件
添加设备文件权限(不能忘记)
#include
#include
#include
#include
#include
#include
char buf[128]={"my name is southernbird"};//写入数据
int main(int argc,const char *argv[])
{
int fd;
fd=open("./hello",O_RDWR);
if(fd==-1)
{
perror("open error");
return -1;
}
write(fd,buf,sizeof(buf));
memset(buf,0,sizeof(buf)); //清空
read(fd,buf,sizeof(buf));
printf("%s\n",buf);//用来验证
close(fd);
return 0;
}
使用gcc编译器进行编译,生成可执行文件
》1.使用普通文件hello的情况
》2.使用设备文件
数据从应用层到驱动,又从驱动又回到了应用层
驱动如何操作寄存器:
Led灯的寄存器是物理地址
Linux内核启动之后,操作的全是虚拟地址
这就牵扯到了虚拟地址和物理地址之间的转换
》1.函数原型:void * ioremap(phys_addr_t offset, unsigned long size)
功能:将物理地址映射成虚拟地址
参数:
offset:要映射的地址
size:间下方解释
返回值:
成功:返回虚拟地址
失败:NULL
》2.函数原型:void iounmap(void *addr)
功能:将映射取消
参数:虚拟地址
返回值:
无
1个为4字节,大小就应该写36
红灯引脚
绿灯引脚
蓝灯引脚
想要让灯灭,将三个引脚输出低电平就可
#include
#include
#include
#include
#include
#include //添加内存映射的函数
unsigned int major =0;
#define NAME "hello"
int error_num=0;
char kbuf[128]={0};
//定义三个灯的基地址
#define RED_BASE 0XC001A000
#define BLUE_BASE 0XC001B000
#define GREEN_BASE 0XC001E000
//定义三个指针用来存放映射后的虚拟地址
unsigned int * red_base=NULL;
unsigned int * blue_base=NULL;
unsigned int * green_base=NULL;
ssize_t mycdev_read (struct file *file, char __user *ubuf, size_t size, loff_t * offs)
{
if(size>sizeof(kbuf))
{
size=sizeof(kbuf);
}
error_num=copy_to_user(ubuf,kbuf,size);
if(error_num)
{
printk("copy_to_user error\n");
return -1;
}
return 0;
}
ssize_t mycdev_write (struct file *file, const char __user *ubuf, size_t size, loff_t *offs)
{
if(size>sizeof(kbuf))
{
size=sizeof(kbuf);
}
error_num=copy_from_user(kbuf,ubuf,size);
if(error_num)
{
printk("copy_from_user error\n");
return -1;
}
return 0;
}
int mycdev_open(struct inode *inode, struct file *file)
{
printk("hello open\n");
return 0;
}
int mycdev_release (struct inode *inode, struct file *file)
{
printk("hello close\n");
return 0;
}
struct file_operations fops={
.open=mycdev_open,
.write=mycdev_write,
.release=mycdev_release,
.read=mycdev_read,
};
static int __init hello_init(void)
{
major= register_chrdev(major, NAME, &fops);
if(major<0)
{
printk("register_chrdev error\n");
return major;
}
//在入口处将物理地址映射成虚拟地址
red_base=ioremap(RED_BASE, 36);//大小最大就是36
if(red_base ==NULL)
{
printk("ioremap error RED_BASE\n");
return -ENOMEM; //返回内存溢出
}
//注意解引用
*red_base&=~(1<<28); //灭红灯
*(red_base+1)|=(1<<28);//设置为输出模式
*(red_base+9)&=~(3<<24);//选择function 0
green_base=ioremap(GREEN_BASE,36);
if(green_base==NULL)
{
printk("ioremap error GREEN_BASE\n");
return -ENOMEM;
}
*green_base&=~(1<<13);//灭绿灯
*(green_base+1)|=(1<<13);//设置为输出模式
*(green_base+8)&=~(3<<26);//选择function 0
blue_base=ioremap(BLUE_BASE,36);
if(blue_base==NULL)
{
printk("ioremap error BLUE_BASE\n");
return -ENOMEM;
}
*blue_base&=~(1<<12);//灭蓝灯
*(blue_base+1)|=(1<<12);//设置为输出模式
*(blue_base+8)|=(1<<25);//将25位置为1
*(blue_base+8)&=~(1<<24);//将24位置为0
return 0;
}
static void __exit hello_exit(void)
{
//注销内存
iounmap(blue_base);//先注册的后注销
iounmap(green_base);
iounmap(red_base);
unregister_chrdev(major, NAME);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
因为驱动是基于开发板写的,编译的时候使用开发板内核
#KERNELDIR:=/lib/modules/$(shell uname -r)/build
KERNELDIR:=/home/hq/temp/kernel-3.4.39/
PWD:=$(shell pwd)
all:
make -C $(KERNELDIR) M=$(PWD) modules
clean:
make -C $(KERNELDIR) M=$(PWD) clean
obj-m:=hello.o
结合我驱动系统移植篇的博客可以知道我的nfs搭建在
/opt/6818/rootfs/rootfs下
将hello.ko放到此文件下
下面是观测的现象