Linux-自己动手写驱动
流程图:
代码模块化:
#include <linux/module.h>
#include <linux/init.h>
struct cdev mdev;
dev_t devno;
loff_t mem_lseek(struct file *file, loff_t ppos, int whence)
{
}
ssize_t mem_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
/*1.获得设备的基地址*/
/*2.将设备中指定位置的内容输出到用户空间中*/
/*3.移动ppos*/
}
ssize_t mem_write(struct file *file, const char __user *buf, size_t size, loff_t *ppos)
{
/*1.获得设备的基地址*/
/*2.将用户空间的数据写入到设备指定位置中*/
/*3.移动ppos*/
}
int mem_close(struct inode *inode, struct file *file)
{
}
int mem_open(struct inode *inode, struct file *file)
{
/*1.标明设备号*/
/*2.识别次设备号*/
/*3.启动设备*/
}
struct file_operations f_ops ={
.llseek = mem_lseek,
.read = mem_read,
.write = mem_write,
.release = mem_close,
.open = mem_open,
};
static int mem_init(void)
{
/*1.初始化mdev*/
/*2.分配设备号以及注册mdev*/
}
static void mem_exit(void)
{
/*1.注销设备*/
/*2.注销设备号*/
}
module_init(mem_init);
module_exit(mem_exit);
实际代码:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
struct cdev mdev;
dev_t devno;
int dev1_register[5];
int dev2_register[5];
loff_t mem_lseek(struct file *file, loff_t ppos, int whence)
{
unsigned newpos = 0;
/*1.根据whence来获得偏移量*/
switch(whence){
case SEEK_SET:
newpos = ppos;
break;
case SEEK_CUR:
newpos = file->f_pos + ppos;
break;
case SEEK_END:
newpos = 5*sizeof(int) -1 + ppos;
break;
default:
break;
}
file->f_pos = newpos;
return newpos;
}
ssize_t mem_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
unsigned p = *ppos;
/*1.获得设备的基地址*/
int *register_addr = file->private_data;
/*2.将设备中指定位置的内容输出到用户空间中*/
copy_to_user(buf, register_addr + p, size);
/*3.移动ppos*/
file->f_pos += size;
return size;
}
ssize_t mem_write(struct file *file, const char __user *buf, size_t size, loff_t *ppos)
{
unsigned p = *ppos;
/*1.获得设备的基地址*/
int *register_addr = file->private_data;
/*2.将用户空间的数据写入到设备指定位置中*/
copy_from_user(register_addr + p, buf, size);
/*3.移动ppos*/
file->f_pos += size;
return size;
}
int mem_close(struct inode *inode, struct file *file)
{
/*由于此处是虚拟设备,故返回0即可*/
return 0;
}
int mem_open(struct inode *inode, struct file *file)
{
/*1.标明设备号*/
int num = MINOR(inode->i_rdev);
/*2.识别次设备号*/
if(num == 0)
{
file->private_data = dev1_register;
}else if(num == 1)
{
file->private_data = dev2_register;
}else{
return 0;
}
/*3.启动设备,由于此处是虚拟设备,故不需要*/
return 0;
}
struct file_operations f_ops ={
.llseek = mem_lseek,
.read = mem_read,
.write = mem_write,
.release = mem_close,
.open = mem_open,
};
static int mem_init(void)
{
/*1.初始化mdev*/
cdev_init(&mdev, &f_ops);
/*2.分配设备号以及注册mdev*/
alloc_chrdev_region(&devno, 0, 2, "oujiayu_memdev");
cdev_add(&mdev, devno, 2);
return 0;
}
static void mem_exit(void)
{
/*1.注销设备*/
cdev_del(&mdev);
/*2.注销设备号*/
unregister_chrdev_region(devno, 2);
}
module_init(mem_init);
module_exit(mem_exit);
Tips:
1.struct file_operations f_ops ={.llseek = xxx,};是对结构体变量f_ops进行初始化,类似于C++中的列表初始化,其中.llseek是C99的扩展,可以使用.成员来对结构体中的指定成员进行初始化。
2.当应用程序对设备文件进行读写等操作的时候,内核会将相应的参数传入到驱动程序的相应函数中作为形参,从而实现驱动功能的实现。