20145312《信息安全系统设计基础》实验四 驱动程序设计
2016-11-27 22:37
302 查看
20145312《信息安全系统设计基础》实验四 驱动程序设计
实验目的与要求
学习在 LINUX 下进行驱动设计的原理掌握使用模块方式进行驱动开发调试的过程
实验内容
在 PC 机上编写简单的虚拟硬件驱动程序并进行调试,实验驱动的各个接口函数的实现,分析并理解驱动与应用程序的交互过程。
实验步骤
1. 搭建实验平台
连接arm开发板建立超级终端
启动实验平台(redhat虚拟机)
配置同网段IP
安装arm编译器(bc共享文件夹)
配置环境变量(redhat虚拟机中)
2. 编译应用程序
将01_demo文件夹拷贝到bc共享文件夹中在修改makefile文件后,采用交叉编译器即可进行编译。使用交叉编译器不需要建立设备节点
[root@BC 01_demo]#make
也可以使用gcc进行编译,通过下面的命令来建立设备节点
[root@BC src]#mknod /dev/demo c 254 0
3. 测试驱动程序
插入驱动模块demo.o,可以用lsmod 命令来查看模块是否已经被插入,在不使用该模块的时候还可以用rmmod 命令来将模块卸载然后运行测试程序,和预期结果一致
代码分析
驱动接口的实现过程
源代码
demo.c
#include <linux/config.h> #include <linux/module.h> #include <linux/devfs_fs_kernel.h> #include <linux/init.h> #include <linux/kernel.h> /* printk() */ #include <linux/slab.h> /* kmalloc() */ #include <linux/fs.h> /* everything... */ #include <linux/errno.h> /* error codes */ #include <linux/types.h> /* size_t */ #include <linux/proc_fs.h> #include <linux/fcntl.h> /* O_ACCMODE */ #include <linux/poll.h> /* COPY_TO_USER */ #include <asm/system.h> /* cli(), *_flags */ #define DEVICE_NAME "demo" #define demo_MAJOR 254 #define demo_MINOR 0 static int MAX_BUF_LEN=1024; static char drv_buf[1024]; static int WRI_LENGTH=0; /*************************************************************************************/ /*逆序排列缓冲区数据*/ static void do_write() { int i; int len = WRI_LENGTH; char tmp; for(i = 0; i < (len>>1); i++,len--){ tmp = drv_buf[len-1]; drv_buf[len-1] = drv_buf[i]; drv_buf[i] = tmp; } } /*************************************************************************************/ static ssize_t demo_write(struct file *filp,const char *buffer, size_t count) { if(count > MAX_BUF_LEN)count = MAX_BUF_LEN; copy_from_user(drv_buf , buffer, count); WRI_LENGTH = count; printk("user write data to driver\n"); do_write(); return count; } /*************************************************************************************/ static ssize_t demo_read(struct file *filp, char *buffer, size_t count, loff_t *ppos) { if(count > MAX_BUF_LEN) count=MAX_BUF_LEN; copy_to_user(buffer, drv_buf,count); printk("user read data from driver\n"); return count; } /*************************************************************************************/ static int demo_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { printk("ioctl runing\n"); switch(cmd){ case 1:printk("runing command 1 \n");break; case 2:printk("runing command 2 \n");break; default: printk("error cmd number\n");break; } return 0; } /*************************************************************************************/ static int demo_open(struct inode *inode, struct file *file) { sprintf(drv_buf,"device open sucess!\n"); printk("device open sucess!\n"); return 0; } /*************************************************************************************/ static int demo_release(struct inode *inode, struct file *filp) { MOD_DEC_USE_COUNT; printk("device release\n"); return 0; } /*************************************************************************************/ static struct file_operations demo_fops = { owner: THIS_MODULE, write: demo_write, read: demo_read, ioctl: demo_ioctl, open: demo_open, release: demo_release, }; /* static struct file_operations demo_fops = {…}完成了将驱动函数映射为 标准接口,devfs_registe()和 register_chrdev()函数完成将驱动向内核注册。 */ /*************************************************************************************/ #ifdef CONFIG_DEVFS_FS static devfs_handle_t devfs_demo_dir, devfs_demoraw; #endif /*************************************************************************************/ static int __init demo_init(void) { #ifdef CONFIG_DEVFS_FS devfs_demo_dir = devfs_mk_dir(NULL, "demo", NULL); devfs_demoraw = devfs_register(devfs_demo_dir, "0", DEVFS_FL_DEFAULT, demo_MAJOR, demo_MINOR, S_IFCHR | S_IRUSR | S_IWUSR, &demo_fops, NULL); #else int result; SET_MODULE_OWNER(&demo_fops); result = register_chrdev(demo_MAJOR, "demo", &demo_fops); if (result < 0) return result; // if (demo_MAJOR == 0) demo_MAJOR = result; /* dynamic */ #endif printk(DEVICE_NAME " initialized\n"); return 0; } /*************************************************************************************/ static void __exit demo_exit(void) { unregister_chrdev(demo_MAJOR, "demo"); //kfree(demo_devices); printk(DEVICE_NAME " unloaded\n"); } /*************************************************************************************/ module_init(demo_init); module_exit(demo_exit);
重要函数分析
Open 方法static int demo_open(struct inode *inode, struct file *file) { sprintf(drv_buf,"device open sucess!\n"); printk("device open sucess!\n"); return 0; }
Open 方法提供给驱动程序初始化设备的能力,从而为以后的设备操作做好准备,此外open 操作一般还会递增使用计数,用以防止文件关闭前模块被卸载出内核。在大多数驱动程序中 Open 方法应完成如下工作:
1. 递增使用计数 2. 检查特定设备错误。 3. 如果设备是首次打开,则对其进行初始化。 4. 识别次设备号,如有必要修改 f_op 指针。 5. 分配并填写 filp->private_data 中的数据。
Release 方法
static int demo_release(struct inode *inode, struct file *filp) { MOD_DEC_USE_COUNT; printk("device release\n"); return 0; }
与 open 方法相反,release 方法应完成如下功能:
1. 释放由 open 分配的 filp->private_data 中的所有内容 2. 在最后一次关闭操作时关闭设备 3. 使用计数减一
Read 和 Write 方法
static ssize_t demo_write(struct file *filp,const char *buffer, size_t count) { if(count > MAX_BUF_LEN)count = MAX_BUF_LEN; copy_from_user(drv_buf , buffer, count); WRI_LENGTH = count; printk("user write data to driver\n"); do_write(); return count; } /*************************************************************************************/ static ssize_t demo_read(struct file *filp, char *buffer, size_t count, loff_t *ppos) { if(count > MAX_BUF_LEN) count=MAX_BUF_LEN; copy_to_user(buffer, drv_buf,count); printk("user read data from driver\n"); return count; }
read 方法完成将数据从内核拷贝到应用程序空间,write 方法相反,将数据从应用程序空间拷贝到内核。对于者两个方法,参数 filp 是文件指针,count 是请求传输数据的长度,buffer 是用户空间的数据缓冲区,ppos 是文件中进行操作的偏移量,类型为 64 位数。
由于用户空间和内核空间的内存映射方式完全不同,所以不能使用象 memcpy 之类的函数,必须使用如下函数:
unsigned long copy_to_user (void *to,const void *from,unsigned long count);
unsigned long copy_from_user(void *to,const void *from,unsigned long count);
Read 的返回值
1. 返回值等于传递给 read 系统调用的 count 参数,表明请求的数据传输成功。 2. 返回值大于 0,但小于传递给 read 系统调用的 count 参数,表明部分数据传输成功,根据设备的不同,导致这个问题的原因也不同,一般采取再次读取的方法。 3. 返回值=0,表示到达文件的末尾。 4. 返回值为负数,表示出现错误,并且指明是何种错误。 5. 在阻塞型 io 中,read 调用会出现阻塞。
Write 的返回值
1. 返回值等于传递给 write 系统调用的 count 参数,表明请求的数据传输成功。 2. 返回值大于 0,但小于传递给 write 系统调用的 count 参数,表明部分数据传输成功,根据设备的不同,导致这个问题的原因也不同,一般采取再次读取的方法。 3. 返回值=0,表示没有写入任何数据。标准库在调用 write 时,出现这种情况会重复调用 write。 4. 返回值为负数,表示出现错误,并且指明是何种错误。 5. 在阻塞型 io 中,write 调用会出现阻塞。
ioctl 方法
static int demo_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { printk("ioctl runing\n"); switch(cmd){ case 1:printk("runing command 1 \n");break; case 2:printk("runing command 2 \n");break; default: printk("error cmd number\n");break; } return 0; }
ioctl 方法主要用于对设备进行读写之外的其他控制,比如配置设备、进入或退出某种操作模式,这些操作一般都无法通过 read/write 文件操作来完成.
用户空间的 ioctl 函数的原型为:
int ioctl(inf fd,int cmd,…)
其中的…代表可变数目的参数表,实际中是一个可选参数,一般定义为:
int ioctl(inf fd,int cmd,char *argp)
驱动程序中定义的 ioctl 方法原型为:
int (*ioctl) (struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg)
inode 和 filp 两个指针对应应用程序传递的文件描述符 fd,cmd 不会被修改地传递给驱动程序,可选的参数 arg 则无论用户应用程序使用的是指针还是其他类型值,都以
unsigned long的形式传递给驱动。
ioctl 方法的返回值
ioctl 通常实现一个基于 switch 语句的各个命令的处理,对于用户程序传递了不合适的命名参数时,POSIX 标准规定应返回-ENOTTY,返回-EINVAL 是以前常见的方法。
不能使用与 LINUX 预定义命令相同的号码,因为这些命令号码会被内核 sys_ioctl 函数识别,并且不再将命令传递给驱动的 ioctl。Linux 针对所有文件的预定义命令的幻数为“T”。所以我们不应使用 TYPE 为”T”的幻数。
实验过程中遇到的问题
问题:插入驱动模块失败如下:
[root@zxt 01_demo]# ./test_demo [root@zxt 01_demo]#device open fail
解决
这个主要是因为,因为手动编译代码太为繁琐,我们选择了用make的方法,将Makefile稍微修改后就可以使用,但是我们错误的默认了make使用交叉编译,而实际上是用的gcc编译,所以缺少了设备节点的建立,补上这一步骤之后就成功了。
问题:Make编译失败
解决
经过查看指导书,发现可能是在/usr/src 下没有建立一个linux 连接,可以使用下面的命令,解决了问题。
[root@zxt 01_demo]# cd /usr/src/ [root@zxt src]# ln -sf linux-2.4.20-8 linux [root@zxt src]# ls debug linux linux-2.4 linux-2.4.20-8 redhat
对于ln指令:
ln指令的用法是连接,使用格式是ln [options] source dist,这里我们用到的sf参数的含义是:
-f:链接时先将与dist同档名的档案删除
-s:进行软链接。(软链接,又称符号链接,这个文件包含了另一个文件的路径名,特点是可以链接不同文件系统的文件,甚至可以链接不存在的文件。)
实验心得
本学期的Linux课程的学习从这次实验中有了很大的提高,理论知识,只有结合实践才会出真知。面对驱动程序这一陌生概念,对于本次实验确实不好理解,只是跟着实验步骤操作,遇到问题,解决问题的过程中才加深了对课本知识的理解。希望下次实验后,Linux的学习能力能有更大提升。相关文章推荐
- 20145312 《信息安全系统设计基础》实验三 实时系统的移植
- 20145337 《信息安全系统设计基础》实验四 外设驱动程序设计
- 20145218《信息安全系统设计基础》实验四 驱动程序设计
- 20145221 《信息安全系统设计基础》实验四 外设驱动程序设计
- 信息安全系统设计基础实验四:外设驱动程序设计 20135211李行之&20135216刘蔚然
- 20145317《信息安全系统设计基础》实验四 外设驱动程序设计
- 20145324 20145325 《信息安全系统设计基础》实验四 外设驱动程序设计
- 信息安全系统设计基础实验四:外设驱动程序设计
- 20145221高其&20145326蔡馨熠《信息安全系统设计基础》实验四 外设驱动程序设计
- 信息安全系统设计基础实验四:外设驱动程序设计
- 20145240《信息安全系统设计基础》实验四 驱动程序设计
- 20145312 《信息安全系统设计基础》实验二 固件设计
- 20145215 《信息安全系统设计基础》实验四 外设驱动程序设计
- 20145327 《信息安全系统设计基础》 实验四 驱动程序设计实验
- 20145210 20145226《信息安全系统设计基础 》实验四 外设驱动程序设计
- 信息安全系统设计基础实验四:外设驱动程序设计
- 信息安全系统设计基础实验四:外设驱动程序设计
- #20145230《信息安全系统设计基础》实验四 外设驱动程序设计
- 信息安全系统设计基础实验四:外设驱动程序设计(20135229,20135234)
- 20145304 20145315 《信息安全系统设计基础》 实验四 外设驱动程序设计