从头开始编写Linux字符设备驱动程序(3)(基于友好的arm tiny4412开发板)

在本节中,让我们看一下新的知识点。

这次,我们将进一步改进该字符设备的驱动程序。

首先,修改上一节中的代码:#include& lt; linux / init.h& gt; #include& lt; linux / module.h& gt; #include& lt; linux / sched.h& gt; #include& lt; linux / kernel。

h& gt; #include& lt; linux / cdev.h& gt; #include& lt; linux / fs.h& gt; #include& lt; linux / slab.h& gt; //创建字符设备struct char_dev {& nbsp; & nbsp; struct cdev c_dev; & nbsp; ; & nbsp; dev_t dev_num; & nbsp; & nbsp; char buf [1024]; & nbsp; }; int my_open(){& nbsp; & nbsp; printk(“ cdev open”);} int my_close(){  & nbsp; printk(“ cdev del”);} struct file_operations my_ops = {.open = my_open,.release = my_close,}; struct char_dev * test_dev; static int __init& nbsp; cdev_test_init(void){int ret; // 1,分配字符设备结构内存test_dev = kmalloc(sizeof(* test_dev),GFP_KERNEL); if(!test_dev){& nbsp; ret = -ENOMEM; & nbsp; goto malloc_dev_fair;} // 2,申请一个设备号并注册一个字符设备ret = alloc_chrdev_region(& test_dev-> dev_num,1,1,“ test_dev”); if(ret& lt; 0){& nbsp; goto alloc_chrdev_fair;} // 3。

初始化字符设备cdev_init(& test_dev-& gt; dev_num,& amp; my_ops); // 4,添加一个字符设备ret = cdev_add(& amp; test_dev-& gt; c_dev,test_dev-& gt; dev_num,1); if(ret& lt; 0){& nbsp; goto cdev_add_fair;} my_open();返回0; cdev_add_fair:返回ret; malloc_dev_fair:返回ret& nbsp ;; alloc_chrdev_fair:return ret;}静态int __exit cdev_test_exit(void){//删除设备cdev_del(& test_dev-& gt;); //取消注册驱动程序-& gt;表示unregister_chrdev_region(test_dev-& gt; dev_num,1)后写1; return 0;} module_init(cdev_test_init); module_exit(cdev_test_exit); MODULE_LICENSE(“ GPL”);在代码中,我们要实现一个虚拟字符设备,该设备非常简单,但是功能更加丰富。

我们首先创建一个字符设备,由结构char_dev表示。

将内存分配给该结构,然后申请设备号和寄存器,最后进行初始化,然后将此字符设备添加到内核。

一旦这些操作成功,将调用my_open函数。

这是字符设备的最基本组成。

在上一节中,我们已经讨论了alloc_chrdev_region的功能。

因此,本节具有file_operations结构,它的功能是什么?注册字符设备后,我们将立即操作该字符设备,打开& nbsp,读取,写入,关闭和其他操作。

下面的代码:struct file_operations {struct module * owner; loff_t(* llseek)(结构文件*,loff_t,int); ssize_t(* read)(结构文件*,char __user *,size_t,loff_t *); ssize_t(* write)(结构文件*,const char __user *,size_t,loff_t *); ssize_t(* aio_read)(struct kiocb *,const struct iovec *,unsigned long,loff_t); ssize_t(* aio_write)(struct kiocb *,const struct iovec *,unsigned long,loff_t); int(* readdir)(结构文件*,void *,filldir_t); unsigned int(* poll)(结构文件*,struct poll_table_struct *); long(* unlocked_ioctl)(结构文件*,unsigned int,unsigned long); long(* compat_ioctl)(结构文件*,unsigned int,unsigned long); int(* mmap)(结构文件*,struct vm_area_struct *); int(* open)(结构索引节点*,结构文件*); int(* flush)(结构文件*,fl_owner_t id); int(* release)(结构索引节点*,结构文件*); int(* fsync)(结构文件*,loff_t,loff_t,int datasync); int(* aio_fsync)(结构kiocb *,int数据同步); int(* fasync)(int,结构文件*,int); int(* lock)(结构文件*,int,struct file_lock *); ssize_t(* sendpage)(结构文件*,结构页*,int,size_t,loff_t *,int); unsigned long(* get_unmapped_area)(结构文件*,unsigned long,unsigned long,unsigned long,unsigned long); int(* check_flags)(int); int(* flock)(结构文件*,int,struct file_lock *); ssize_t(* splice_write)(struct pipe_inode_info *,struct file *,loff_t *,size_t,unsigned int); ssize_t(* splice_read)(结构文件*,loff_t *,结构pipe_inode_info *,size_t,unsigned int); int(* setlease)(结构文件*,long,结构文件锁**); long(* fallocate)(结构文件* file,int模式,loff_t偏移量,& nbsp; loff_t len); );那么内核如何识别相应的函数呢?例如,它是通过上层应用程序中的系统调用来实现的。

通过使用open()打印相应的设备,syscall函数将通过系统调用号在内核模式下标识该函数,然后调用此处实现的my_open。

这是内核模式和用户模式相互通信的方式。

在这里我不会编写相应的应用程序。

我以前写过。

我会直接把