1、以模块的方式生成设备节点,不需要烧写镜像
2、生成的设备节点在ls /dev/下,可以供上层应用程序打开使用。相当于提供一个访问内核模块的一个接口。对于常见的open函数,操作一个节点设备 /dev/led0,可以按照下面这样,通过这样的方式就可以调用到模块中定义的file ops接口。
open("/dev/led0", O_RDWR); // 有点类似window下的文件路径,但是led0是一个内核模块设备
0、查看设备号:
设备分主设备号和次设备号,主设备号用于区分一大类设备,此设备号区分大类设备号下的不同设备。
cat /proc/device 查看主设备号;
cat /proc/misc 查看杂项设备的此设备号;
1、杂项设备:可以节省主设备节点(主设备节点有限255),驱动编写也相对简单,并且是必须要编译的;
2、头文件:include/linux/miscdevice.h
3、涉及到的结构体:struct miscdevice 注册的是杂项设备
struct miscdevice { int minor; // 设备号,一般随机取值,系统分配 const char *name; // 生成设备节点的名称,随便取 const struct file_operations *fops; // 设备结点文件 struct list_head list; struct device *parent; struct device *this_device; const char *nodename; mode_t mode; };
4、注册函数、卸载函数;杂项设备注册与卸载
extern int misc_register(struct miscdevice * misc); extern int misc_deregister(struct miscdevice *misc);
5、上面需要注册结构体中所包含的结构体 const struct file_operations *fops; // 设备结点文件,操作该结构体中的函数,进而读写相应的设备
对外界提供访问该设备的接口;需要自己实现函数功能;
上层应用对接该结构体的函数接口;
在头文件include/linux/miscdevice.h
const struct file_operations *i_fop; 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 (*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) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); ... // 包含非常多,只列举常用的
6、常用的结构体变量:
.owner = THIS_MODULE // 使用宏定义 .open .release .unlocked_ioctl() 对GPIO的操作 应用向节点串参数
7、实验代码devicenode_linux_module.c,生成设备结点 hello_ctl123
1 // 驱动注册所需头文件 包含结构体,注册和卸载所需的函数 2 #include <linux/platform_device.h> 3 #include <linux/module.h> 4 #include <linux/init.h> 5 #include <linux/miscdevice.h> // 注册杂项设备头文件 6 #include <linux/fs.h> // 设备结点文件 7 8 #define DRIVER_NAME "hello_ctl" 9 #define DEVICE_NAME "hello_ctl123" 10 11 12 MODULE_LICENSE("Dual BSD/GPL"); 13 MODULE_AUTHOR("TOPEET"); 14 15 static int hello_open(struct inode *node, struct file *f) 16 { 17 printk(KERN_EMERG "\t func hello open\n"); 18 return 0; 19 } 20 21 static int hello_release(struct inode *node, struct file *f) 22 { 23 printk(KERN_EMERG "\t hello release\n"); 24 return 0; 25 } 26 27 static long hello_ioctl(struct file *f, unsigned int cmd, unsigned long arg) 28 { 29 printk(KERN_EMERG "\t hello ioctl\n"); 30 printk("hello_ioctl cmd is %d ...arg is %d\n",cmd,arg); 31 return 0; 32 } 33 34 // 文件操作,对应生成的设备结点具体如何操作 35 static struct file_operations hello_ops = { 36 .owner = THIS_MODULE, 37 .open = hello_open, 38 // int (*open) (struct inode *, struct file *); 39 .release = hello_release, 40 // int (*release) (struct inode *, struct file *); 41 .unlocked_ioctl = hello_ioctl, 42 // long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long); 43 44 }; 45 46 static struct miscdevice hello_dev = { 47 .minor = MISC_DYNAMIC_MINOR, // 自动分配ID 48 .name = DEVICE_NAME, // 设备结点名称 49 .fops = &hello_ops, // 结构体 50 }; 51 52 // int (*probe)(struct platform_device *); 53 // 初始化相关动作 54 static int hello_probe(struct platform_device *p) 55 { 56 printk(KERN_EMERG "\t initialized\n"); 57 misc_register(&hello_dev); //2、 杂项设备注册 58 return 0; 59 } 60 // int (*remove)(struct platform_device *); 61 static int hello_remove(struct platform_device *p) 62 { 63 printk(KERN_EMERG "\t hello_ct123 removed\n"); 64 misc_deregister(&hello_dev); 65 return 0; 66 } 67 // int (*suspend)(struct platform_device *, pm_message_t state); 68 static int hello_suspend(struct platform_device *p, pm_message_t state) 69 { 70 return 0; 71 } 72 // int (*resume)(struct platform_device *); 73 static int hello_resume(struct platform_device *p) 74 { 75 return 0; 76 } 77 // void (*shutdown)(struct platform_device *); 78 static void hello_shutdown(struct platform_device *p) 79 { 80 81 } 82 83 // 3、结构体数据初始化 84 // 注册驱动传入的结构体 85 struct platform_driver hello_driver = { 86 .probe = hello_probe, // 用于初始化模块 87 .remove = hello_remove, // 移除模块时,执行的动作 88 .suspend = hello_suspend,// 模块挂起时,执行的动作 89 .resume = hello_resume, // 挂起的模块,恢复运行执行动作 90 .shutdown = hello_shutdown, 91 .driver = { 92 .name = DRIVER_NAME, // 驱动名称 93 .owner = THIS_MODULE,// 驱动所有者,THIS_MODULE宏定义 94 }, 95 }; 96 // 2、模块初始化相关实现 97 // 模块的入口函数 加载模块执行动作 98 static int hello_init(void) 99 { 100 int DriverState; 101 printk(KERN_EMERG "HELLO WORLD enter!\n");
// 平台驱动注册 102 DriverState = platform_driver_register(&hello_driver); // 注册驱动 转而执行初始化动作 103 printk(KERN_EMERG "\tDriverState is %d\n", DriverState); // 注册驱动执行状态 104 105 return 0; 106 } 107 // 模块退出函数 卸载模块所执行 108 static void hello_exit(void) 109 { 110 printk(KERN_EMERG "HELLO WORLD exit!\n"); 111 platform_driver_unregister(&hello_driver); // 卸载驱动函数 112 } 113 114 //1、 入口函数module_nint 115 module_init(hello_init); 116 module_exit(hello_exit);
上面调用流程
8、测试结果,使用ls dev// 查看生成的设备结点
[root@iTOP-4412]# ./mnt/disk/app_hello.o [ 234.644663] func hello open [ 234.646263] hello ioctl [ 234.648756] hello_ioctl cmd is 1 ...arg is 6 [ 234.653025] hello release App open /dev/hello_ctl123 success // 执行顺兴,貌似内核优先级比较高
9、需要注意,设备、驱动注册都是嵌入到内中的,生成设备结点则是面向应用;
10、编写简单的应用,调用设备结点App_hello_ctl123
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <unistd.h> 6 #include <sys/ioctl.h> 7 8 int main(void) 9 { 10 int fd; 11 char* hello_node = "/dev/hello_ctl123"; 12 fd = open(hello_node,O_RDWR|O_NDELAY); // 非阻塞打开 13 if(fd < 0){ 14 printf("func open app %s failed\n",hello_node); 15 return -1; 16 }else{ 17 printf("App open %s success\n",hello_node); 18 ioctl(fd,1,6); // 应用程序向驱动传值 19 } 20 close(fd); 21 return 0; 22 }
11、设备结点、驱动注册、设备注册
设备结点面向上层应用程序;驱动注册和设备注册需要嵌入到linux内核中;
12、简化上面的生成设备结点代码,devicenode_Samp.c
1 #include <linux/platform_device.h> 2 #include <linux/module.h> 3 #include <linux/init.h> 4 #include <linux/miscdevice.h> // 注册杂项设备头文件 5 #include <linux/fs.h> // 设备结点文件 6 7 #define DEVICE_NAME "hello_ctl123" 8 9 10 MODULE_LICENSE("Dual BSD/GPL"); 11 MODULE_AUTHOR("TOPEET"); 12 13 static int hello_open(struct inode *node, struct file *f) 14 { 15 printk(KERN_EMERG "\t func hello open\n"); 16 return 0; 17 } 18 19 static int hello_release(struct inode *node, struct file *f) 20 { 21 printk(KERN_EMERG "\t hello release\n"); 22 return 0; 23 } 24 25 static long hello_ioctl(struct file *f, unsigned int cmd, unsigned long arg) 26 { 27 printk(KERN_EMERG "\t hello ioctl\n"); 28 printk("hello_ioctl cmd is %d ...arg is %d\n",cmd,arg); 29 return 0; 30 } 31 32 // 文件操作,对应生成的设备结点具体如何操作 33 static struct file_operations hello_ops = { 34 .owner = THIS_MODULE, 35 .open = hello_open, 36 // int (*open) (struct inode *, struct file *); 37 .release = hello_release, 38 // int (*release) (struct inode *, struct file *); 39 .unlocked_ioctl = hello_ioctl, 40 // long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long); 41 42 }; 43 44 static struct miscdevice hello_dev = { 45 .minor = MISC_DYNAMIC_MINOR, // 自动分配ID 46 .name = DEVICE_NAME, // 设备结点名称 47 .fops = &hello_ops, // 结构体 48 }; 49 50 // 2、模块初始化相关实现 51 // 模块的入口函数 加载模块执行动作 52 static int hello_init(void) 53 { 54 int DriverState; 55 printk(KERN_EMERG "HELLO WORLD enter!\n"); 56 DriverState = misc_register(&hello_dev); 57 printk(KERN_EMERG "\tDriverState is %d\n", DriverState); // 注册驱动执行状态 58 59 return 0; 60 } 61 // 模块退出函数 卸载模块所执行 62 static void hello_exit(void) 63 { 64 printk(KERN_EMERG "HELLO WORLD exit!\n"); 65 misc_deregister(&hello_dev); 66 } 67 68 //1、 入口函数module_nint 69 module_init(hello_init); 70 module_exit(hello_exit);
平台驱动注册