目录

  • 一. 实验题目:设备管理与驱动编程
  • 二. 实验目的
  • 三. 实验内容
  • 四. 实验过程及结果
    • 1)关键代码
    • 2)实验过程
      • 第一部分 内核外编译:
      • 第二部分 源码树内编译
  • 五. 未解决的问题
  • 附实验源码

一. 实验题目:设备管理与驱动编程

该题目是写一个scull设备驱动,虽然scull不是实体的硬件驱动,但是具备驱动的各个元素,其实质是在rom中开辟指定size的内存,该博客是对作业过程以及遇到的错误的简短记录:

二. 实验目的

 理解设备驱动应包含的基本功能;
 学会将设备驱动从内核源码树外加入到内核的方法;
 学会将设备驱动从内核源码树内加入到内核的方法。

三. 实验内容

 将树莓派设想为智能家居Linux服务器,可用来采集并维护环境数据,如PM2.5、温度、湿度、气味、电器状态数据等。实际环境中,数据来自物理设备,如传感器,本次试验用scull设备模拟。有条件的小组可以通过类似的步骤将物理设备添加到系统中;
 创建2个scull设备,一个设备驱动选择从树莓派内核源码树外(Kbuild)编译安装,另一个设备驱动选择从树莓派内核源码树内以模块方式编译安装。驱动函数要求包括:scull_open(),scull_read(),scull_write(),scull_ioctl(),scull_release();
 编写测试程序,验证所实现的驱动是否正常工作。

四. 实验过程及结果

注:scull1分为两个版本:主机本地编译和树莓派交叉编译两种,由于两者系统版本不同,因而要分别选择对应版本的源码,更改源码路径。具体体现在Makefile文件的差异

1)关键代码

Makefile(主机本地):

Makefile(交叉编译):

h文件:

c文件:

设备启动程序和写入设备程序:

以上均为对字符文件的操作,函数头存入结构体file_operations中:

设备安装、卸载函数:

2)实验过程

第一部分 内核外编译:

主机本地:

创建设备:


安装和卸载模块的显示:


由于自kernel 2.6.36版本起linux中file_operations的ioctl已经被删除,改用unlocked_ioctl实现特定功能,这个函数的功能是调用之前不再先调用lock_kernel()然后再unlock_kernel()(不需要经过内核的上锁和解锁)。

原先:

改为:


运行测试程序:


交叉编译:
由于对应版本的源码仅仅是官网压缩包解压后的文件,没有config和编译,所以会提示一系列文件错误:

将源码deconfig:

发现仍缺少文件:

完整编译完内核后发现仍然有错误:

这些错误显然是因为编译链没有选对而产生的,原因是当初Makefile未指定平台和编译器信息:


指定后编译可以通过:

随后将ko程序传输到树莓派中,但是在运行insmod scull1时发生insmod: ERROR: could not insert module scull1.ko: Invalid module format这一错误,是由于编译的源码版本和pi的版本不一致所导致。
pi的版本:

解决方案一,将树莓派内核升级到现有的源码版本(不推荐这一方法,因为在实际工程中不可能随意更改目标底层的系统,由于本次时间有限,所以采取此下下策):

解决方案二,重新下载和树莓派版本一致的源码,再重新编译一遍,再通过新的源码make出新的ko文件,传输至树莓派上。(无图,在此采用的方案一)

升级后版本,和源码一致:

此时出现另一个错误:

输入cat /proc/devices,查看设备号使用情况:

发现240号设备已使用。所以应当修改scull1.h的MAJOR的值,改为未占用的设备号。

重新创建设备文件:

安装成功:


查看日志:


运行测试:

Command:

读写:写入ab\0,再读出:

第二部分 源码树内编译

总共6步:



Kconfig:

修改Makefile:

Menuconfig,选择以模块形式添加:

编译内核(包括生成module):


(scull2设备号为258:)

五. 未解决的问题

问题一,Printk打印的信息未在终端显示:

因为printk本身为后台打印函数,所以应当进入后台查看。如果想在终端显示后台信息,有两种方法;

方法1,在一个终端写一个脚本,每秒实时显示日志信息:


方法2:用dmesg查看日志:


问题二,测试中,cmd2不能执行:
Scull1.c:

Scull1.h:



发现如果将cmd2定义成别的数,便可以执行,但具体原因仍未搞清楚。

附实验源码

