1、实验程序
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/io.h>
#define CHRDEVBASE_MAJOR 200 //主设备号
#define CHRDEVBASE_NAME "CHRDEVBASE" //字符设备名称
static char readbuf[100];//读缓冲区
static char writebuf[100];//写缓冲区
static char kernel_data[]={"kernel data!"};//将用户函数读到的内容进行写死
/*
* @description : 打开设备
* @param - inode : 传递给驱动的inode
* @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
* 一般在open的时候将private_data指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int chrdevbase_open(struct inode *inode, struct file *filp)
{
printk("chrdevbase open!\r\n");
return 0;
}
/*
* @description : 从设备读取数据
* @param - filp : 要打开的设备文件(文件描述符)
* @param - buf : 返回给用户空间的数据缓冲区
* @param - cnt : 要读取的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 读取的字节数,如果为负值,表示读取失败
*/
static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue = 0;
memcpy(readbuf,kernel_data,sizeof(kernel_data));//完成字符串、数据的拷贝;将kernel_data的数据拷贝到readbuf,操作的字节大小为sizeof(kernel_data)。
retvalue=copy_to_user(buf,readbuf,cnt);
printk("read success\r\n");
if(retvalue<=0)
{
printk("read success\r\n");
}else{
printk("read failed\r\n");
}
printk("chrdevbase read!\r\n");
return 0;
}
/*
* @description : 向设备写数据
* @param - filp : 设备文件,表示打开的文件描述符
* @param - buf : 要写给设备写入的数据
* @param - cnt : 要写入的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 写入的字节数,如果为负值,表示写入失败
*/
static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue = 0;
retvalue= copy_from_user(writebuf,buf,cnt);
if(retvalue<=0)
{
printk(" success\r\n");
printk("内核收到数据:%s\r\n",writebuf);
}else{
printk("write failed\r\n");
}
printk("chrdevbase write!\r\n");
return 0;
}
/*
* @description : 关闭/释放设备
* @param - filp : 要关闭的设备文件(文件描述符)
* @return : 0 成功;其他 失败
*/
static int chrdevbase_release(struct inode *inode, struct file *filp)
{
//printk("chrdevbase release!\r\n");
return 0;
}
/*字符设备操作集合*/
static struct file_operations chrdevbase_ops={
.owner=THIS_MODULE,
.open=chrdevbase_open,
.release=chrdevbase_release,
.read=chrdevbase_read,
.write=chrdevbase_write,
};
/*驱动入口函数*/
static int __init chrdevbase_init(void)
{
int ret=0;
printk("init\n");
ret=register_chrdev(CHRDEVBASE_MAJOR,CHRDEVBASE_NAME,&chrdevbase_ops);
if(ret<0){
printk("register_chrdev failed\r\n");
}
else{
printk("register_chrdev success\r\n");
}
return 0;
}
/*驱动出口函数*/
static void __exit chrdevbase_exit(void)
{
unregister_chrdev(CHRDEVBASE_MAJOR,CHRDEVBASE_NAME);
printk("exit\n");
}
/*模块的加载与卸载*/
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);
MODULE_LICENSE("GPL");//添加模块 LICENSE 信息
MODULE_AUTHOR("liuchuanqiang");//添加模块作者信息
2、程序说明
1、static int __init chrdevbase_init(void)是驱动入口函数,当调用函数module_init(chrdevbase_init);加载模块时,chrdevbase_init驱动入口函数会执行,完成字符设备的注册。驱动入口函数与驱动出口函数中的__init 与 __exit 是修饰符,用来修饰具体的入口函数与出口函数。
2、驱动入口函数调用register_chrdev(CHRDEVBASE_MAJOR,CHRDEVBASE_NAME,&chrdevbase_ops);函数进行注册字符设备,其中CHRDEVBASE_MAJOR是主设备号(在根文件系统下挂载节点时要注意保持一致),CHRDEVBASE_NAME是设备名,&chrdevbase_ops是字符设备操作集合,其类型是struct file_operations,初始化其中的open、 release、read 和 write 等具体的设备操作函数。
3、驱动出口函数调用unregister_chrdev(CHRDEVBASE_MAJOR,CHRDEVBASE_NAME);来注销字符设备。
4、驱动程序中的操作函数中的读写操作与应用程序的读写操作是对应关系。读指的是读取设备的字符;写指的是向设备中写入字符。
5、static inline long copy_to_user(void __user *to, const void *from, unsigned long n)
参数 to 表示目的,参数 from 表示源,参数 n 表示要复制的数据长度。如果复制成功,返回值为 0,如果复制失败则返回负数。copy_to_user(buf,readbuf,cnt)(读)表示将readbuf中的数据复制到buf中,之所以这样做,是因为内核空间不能直接操作用户空间的内存,因此需要借助 copy_to_user 函数来完成内核空间的数据到用户空间的复制。
6、copy_from_user(writebuf,buf,cnt)(写);将 buf 中的数据复制到写缓冲区 writebuf 中,因为用户空间内存不能直接访问内核空间的内存,所以需要借助函数 copy_from_user 将用户空间的数据复制到 writebuf 这个内核空间中。