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_FSdevfs_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);
#elseint  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 */
#endifprintk(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]# lsdebug linux linux-2.4 linux-2.4.20-8 redhat
  • 对于ln指令:

     - ln指令的用法是连接,使用格式是ln [options] source dist,这里我们用到的sf参数的含义是:- -f:链接时先将与dist同档名的档案删除- -s:进行软链接。(软链接,又称符号链接,这个文件包含了另一个文件的路径名,特点是可以链接不同文件系统的文件,甚至可以链接不存在的文件。)

实验心得

本学期的Linux课程的学习从这次实验中有了很大的提高,理论知识,只有结合实践才会出真知。面对驱动程序这一陌生概念,对于本次实验确实不好理解,只是跟着实验步骤操作,遇到问题,解决问题的过程中才加深了对课本知识的理解。希望下次实验后,Linux的学习能力能有更大提升。

转载于:https://www.cnblogs.com/yx20145312/p/6107735.html

20145312《信息安全系统设计基础》实验四 驱动程序设计相关推荐

  1. 信息安全系统设计基础实验四:外设驱动程序设计 20145222黄亚奇 20145213祁玮

    北京电子科技学院(BESTI) 实 验 报 告 封 面 课程:信息安全系统设计基础 班级:1452 姓名: 黄亚奇 祁玮 学号: 20145222 20145213 成绩: 指导教师:娄嘉鹏 实验日期 ...

  2. 信息安全系统设计基础 实验四:外社驱动程序设计 20135327郭皓 20135329 李海空...

    北京电子科技学院(BESTI) 实     验    报     告 课程:信息安全设计基础                         班级:1353 姓名:郭皓 李海空 学号:20135327 ...

  3. 信息安全系统设计基础实验四—20135215黄伟业20135222胡御风

    http://www.cnblogs.com/huyufeng/p/5004797.html 转载于:https://www.cnblogs.com/1551127024hwy/p/5010833.h ...

  4. 信息安全系统设计基础实验三

    北京电子科技学院(BESTI) 实     验    报     告 课程:信息安全系统设计基础              班级:1352 姓名:马悦   学号:20135235 成绩:        ...

  5. 信息安全系统设计基础实验三—20135222胡御风20135215黄伟业

    北京电子科技学院(BESTI) 实     验    报     告 课程:信息安全系统设计基础                         班级: 1352 姓名:胡御风      黄伟业    ...

  6. 20135201李辰希20135219洪韶武——信息安全系统设计基础实验五报告

    北京电子科技学院(BESTI) 实     验    报     告 课程:信息安全系统设计基础          班级:   1352 姓名:李辰希,洪韶武 学号:20135201,20135219 ...

  7. 信息安全系统设计基础实验一—20135222胡御风20135215黄伟业

    北京电子科技学院(BESTI) 实     验    报     告 课程:信息安全系统设计基础                         班级: 1352 姓名:胡御风      黄伟业    ...

  8. 信息安全系统设计基础实验五—20135222胡御风20135215黄伟业

    北京电子科技学院(BESTI) 实     验    报     告 课程:信息安全系统设计基础                         班级: 1352 姓名:胡御风      黄伟业    ...

  9. 20135201李辰希20135219洪韶武——信息安全系统设计基础实验报告

    北京电子科技学院(BESTI) 实     验    报     告 课程:信息安全系统设计基础                                           班级:1352班 ...

最新文章

  1. php数字截取2位小树,数据结构-PHP 二分搜索树的层序遍历(队列实现)
  2. 通俗大白话来理解TCP协议的三次握手和四次分手
  3. SecureCRTPortable - 破解
  4. 安卓学习笔记---Activity
  5. mysql having和where_浅谈Mysql中where和having的区别
  6. 只在当前页面生效的css样式,修改页面中的一个样式 仅在当前页面生效
  7. 一叶障目:排查问题的思路
  8. 面向对象第八天---预处理与mysql事务
  9. openwrt首次登录密码_什么是路由器登录密码 路由器登录密码介绍【详解】
  10. Linux xmpp网络不通,Pidgin XMPP协议拒绝访问漏洞
  11. android 布局颜色设置颜色设置,怎么在Android中利用view设置布局颜色
  12. PAT甲级 1003 Dijkstra的口诀干货
  13. JS 上传图片本地缓存预览
  14. Python语言认识和实用工具(1)
  15. 英文论文写作LaTeX模板
  16. Phodal 的 2018 节点:Think Big Be Long
  17. 身份证校验规则Js代码
  18. 计蒜客 - 蒜头君的任务
  19. display常用属性值
  20. pyautogui微信小程序自动化操作

热门文章

  1. ZOJ 3964 Yet Another Game of Stones (博弈)
  2. python 按照顺序读取文件夹中的图片名称
  3. 33岁开始学习java开发晚吗?
  4. 打开和写入文件( fopen和fopen_s
  5. Java常用的几种JSON解析工具
  6. 对于一颗满二叉排序树深度为K,求最小子树根节点值 Python代码实现
  7. boost------asio库的使用2(Boost程序库完全开发指南)读书笔记
  8. 智能 | 你真的了解自动化仓储系统吗?
  9. angular 实现类似脑图树结构
  10. 【数论】快速幂(欧拉幂)