scull1.c / scull2.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include "scull1.h"struct scull_dev *scull_device;int scull_open(struct inode*inode, struct file *filp)
{struct scull_dev *dev;dev = container_of(inode->i_cdev, struct scull_dev, cdev);  filp->private_data = dev;                            return 0;
}int scull_release(struct inode*inode, struct file *filp)
{printk(KERN_NOTICE "Scull released.\n");return 0;
}ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{ssize_t retval = -ENOMEM;//.. //???????if (raw_copy_from_user(scull_device->scull_buffer+*f_pos, buf, count)) {retval = -EFAULT;goto out;}retval=count;
out:return retval;
}//fop:?
ssize_t scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{ssize_t retval = 0;if (raw_copy_to_user(buf, scull_device->scull_buffer+*f_pos, count)) {retval = -EFAULT;goto out;}retval=count;
out:return retval;
}//fop:ioctl
long scull_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{if (cmd == SCULL_CMD1) {printk("running SCULL_CMD1 \n");return 0;}else if (cmd == SCULL_CMD2) {printk("running SCULL_CMD2 \n");return 0;}else if (cmd == SCULL_CMD3) {printk("running SCULL_CMD3 \n");return 0;}else printk("cmd error! \n");return -EFAULT;
}struct file_operations scull_ops = {.owner = THIS_MODULE,.read = scull_read,.write = scull_write,.open = scull_open,.unlocked_ioctl = scull_ioctl,.release = scull_release
};void scull_cleanup_module(void)
{dev_t devno = MKDEV(SCULL_MAJOR, SCULL_MINOR);if (scull_device) {cdev_del(&scull_device->cdev);kfree(scull_device);}unregister_chrdev_region(devno, 1);printk(KERN_NOTICE "Scull module exit.\n");
}//module init
int scull_init_module(void)
{int result;dev_t dev = 0; if (scull_major) {dev = MKDEV(scull_major, scull_minor);result = register_chrdev_region(dev, 1, "scull");} else {result = alloc_chrdev_region(&dev, scull_minor, 1,"scull");scull_major = MAJOR(dev);}if (result < 0) {printk(KERN_WARNING "scull: can't get major %d\n", SCULL_MAJOR);return result;}scull_device = kmalloc(sizeof(struct scull_dev), GFP_KERNEL);if (!scull_device) {result = -ENOMEM;goto fail;}memset(scull_device, 0, sizeof(struct scull_dev));cdev_init(&scull_device->cdev, &scull_ops);scull_device->cdev.owner = THIS_MODULE;scull_device->cdev.ops = &scull_ops;int err = cdev_add (&scull_device->cdev, dev, 1);printk(KERN_NOTICE "Scull module init.\n");return 0;
fail:scull_cleanup_module();return result;
}
module_init(scull_init_module);
module_exit(scull_cleanup_module);MODULE_AUTHOR("0B703");
MODULE_LICENSE("GPL");

scull1.h(和scull2.h一致,只是scull2.h的设备号为258)

#define SCULL_BUFF_MAXSIZE 1024
#define SCULL_MAJOR 256      //设备号
#define SCULL_MINOR 0
#define SCULL_CMD1 1
#define SCULL_CMD2 2
#define SCULL_CMD3 3int scull_major = SCULL_MAJOR;
int scull_minor = 0;struct scull_dev {char scull_buffer[SCULL_BUFF_MAXSIZE];struct cdev cdev; //cdev结构是描述字符设备的数据结构
};

Makefile(主机本地):

ifneq ($(KERNELRELEASE),)obj-m:=scull1.o
elseKERNELDIR:=/lib/modules/$(shell uname -r)/buildPWD:=$(shell pwd)
all:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:rm -f *.o
endif

Makefile(交叉编译):

CC = $(CROSS_COMPILE)gcc
ifneq ($(KERNELRELEASE),)obj-m:=scull1.o
else#KERNELDIR:=/lib/modules/$(shell uname -r)/buildKERNELDIR:=~/linuxPWD:=$(shell pwd)
all:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
clean:rm -f *.o
endif

test.c

#include<stdio.h>
#include<fcntl.h>
#include<stdlib.h>
#define SCULL_CMD1 1
#define SCULL_CMD2 2
#define SCULL_CMD3 3
int main(){int fd, retval;char W_buffer[26]={'a','b','\0'};char R_buffer[26];fd=open("/dev/scull1", O_RDWR);retval = ioctl(fd, SCULL_CMD1, 0);retval = write(fd, W_buffer, 26);printf("which command do u want?\n");int a;scanf("%d",&a);if(a==1){retval = ioctl(fd, SCULL_CMD1, 0);}else if(a==2)retval = ioctl(fd, SCULL_CMD2, 0);else    retval = ioctl(fd, SCULL_CMD3, 0);
//  retval = lseek(fd, 0, 0);retval = read(fd, R_buffer, 26);printf("read:%s",R_buffer);close(fd);return 0;
}

