赞
踩
若该文为原创文章,转载请注明原文出处
本文章博客地址:https://hpzwl.blog.csdn.net/article/details/134561660
红胖子网络科技博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中…
上一篇:《Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo》
下一篇:《Linux驱动开发笔记(六):用户层与内核层进行数据传递的原理和Demo》
驱动写好后,用户层使用系统函数调用操作相关驱动从而实现与系统内核的关联,本篇主要就是理解清楚驱动如何让用户编程来实现与内核的交互。
cd /usr/src/linux-headers-4.18.0-15
vi include/linux/fs.h
搜索到(vi则直接使用“/”):
struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t (*read_iter) (struct kiocb *, struct iov_iter *); ssize_t (*write_iter) (struct kiocb *, struct iov_iter *); int (*iterate) (struct file *, struct dir_context *); int (*iterate_shared) (struct file *, struct dir_context *); __poll_t (*poll) (struct file *, struct poll_table_struct *); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); unsigned long mmap_supported_flags; int (*open) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, loff_t, loff_t, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); int (*setfl)(struct file *, unsigned long); int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); ssize_t (*copy_file_range)(struct file *, loff_t, struct file *, loff_t, size_t, unsigned int); int (*setlease)(struct file *, long, struct file_lock **, void **); long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len); void (*show_fdinfo)(struct seq_file *m, struct file *f); #ifndef CONFIG_MMU unsigned (*mmap_capabilities)(struct file *); #endif int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t, u64); ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *, u64); } __randomize_layout;
例如read函数,那么就是打开驱动使用系统read,打开这个设备驱动的句柄,那么就会调用read函数,其他的以此类推,还比较好理解。
Linux一切都是文件,都有对应的打开、关闭和读写等相关操作,而这些操作都是使用打开文件后的句柄来表示,那么函数再根据句柄的类型,如打开的是杂项设备驱动,就会去调用杂项设备操作文件字符集里面对应的函数来执行操作了。
在编程的时候会使用open打开一个设备节点(可以是文件打开,可以打开设备),这时候返回得到设备节点句柄标识fd(失败是-1),然后使用fd去read、write等各种操作则会相当于调用这个设备驱动里面文件操作集的read、write。
下面是常用的文件操作。
int (*open) (struct inode *, struct file *);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *)
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
__poll_t (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
int (*release) (struct inode *, struct file *);
首先复制之前的registerMiscDev的驱动,改个名字为:testFileOpts:
cd ~/work/drive
cp -arf registerMiscDev testFileOpts
cd testFileOpts
make clean
mv registerMiscDev testFileOpts.c

然后修改makefile里面的(obj-m模块名称改下),模板准备好了
gedit Makefile

下面基于testFileOpts.c文件进行注册杂项设备,修改.c文件:
gedit testFileOpts.c

#include <linux/init.h> #include <linux/module.h> #include <linux/miscdevice.h> #include <linux/fs.h> struct file_operations misc_fops = { .owner = THIS_MODULE, }; struct miscdevice misc_dev = { .minor = MISC_DYNAMIC_MINOR, // 这个宏是动态分配次设备号,避免冲突 .name = "register_hongPangZi_testFileOpt", // 设备节点名称 .fops = &misc_fops, // 这个变量记住,自己起的,步骤二使用 }; static int registerMiscDev_init(void) { int ret; // 在内核里面无法使用基础c库printf,需要使用内核库printk printk("Hello, I’m hongPangZi, registerMiscDev_init\n"); ret = misc_register(&misc_dev); if(ret < 0) { printk("Failed to misc_register(&misc_dev)\n"); return -1; } return 0; } static void registerMiscDev_exit(void) { misc_deregister(&misc_dev); printk("bye-bye!!!\n"); } MODULE_LICENSE("GPL"); module_init(registerMiscDev_init); module_exit(registerMiscDev_exit);
注意,要是调用的函数没有写,则不会报错也不会有其他操作反应,所以并不是所有函数都是必须写的。
// int (*open) (struct inode *, struct file *);
int misc_open(struct inode * pInode, struct file * pFile)
{
printk("int misc_open(struct inode * pInode, struct file * pFile)");
return 0;
}


先编译试试:

然后加载驱动:
sudo insmod tesFileOpts.ko



这时候,设备节点注册成功了。
本步骤是c语言编程,使用linux系统函数打开设备节点:
新建文件:
vi test.c
输入代码:
#include <stdio.h> #include <unistd.h> #include <fcntl.h> int main(int argc, char **argv) { int fd; const char devPath[] = "/dev/register_hongPangZi_testFileOpt"; fd = open(devPath, O_RDWR); if(fd < 0) { printf("fialed to open %s\n", devPath); return -1; } else{ printf("Succeed to open %s\n", devPath); } return 0; }

编译:
gcc test.c

默认输出就是a.out,下面运行一下:

无法运行,是因为ubuntu对设备需要管理员权限,管理员权限运行:

查看内核打印输出(这里出现没有打印输出,查看“入坑一”):

至此,从用户编程层如何对设备结点,然后调用到内核层函数就基本清楚了。
#include <linux/init.h> #include <linux/module.h> #include <linux/miscdevice.h> #include <linux/fs.h> // int (*open) (struct inode *, struct file *); int misc_open(struct inode * pInode, struct file * pFile) { printk("int misc_open(struct inode * pInode, struct file * pFile\n)"); return 0; } // int (*release) (struct inode *, struct file *); int misc_release(struct inode * pInde, struct file * pFile) { printk("int misc_release(struct inode * pInde, struct file * pFile\n)"); return 0; } // ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t misc_read(struct file * pFile, char __user * pUser, size_t size, loff_t *pLofft) { printk("ssize_t misc_read(struct file * pFile, char __user * pUser, size_t size, loff_t *pLofft)\n"); return 0; } // ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t misc_write(struct file * pFile, const char __user * pUser, size_t size, loff_t *pLofft) { printk("ssize_t misc_write(struct file * pFile, const char __user * pUser, size_t size, loff_t *pLofft)\n"); return 0; } struct file_operations misc_fops = { .owner = THIS_MODULE, .open = misc_open, .release = misc_release, .read = misc_read, .write = misc_write, }; struct miscdevice misc_dev = { .minor = MISC_DYNAMIC_MINOR, // 这个宏是动态分配次设备号,避免冲突 .name = "register_hongPangZi_testFileOpt", // 设备节点名称 .fops = &misc_fops, // 这个变量记住,自己起的,步骤二使用 }; static int registerMiscDev_init(void) { int ret; // 在内核里面无法使用基础c库printf,需要使用内核库printk printk("Hello, I’m hongPangZi, registerMiscDev_init\n"); ret = misc_register(&misc_dev); if(ret < 0) { printk("Failed to misc_register(&misc_dev)\n"); return -1; } return 0; } static void registerMiscDev_exit(void) { misc_deregister(&misc_dev); printk("bye-bye!!!\n"); } MODULE_LICENSE("GPL"); module_init(registerMiscDev_init); module_exit(registerMiscDev_exit);
#include <stdio.h> #include <unistd.h> #include <fcntl.h> int main(int argc, char **argv) { int fd; char buf[32] = {0}; const char devPath[] = "/dev/register_hongPangZi_testFileOpt"; fd = open(devPath, O_RDWR); if(fd < 0) { printf("Failed to open %s\n", devPath); return -1; }else{ printf("Succeed to open %s\n", devPath); } read(fd, buf, sizeof(buf)); write(fd, buf, sizeof(buf)); close(fd); printf("exit\n"); fd = -1; return 0; }

程序打开设别节点,未打印open函数
打开函数没有赋值给文件操作集。




研究了dmesg,就是没出来,这个不清楚了,后来问了驱动大佬,提醒是可能是换行的问题,后加上可以了。


上一篇:《Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo》
下一篇:《Linux驱动开发笔记(六):用户层与内核层进行数据传递的原理和Demo》
本文章博客地址:https://hpzwl.blog.csdn.net/article/details/134561660
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。