📄 device.c
字号:
/*c_device.c*/
/* 一些必要的头文件 */
#include <linux/kernel.h>
#include <linux/module.h>
#if CONFIG_MODVERSIONS==1
#define MODVERSIONS
#include <linux/modversions.h>
#endif
/* 针对字符设备的头文件 */
#include <linux/fs.h>
#include <linux/wrapper.h>
/* 对不同版本的兼容性问题*/
#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c))
#endif
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,0)
#include <asm/uaccess.h> /* 调用copy_to_user需要的头文件 */
#endif
#define SUCCESS 0
/* 声明设备 */
/* 定义本设备的名字,它将出现在 /proc/devices 中 */
#define DEVICE_NAME "char_my_device"
/* 定义此设备消息缓冲的最大长度 */
#define BUF_LEN 100
/* 为了防止不同的进程在同一个时间使用此设备,定义此静态变量跟踪其状态 */
static int Device_Open=0;
/* 当提出请求的时候,设备将读写的内容放在下面的数组中 */
static char Message[BUF_LEN];
/* 在进程读取这个内容的时候,这个指针是指向读取的位置 */
static char *Read_Ptr;
/* 在这个文件中,主设备号作为全局变量以便于这个设备在注册和释放的时候使用 */
static char *Write_Ptr;
static int Major;
/* open()函数 */
/* 功能:无论一个进程何时试图打开这个设备都会调用这个函数 */
static int device_open(struct inode *inode,struct file *file)
{
static int counter = 0;
#ifdef DEBUG
printk("device_open(%p,%p)\n",inode,file);
#endif
printk("Device: %d.%d\n",inode->i_rdev>>8,inode->i_rdev & 0xFF);
/* 此设备为独占设备,定义一个变量防止同时有两个进程使用该设备 */
if (Device_Open)
return -EBUSY;
Device_Open ++;
printk("You have opened the device successfully!\n");
/* 下面为初始化消息,注意不要使读写内容的长度超出缓冲区长度BUF_LEN,特别是运行在内核模式时,否则若出现缓冲区上溢则可能导致系统崩溃 */
sprintf(Message,"If I told you once, I told you %d times - %s",counter ++,"Hello,world!\n");
Read_Ptr = Write_Ptr = Message;
printk(Message,"\n");
/* 当这个文件被打开的时候,我们必须确认该模块还没有被移走并且增加此模块的用户数目(在移走一个模块的时候会根据这个数字去决定可否移去,如果不是0则表明还有进程正在使用这个模块,不能移走 */
MOD_INC_USE_COUNT;
return SUCCESS;
}
/* release()函数 */
/* 功能:当一个进程试图关闭这个设备特殊文件的时候调用这个函数 */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
static int device_release(struct inode *inode,struct file *file)
#else
static void device_release(struct inode *inode,struct file *file)
#endif
{
#ifdef DEBUG
printk("device_release(%p,%p)\n",inode,file);
#endif
/* 为下一个准备使用此设备的进程做准备 */
Device_Open --;
/* 减少这个模块使用者的数目,否则一旦打开这个模块以后,永远都不能释放掉它 */
MOD_DEC_USE_COUNT;
printk("You have close the device successfully!\nGood bye!\n");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
return 0;
#endif
}
/* read()函数 */
/* 功能:当一个进程已经打开此设备文件以后并且试图去读它的时候调用这个函数*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
static ssize_t device_read(struct file *file,
char *buffer, /* 把读出的数据放到这个缓冲区 */
size_t length, /* 缓冲区的长度 */
loff_t *offset) /* 文件中的偏移 */
#else
static int device_read(struct inode *inode,
struct file *file,
char *buffer,
int length)
#endif
{
/*实际上读出的字节数*/
int bytes_read = 0;
/*如果读到缓冲区的末尾,则返回0,类似文件的结束*/
if (*Read_Ptr == 0)
return 0;
/*将数据放入缓冲区中*/
while(length && *Read_Ptr){
/*由于缓冲区是在用户空间而不是内核空间,所以必须用copy_to_user()函数将内核空间中的数据拷贝到用户空间 */
copy_to_user(buffer ++,Read_Ptr ++,length --);
bytes_read ++;
}
#ifdef DEBUG
printk("Read %d bytes, %d left\n",bytes_read,length);
#endif
return bytes_read;
}
/* write()函数 */
/* 功能:当试图将数据写入这个设备文件的时候,这个函数被调用 */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
static ssize_t device_write(struct file *file,
const char *buffer,
size_t length,
loff_t *offset)
#else
static int device_write(struct inode *inode,
struct file *file,
const char *buffer,
int length)
#endif
{
int bytes_write = 0;
#ifdef DEBUG
printk("device_write(%p,%s,%d)",file,buffer,length);
#endif
if (*buffer == 0)
return 0;
while(length && *buffer){
copy_from_user(Write_Ptr ++,buffer ++,length --);
bytes_write ++;
}
#ifdef DEBUG
printk("Write %d bytes, %d left\n",bytes_write,length);
#endif
/*返回写入的字节数*/
return bytes_write;
}
/* 一个进程试图对该设备进行操作的时候就利用下面这个结构,该结构即提供给操作系统的接口,它的指针保存在设备表中,在init_module()中被传递给操作系统 */
struct file_operations Fops = {
read: device_read,
write: device_write,
open: device_open,
release: device_release
};
/* init_module()函数*/
/* 功能:初始化这个模块--注册该字符设备。init_module()函数调用register_chrdev(),把设备驱动程序添加到内核的字符设备驱动程序表中,它返回这个驱动程序所使用的主设备号*/
int init_module()
{
/* 试图注册设备 */
Major = register_chrdev(0,DEVICE_NAME,&Fops);
/* 注册失败返回负值 */
if (Major < 0){
printk("%s device failed with %d\n","Sorry,registering the character",Major);
return Major;
}
/*注册成功时显示初始化信息*/
printk("%s The major device number is %d.\n","Registeration is a success.",Major);
printk("The author for the program development is:\n");
printk("zhengzhangxiao : 012002013324\n");
printk("Welcome to use my char device!\n");
return 0;
}
/* cleanup_module()函数*/
/*功能:卸载模块,主要是从 /proc 中取消注册的设备特殊文件*/
void cleanup_module()
{
int ret;
/*取消注册的设备*/
ret = unregister_chrdev(Major,DEVICE_NAME);
/*如果出错则显示出错信息*/
if (ret < 0)
printk("Error in unregister_chrdev: %d\n",ret);
else
printk("You have unloaded the device successfully!");
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -