linux设备驱动day3-驱动介绍,注册和加载实现
2016-08-24 23:24
267 查看
linux设备的分类:
字符设备:
串口、终端、触摸屏、键盘
查看方式:ls -l /dev 以c开头的文件都是字符设备文件。
块设备:
Flash、ramdisk(内存磁盘)、harddisk(硬盘)
查看方式:ls -l /dev 以b开头的文件
网络设备:
ifconfig -a
Linux抽象了对硬件的处理,所有的硬件设备都可以看作普通文件一样看待,可以使用和操作文件相同的,标准的系统调用接口来完成对设备的打开,关闭,读写和IO控制操作
字符设备和块设备是通过文件节点访问的,在Linux文件系统中,可以找到或者mknod创建设备对应的文件名,称为设备文件。
块设备和字符设备有两个数字分别是主设备号和次设备号,普通文件只有文件大小。
同一个驱动程序可以管理多个设备,依靠次设备号来区别。
主设备号标识该设备的种类,也标识了该设备所使用的驱动程序。
次设备号:标识使用同一设备驱动程序的不同硬件设备。次设备号只能在驱动程序内部使用,系统内核直接把次设备号传递给驱动程序,由驱动程序去管理。
设备驱动程序有三个接口:
(加载接口)系统引导接口:系统各设备初始化, 激活,运行驱动程序
(控制硬件接口)交互接口:驱动程序与具体设备间进行交互
(系统调用接口)实现接口:操作系统内核 通过数据结构file_operations实现具体的函数功能
软中断切换用户态进入内核态,地址空间不一样,不能直接对地址操作
Linux内核加载驱动程序:
1、系统启动时,通过代码自身加载模块,称为静态编译入内核,驱动程序开发完毕口一般这么使用
2、Linux系统启动后,通过insmod命令加载模块,称为动态加载。
驱动标识:文件节点,设备号
接口的实现:
编写驱动要遵守一定的协议:
MODULE_AUTHOR(” “);
MODULE_LICENSE(“GPL”); //这是必须的
MODULE_DESCRIPTION(” “);
MODULE_ALIAS(” “);
加载接口:int __init hello_init(void) (加载)、void __exit hello_exit(void)(卸载)(表示运行一次)
注册接口:module_init(hello_init);//模块加载函数
module_exit(hello_exit);//模块卸载函数
在内核里打印用printk,不能用printf函数
编译驱动程序要用Makefile来编译
生成的xxxx.ko文件放到板子上进行加载
加载命令:insmod xxxxx.ko 把内核模块加入到系统中, 会调用之前定义的init函数
查看动态加载的内核模块:lsmod 可以看到刚刚加载的内核模块
驱动程序打印的信息会打印到系统日志中 /var/log/syslog
卸载模块:rmmod xxxx, 会调用定义的exit函数
查看某个模块的信息:modinfo xxxxx.ko
申请设备号(在init时申请):register_chrdev(arg1, arg2, arg3);//注册字符设备,返回设备号
arg1 = 0 表示由系统分配设备号,arg1 > 0 表示自己规定一个,但不能冲突
arg2 是一个字符串,表示设备的名字
arg3 是一个结构体指针,(struct file_operations)结构体成员是一系列函数指针, 要给成员函数赋值,定义出相关函数(系统调用相关接口)
定义出一系列的函数实际就是在实现系统调用接口(open, release, read, write)
释放设备号(在exit时释放):unregister_chrdev(arg1, arg2);
arg1是要释放的主设备号
arg2 是出册时的设备名字
结构体的定义包含在头文件
字符设备:
串口、终端、触摸屏、键盘
查看方式:ls -l /dev 以c开头的文件都是字符设备文件。
块设备:
Flash、ramdisk(内存磁盘)、harddisk(硬盘)
查看方式:ls -l /dev 以b开头的文件
网络设备:
ifconfig -a
Linux抽象了对硬件的处理,所有的硬件设备都可以看作普通文件一样看待,可以使用和操作文件相同的,标准的系统调用接口来完成对设备的打开,关闭,读写和IO控制操作
字符设备和块设备是通过文件节点访问的,在Linux文件系统中,可以找到或者mknod创建设备对应的文件名,称为设备文件。
块设备和字符设备有两个数字分别是主设备号和次设备号,普通文件只有文件大小。
同一个驱动程序可以管理多个设备,依靠次设备号来区别。
主设备号标识该设备的种类,也标识了该设备所使用的驱动程序。
次设备号:标识使用同一设备驱动程序的不同硬件设备。次设备号只能在驱动程序内部使用,系统内核直接把次设备号传递给驱动程序,由驱动程序去管理。
设备驱动程序有三个接口:
(加载接口)系统引导接口:系统各设备初始化, 激活,运行驱动程序
(控制硬件接口)交互接口:驱动程序与具体设备间进行交互
(系统调用接口)实现接口:操作系统内核 通过数据结构file_operations实现具体的函数功能
软中断切换用户态进入内核态,地址空间不一样,不能直接对地址操作
Linux内核加载驱动程序:
1、系统启动时,通过代码自身加载模块,称为静态编译入内核,驱动程序开发完毕口一般这么使用
2、Linux系统启动后,通过insmod命令加载模块,称为动态加载。
驱动标识:文件节点,设备号
接口的实现:
编写驱动要遵守一定的协议:
MODULE_AUTHOR(” “);
MODULE_LICENSE(“GPL”); //这是必须的
MODULE_DESCRIPTION(” “);
MODULE_ALIAS(” “);
加载接口:int __init hello_init(void) (加载)、void __exit hello_exit(void)(卸载)(表示运行一次)
注册接口:module_init(hello_init);//模块加载函数
module_exit(hello_exit);//模块卸载函数
在内核里打印用printk,不能用printf函数
编译驱动程序要用Makefile来编译
生成的xxxx.ko文件放到板子上进行加载
加载命令:insmod xxxxx.ko 把内核模块加入到系统中, 会调用之前定义的init函数
查看动态加载的内核模块:lsmod 可以看到刚刚加载的内核模块
驱动程序打印的信息会打印到系统日志中 /var/log/syslog
卸载模块:rmmod xxxx, 会调用定义的exit函数
查看某个模块的信息:modinfo xxxxx.ko
申请设备号(在init时申请):register_chrdev(arg1, arg2, arg3);//注册字符设备,返回设备号
arg1 = 0 表示由系统分配设备号,arg1 > 0 表示自己规定一个,但不能冲突
arg2 是一个字符串,表示设备的名字
arg3 是一个结构体指针,(struct file_operations)结构体成员是一系列函数指针, 要给成员函数赋值,定义出相关函数(系统调用相关接口)
定义出一系列的函数实际就是在实现系统调用接口(open, release, read, write)
释放设备号(在exit时释放):unregister_chrdev(arg1, arg2);
arg1是要释放的主设备号
arg2 是出册时的设备名字
结构体的定义包含在头文件
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <asm/uaccess.h> #include <linux/slab.h> MODULE_AUTHOR("CYW"); MODULE_LICENSE("GPL"); //必须要写的遵守协议 MODULE_DESCRIPTION("A simple test for kernel module"); MODULE_ALIAS("a simple module"); struct file_operations hello_fops = { //定义一个结构体 .open = hello_open, .release = hello_release, .read = hello_read, .write = hello_write, }; //部分成员初始化 #define DEVICE_NAME "Hello_Driver" //注册成功会出现在/proc/devices中 int major; static int __init hello_init(void) //加载驱动 { printk(DEVICE_NAME":Hello world in kernel module\n"); //不能用printf major = register_chrdev(0, DEVICE_NAME, &hello_fops);//向内核申请主设备号,需要结构体地址参数 if(major < 0){ //申请失败 printk(DEVICE_NAME":register %s fail\n", DEVICE_NAME); return major; } printk(DEVICE_NAME":got major number: %d\n", major); return 0; } static void __exit hello_exit(void) //卸载驱动 { printk(DEVICE_NAME":Hello world in exit\n"); //不能用printf unregister_chrdev(major, DEVICE_NAME); //释放资源,根据主设备号和名字 } module_init(hello_init);//模块加载所用函数 module_exit(hello_exit);//模块卸载所用函数 int hello_open(struct inode *pinode, struct file *pfile) //打开驱动 { printk(DEVICE_NAME":hello_open\n"); wr_pos = 0; rd_pos = 0; pbuff =(unsigned char *)kmalloc(BUFF_SIZE, 0); if(pbuff == NULL){ printk(DEVICE_NAME":kmalloc fail\n"); return 1; } return 0; } int hello_release(struct inode *pinode, struct file *pfile) //释放驱动 { printk(DEVICE_NAME":hello_release\n"); kfree(pbuff); return 0; } /* *__user *buff, 用户空间内存地址 * */ ssize_t hello_read(struct file *pfile, char __user *buff, size_t bufsize, loff_t *poffset) //读驱动 { int i = 0; for(i = 0; i < bufs 4000 ize && wr_pos - rd_pos > 0; i++){ //循环拷贝一个字节到用户空间 put_user(pbuff[rd_pos % BUFF_SIZE] , buff+i); rd_pos++; } if(i >= 0) return i; #if 0 int len = wr_pos - rd_pos; printk(DEVICE_NAME":hello_read\n"); if( len > 0){ int err = copy_to_user(buff, pbuff+(rd_pos % BUFF_SIZE), len > bufsize ? bufsize : len); //把内核空间的数据读到用户空间 rd_pos += len > bufsize ? bufsize : len; if(!err) return len > bufsize ? bufsize : len; } #endif return -1; } ssize_t hello_write(struct file *pfile, const char __user *buff, size_t bufsize, loff_t *poffset) //写驱动 { int i = 0; for(i = 0; i < bufsize && free_size() > 0; i++){ //拷贝一个字节到内核空间 get_user(pbuff[wr_pos % BUFF_SIZE] , buff+i); wr_pos++; } if(i >= bufsize) return bufsize; #if 0 int free_len = BUFF_SIZE - (wr_pos - rd_pos); printk(DEVICE_NAME":hello_write\n"); if( free_len > bufsize){ int err = copy_from_user(pbuff+wr_pos % BUFF_SIZE, buff, bufsize);//把用户空间的数据写道内核空间 wr_pos += bufsize; if(!err) return bufsize; } #endif return -1; }
相关文章推荐
- JDBC驱动加载注册及数据库接(集)
- 详解MSDN上推荐的分步加载流驱动实现启动提速
- linux设备驱动(三)--应用input子系统实现按键驱动
- 手动加载NT式驱动(非工具)修改注册表实现
- c# winform实现dll加载时注册
- uClinux2.6(bf561)的NorFlash驱动实现分析(3) : chip_driver注册
- C#实现自动注册自定义文件类型介绍
- 不同数据库驱动的加载并向DriverManager注册
- windows mobile/wince 大容量存储驱动实现介绍
- 分步加载流驱动实现启动提速
- 详解MSDN上推荐的分步加载流驱动实现启动提速(By Walle@ARMCE)
- 分步加载流驱动实现启动提速
- 各种数据库JDBC驱动加载注册方式以及建立连接时的URL字符串
- LDD3《Linux设备驱动》中的最简单的字符设备驱动实现与测试
- wince驱动学习笔记(vs2005实现流驱动动态加载与卸载 2)
- 利用GRUB4DOS实现加载SCSI/RAID驱动免软驱,免按F6
- gendisk,request与bio结构体,以及块设备驱动注册与注销,以及加载与卸载
- 使用WDM驱动实现在NT下读取物理端口,特殊寄存器,物理内存的代码(C++驱动加载代码)
- 字符型驱动1 注册加载与卸载,读写与控制IOCTL的使用
- gendisk,request与bio结构体,以及块设备驱动注册与注销,以及加载与卸载