参考文档:https://segmentfault.com/a/1190000004474802
记录具体操作和出现的问题,以及一些扩展知识。
对于linux ,设备也被看做是一个文件,对于用户来说, 通过系统调用和驱动用操作普通文件的方式打开、读写设备,屏蔽硬件细节。
创建设备:mknod (类似创建文件节点) 这时指定一个主设备号。加载设备驱动,驱动中也有一个设备号,通过该设备号将驱动和设备对应起来。
c文件
代码转自上述参考链接,有少许改动。
#include
#include
#include
#include
#include
//int init_module(void);
//void cleanup_module(void);
static ssize_t device_read(struct file *,char *,size_t,loff_t *);
static ssize_t device_write(struct file *,const char *,size_t,loff_t *);
static int device_open(struct inode *,struct file *);
static int device_release(struct inode *,struct file *);
//name of device
#define DEVICE_NAME "ninafirst"
static int major_version;
static int device_is_open = 0;
static char msg[1024];
static char *pmsg;
//what the device is permitted to do //驱动程序最重要的就是填充这四个函数。应用程序通过设备号找到设备对应的驱动程序,在入口函数处指定了该操作结构体
//并确定其文件操作结构体,将控制权交给对应的函数指针
static struct file_operations fops={
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};
//module register function
static int __init init_chardev(void){ //设备注册函数,也就是驱动程序的入口,一般命名为init_module
//这里使用了 __init 宏
major_version = register_chrdev(0,DEVICE_NAME,&fops); //major 参数为零,系统将随机分配一个主设备号,成功返回大于零的主设备号
if(major_version < 0){
printk(KERN_ALERT "Register failed,error %d.\n",major_version);
return major_version;
}
printk(KERN_INFO "'mknod /dev/%s c %d 0'.\n",DEVICE_NAME,major_version);//提示手动创建设备文件节点,主设备号要与系统分配的主设备号一致,设备名不一定相同
//后面结果里贴图
return 0;
}
module_init(init_chardev);
static void __exit exit_chrdev(){
unregister_chrdev(major_version,DEVICE_NAME); //该函数并不真的删除设备文件,只是卸载该模块,对于上层而言,就是没有的操作设备的驱动。
}
module_exit(exit_chrdev);
static ssize_t device_read(struct file * filp,char *buffer,size_t length,loff_t *offset){ //以下就是四个操作的具体实现函数
int bytes=0;
if(*pmsg == 0){
return 0;
}
while(length && *pmsg){
put_user(*(pmsg++),buffer++);
length--;
bytes++;
}
return bytes;
}
static ssize_t device_write(struct file *filp,const char *buff,size_t length,loff_t *offset){
return -EINVAL; //ENINVAL :向函数传递了无效参数
}
static int device_open(struct inode *inode,struct file *file){
static int counter=0;
if(device_is_open){
return -EBUSY; //错误异常,存疑
}
device_is_open=1;
sprintf(msg,"Device open for %d times.\n",++counter);
pmsg = msg;
try_module_get(THIS_MODULE);
return 0;
}
static int device_release(struct inode *inode,struct file *file){
device_is_open = 0;
module_put(THIS_MODULE); //结构体struct module在内核中代表一个内核模块,
//通过insmod(实际执行init_module系统调用)把自己编写的内核模块插入内核时,
//模块便与一个 struct module结构体相关联,并成为内核的一部分。
//THIS_MODULE 宏用来引用当前模块的 struct module。引用自http://blog.csdn.net/lizhiguo0532/article/details/6346958
return 0; }
安装模块后,dmesg 查看提示,创建设备,如果设备名不一致仍然能操作成功