天天看点

20145216 20145330 《信息安全系统设计基础》 实验四 模块方式驱动实验

20145216 20145330 《信息安全系统设计基础》 实验四 模块方式驱动实验

实验报告封面

20145216 20145330 《信息安全系统设计基础》 实验四 模块方式驱动实验
20145216 20145330 《信息安全系统设计基础》 实验四 模块方式驱动实验

实验步骤

1、阅读和理解源代码

进入/arm2410cl/exp/drivers/01_demo,使用vi 编辑器或其他编辑器阅读理解源代码。

20145216 20145330 《信息安全系统设计基础》 实验四 模块方式驱动实验

2、实验开始前配置环境变量

20145216 20145330 《信息安全系统设计基础》 实验四 模块方式驱动实验

3、编译驱动模块及测试程序上面介绍了在 Makefile 中有两种编译方法,可以在本机上使用gcc 也可以使用交叉编译器进行编译,这里我们只介绍用交叉编译器进行编译的结果。

20145216 20145330 《信息安全系统设计基础》 实验四 模块方式驱动实验

4、测试驱动程序如果使用 gcc 编译的话,需要通过下面的命令来建立设备节点,如果使用交叉编译器的话,不需要建立设备节点。

20145216 20145330 《信息安全系统设计基础》 实验四 模块方式驱动实验

首先要插入驱动模块demo.o,然后可以用lsmod 命令来查看模块是否已经被插入,在不使用该模块的时候还可以用rmmod 命令来将模块卸载。

20145216 20145330 《信息安全系统设计基础》 实验四 模块方式驱动实验

根据上述命令输入:

20145216 20145330 《信息安全系统设计基础》 实验四 模块方式驱动实验

下面使用测试程序来进行测试:./test_demo

直接运行出现请求打开失败的提示,经过多次探索问题解决运行成功

20145216 20145330 《信息安全系统设计基础》 实验四 模块方式驱动实验
20145216 20145330 《信息安全系统设计基础》 实验四 模块方式驱动实验

问题与解决

1、问题:忘记配置环境变量,需要将实验一armv4l文件夹要考到bc中

20145216 20145330 《信息安全系统设计基础》 实验四 模块方式驱动实验
  • 解决方法:将armv4l文件夹拷贝到bc中再次输入相关命令
    20145216 20145330 《信息安全系统设计基础》 实验四 模块方式驱动实验

输入命令./install.sb

20145216 20145330 《信息安全系统设计基础》 实验四 模块方式驱动实验

2、问题:交叉编译器进行编译第一次输入命令出现错误

20145216 20145330 《信息安全系统设计基础》 实验四 模块方式驱动实验
  • 解决方法:
    20145216 20145330 《信息安全系统设计基础》 实验四 模块方式驱动实验

输入命令后问题解决

20145216 20145330 《信息安全系统设计基础》 实验四 模块方式驱动实验

3、问题:在使用测试程序进行测试时,多次出现device open failed提示

20145216 20145330 《信息安全系统设计基础》 实验四 模块方式驱动实验
  • 解决方法:怀疑是前面某一步骤漏输入或者命令输错,依次再次输入排除问题,再次输入insmod demo.o,发现插件已存在于是再次运行,依然失败,排除插件插入因素
  • 输入命令#mknod /dev/demo c 254 0 ,建立设备节点(因为第一次编译时根据指导书指示没有操作此步骤),进行运行发现成功。
20145216 20145330 《信息安全系统设计基础》 实验四 模块方式驱动实验

理解源代码

  • 源代码
#define DEVICE_NAME  "demo"
	static ssize_t demo_write(struct file *filp,const char * buffer, size_t count)
	{ 
	    char drv_buf[];
	    copy_from_user(drv_buf , buffer, count);
	    …
	}
	static ssize_t demo_read(struct file *filp,char *buffer,size_t count,loff_t *ppos)
	{
	char drv_buf[];
	copy_to_user(buffer, drv_buf,count);
	….
	}
	static int demo_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg)
	{
	}
	static int demo_open(struct inode *inode, struct file *file)
	{
	}
	static int demo_release(struct inode *inode, struct file *filp)
	{
	    MOD_DEC_USE_COUNT;
	    DPRINTK("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,
	};
	#ifdef CONFIG_DEVFS_FS
	static devfs_handle_t devfs_demo_dir, devfs_demoraw;
	#endif
	static int __init demo_init(void)
	{
	    int result;
	    #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
	    SET_MODULE_OWNER(&demo_fops);
	    result = register_chrdev(demo_Major, "scullc", &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);

           

注释

  • 将驱动映射为标准接口
    • static struct file_operations demo_fops = {…}完成了将驱动函数映射为标准接口。
    • 驱动向内核注册
    • devfs_registe()和 register_chrdev()函数完成将驱动向内核注册。
  • Open方法

Open 方法提供给驱动程序初始化设备的能力,从而为以后的设备操作做好准备,此外open操作一般还会递增使用计数,用以防止文件关闭前模块被卸载出内核。

- 递增使用计数
- 检查特定设备错误。
- 如果设备是首次打开,则对其进行初始化。
- 识别次设备号,如有必要修改 f_op 指针。
- 分配并填写 filp->private_data 中的数据。
           
  • Release 方法

与 open 方法相反,release 方法应完成如下功能:

- 释放由 open 分配的 filp->private_data 中的所有内容
- 在最后一次关闭操作时关闭设备
- 使用计数减一
           
  • Read 和 和 Write 方法

ssize_t demo_write(struct file *filp,const char * buffer, size_t count,loff_t *ppos)

ssize_t demo_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)

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);

  • ioctl方法

ioctl 方法主要用于对设备进行读写之外的其他控制,比如配置设备、进入或退出某种

操作模式,这些操作一般都无法通过 read/write 文件操作来完成。

  • 编写中断处理函数的注意事项:

中断处理程序与普通C代码没有太大不同,不同的是中断处理程序在中断期间运行,它有如下限制:

不能向用户空间发送或接受数据

不能执行有睡眠操作的函数

不能调用调度函数

  • 使用/proc文件系统

/proc 文件系统是由程序创建的文件系统,内核利用它向外输出信息。/proc 目录下的

每一个文件都被绑定到一个内核函数,这个函数在此文件被读取时,动态地生成文件的内

容。

大多数情况下 proc 目录下的文件是只读的。使用/proc 的模块必须包 含

头文件

实验感想与体会

这次实验我们耗费了整整两个课时,在阅读实验指导书后本来觉得步骤不是很多应该能很快完成。没想到遇到各种细小的问题,并不是一帆风顺的。做实验的魅力就在于要不断的尝试与探索,在这个过程中操作更加熟练知识条理更加清晰,我们这次采用了新的排除法发现查错效率高了不少,今后会继续努力的。

队友链接:http://www.cnblogs.com/sjy519/p/6083253.html