最简单的scull设备驱动相关推荐

  1. 一个简单字符型设备驱动及其测试

    驱动对一些人来说很难,而对一些人来说很容易.窃以为,理解简单设备驱动模型不难,深入理解并与Linux内核设计联系到一起需要花费时间.对于移植者来说,如何将自己自定义的模块天衣无缝放到内核中,是比较重要 ...

  2. linux 字符设备驱动测试,一个简单字符型设备驱动及其测试

    驱动对一些人来说很难,而对一些人来说很容易.窃以为,理解简单设备驱动模型不难,深入理解并与Linux内核设计联系到一起需要花费时间.对于移植者来说,如何将自己自定义的模块天衣无缝放到内核中,是比较重要 ...

  3. [设备驱动] 最简单的内核设备驱动--字符驱动

    [设备驱动] 最简单的内核设备驱动--字符驱动  概要: x86平台上(linux-2.6.34.14;Linux debian 3.2.0-3-686-pae)编写一个256字节的字符驱动程序.在/ ...

  4. 编写最简单的字符设备驱动

    编写最简单的字符设备驱动 1 编写驱动代码 2 编写makefile 3 编译和加载驱动 4 编写应用程序测试驱动 参考文章: linux驱动开发第1讲:带你编写一个最简单的字符设备驱动 linux驱 ...

  5. 通过内存模拟硬盘实现一个简单的块设备驱动

    本文的主要工作是通过硬盘来模拟内存,按照块设备驱动编程的框架实现一个简单的块设备驱动程序. 一.前期的准备工作 1.基本开发环境 Linux内核版本:Linux-3.4.10 开发板 : JZ2440 ...

  6. Linux设备驱动——第三章字符驱动

    当对幸福的憧憬过于急切,那痛苦就在人的心灵深处升起.--加缪 本章的目的是编写一个完整的字符设备驱动.我们开发一个字符驱动是因为这一类适合大部分简单的硬件设备.字符驱动也比块驱动易于理解.本章的最终目 ...

  7. Linux设备驱动编程第三版-笔记

    第1章 设备驱动简介 1.1 驱动程序的角色 机制:提供什么能力. 策略:如何使用这些能力. 1.2. 划分内核 内核的角色可以划分:     一:进程管理 二:内存管理 三:文件系统 四:设备控制 ...

  8. linux设备驱动第五篇:驱动中的并发与竟态

    目录[-] 综述 信号量与互斥锁 Completions 机制 自旋锁 其他的一些选择 不加锁算法 原子变量与位操作 seqlock(顺序锁) 读取-拷贝-更新(RCU) 小结 综述 在上一篇介绍了l ...

  9. Linux字符设备驱动框架

    字符设备是Linux三大设备之一(另外两种是块设备,网络设备),字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备,常见的字符设备包括鼠标.键盘.显示器.串口等等,当我们执行ls -l ...

最新文章

  1. Java学习总结:30
  2. Spring定时器--时间设置规则
  3. python飞机大战源代码-制作python程序windows安装包(飞机大战源码)
  4. 多线路虚拟主机解决方案
  5. async js 返回值_JS异步编程 | Async / Await / Generator 实现原理解析
  6. 数组做参数_C语言进阶之路:函数—数组参数!
  7. HTML文件可通过www进行传输,使用 zssh 进行 Zmodem 文件传输
  8. golang web php,golang 适合做web开发吗
  9. jquery getjson php,jquery中调用php json函数的方法分享
  10. 高通骁龙712移动平台正式发布!整体性能提升10%
  11. Spring bean注入方式
  12. Input禁用文本框
  13. spss常态检验_科学网—如何在SPSS中进行正态分布检验?
  14. 如何用photoshop做24色环_photoshop制作漂亮色环的教程(2)
  15. p2p通信原理及实现
  16. 2020 零基础 Vue综合应用 教开发音乐播放器—悦听(激发编程乐趣)【整理+源码】
  17. 图片文字翻译的软件有哪些?快收藏这几款实用的软件
  18. JS 获取当前日期时间/时间日期格式化(时间戳 转 yyyy-MM-dd HH:mm:ss)
  19. 正则^ [A-Za-z_][A-Za-z_0-9]*integer类型——学JAVA前一定要搞懂的最基本的东西(2)
  20. TensorFlow Lite编译Android so库

热门文章

  1. Linux虚拟机上JDK MYSQL TOMCAT REDIS安装
  2. mysql必了解的日志和备份
  3. 用curl post 调用接口
  4. github发起PR(pull request)的教程以及常见操作
  5. 巴菲特投资理念是什么?投资案例有哪些?《巴菲特估值逻辑-20个投资案例深入复盘》
  6. vue中$root的用法
  7. MikroTik RouterOS路由搭建的虚拟专用网络PPTP分支连不上
  8. 自然人电子税务局(扣缴端)地区选择为灰色解决办法
  9. matlab图像类型转换以及uint8、double、im2double、im2uint8和mat2gray等说明
  10. python的五个特点