引言

linux驱动是连接软件和硬件的一个中间介质,实现了对硬件的配置和控制。进一步将硬件抽象化,为软件操作硬件提供了简单的接口。不论硬件的具体形式如何,linux驱动都将其映射到一个文件,软件端对硬件的读写操作等都被抽象成文件操作了。本篇从hello world开始,简要介绍驱动的基本结构,然后再进一步介绍LED硬件的搭建,以及驱动的编写,设备树的修改。让大家对linux驱动有一个基本的认识。

1

Hello world驱动

hello world几乎成了所有编程书的第一个程序,用来介绍程序的大体结构。一个简单的hello world让人感觉这本书学起来真的很容易,不知不觉就进入圈套。被诱导入坑的我,也来用一个hello world诱别人入坑。先上程序:

#include #include MODULE_LICENSE("GPL v2");​​static int hello_init(void){​ printk(KERN_INFO "Hello world"); return 0;}​static void hello_exit(void){ printk(KERN_INFO "Goodbye, cruel world");}​module_init(hello_init);module_exit(hello_exit);​

一个驱动的使用过程包括:模块的装载,软件调用,模块卸载。程序中module_init和module_exit是内核中的宏,是一个驱动必须包含部分。当驱动被装载时,就会调用module_init指定的初始化函数hello_init,而当驱动被卸载时,就会调用hello_exit函数。初始化函数通常都是进行设备树检查,内存分配映射,硬件配置,文件和硬件关联等操作。清除函数用于释放内存,硬件的清零等操作。通常驱动还会定义一些文件IO操作,比如write,read,ioctrl等。Hello world只给出一个驱动编写格式和流程,文件操作在LED驱动中再介绍。MODULE_LISENCE用于告诉内核该模块采用的许可证类型,这个一般和linux内核采用

的许可证一致就好了。

现在来看驱动模块是如何编译的,看Makefile:

obj-m:=hello.oARCH=armCROSS_COMPILE=arm-xilinx-linux-gnueabi-CC:=$(CROSS_COMPILE)gccLD:=$(CROSS_COMPILE)ldKERNELDIR:=/home/anpingbo/Design/linux/linux-xlnxPWD:=$(shell pwd)all: make -C $(KERNELDIR) M=$(PWD) modules​

如果我们要构造的模块名称为hello.ko,那么就需要指定obj-m为hello.o,这个是hello.ko生成的依赖选项。是在zynq平台上编译模块,需要制定ARCH类型为arm,以及交叉编译工具,如果使用本机linux系统默认gcc就不能在zynq平台下加载模块,这里的交叉工具链为arm-xilinx-linux-gnueabi-gcc,arm-xilinx-linux-gnueabi-ld是交叉连接器,把程序链接成可以在arm平台运行的模块。同时还需要制定内核系统文件夹,因为模块编译要用到内核的库。All下就是编译模块了。设置好交叉工具的环境变量后,直接执行make,机会生成hello.ko,这个就是编译好的驱动模块。

图1.1 编译hello驱动过程

我们打开zynq系统,加载hello.ko,加载使用insmod命令,卸载使用rmmod命令。我们发现没有打印出任何信息,这是不是模块编译有错误,其实模块没有错误,流程也都对。因为printk打印函数是有级别的,只有低于这个级别值才能打印到terminal中。我们可以修改内核级别,比如我们将级别降低:

echo 8 > /proc/sys/kernel/printk

我们还可以使用dmesg来查看驱动打印的日志。其会打印所有驱动加载和卸载的打印日志。

图1.2 insmod hello.ko

2

LED驱动

2.1 vivado工程

我们通过axi_gpio来连接4个LED灯,通过linux驱动来点亮LED灯。Block如图2.1,我们设置gpio宽度为4。还有一个我们需要用到的就是LED映射到的内存,可以在address editor中看到,gpio0是LED的。接下来就是一般的流程,管脚约束,综合,编译。然后导出工程,打开SDK,新建fsbl,编译,生成设备树,制作boot.bin。

图2.1 LED硬件工程

2.2 LED驱动

LED属于字符类驱动,符合字符类驱动的写法。Linux内核中每个模块实际上是以文件形式存在的,这些文件存放于/dev目录下。而不同的驱动模块具有不同的主设备号和次设备号。主设备号用于区别不同的驱动,而次设备号用于更具体的指向驱动指向的设备。设备号就相当于门牌号,用于唯一区别不同驱动。编写驱动过程中就需要为驱动分配设备号。现在进一步分析LED驱动代码:

#define LED_DATA 0x41200000#define LED_CTRL 0x41200004

这两行定义了LED数据控制寄存器和数据读写寄存器的内存地址。实际上驱动对LED硬件的配置和读写都是通过配置其寄存器实现的。具体要看GPIO的硬件信息。

dev_t led_devt;void __iomem *baseaddr;

dev_t定义了设备编号,__iomem定义了linux内核的存储指针。硬件的内存需要映射到linux内核空间才能操作。在使用和配置LED时,需要先将其物理内存映射到linux内核的内存空间。映射函数ioremap就是专门用于IO端口内存映射的。

static int led_major=25;struct cdev *led_dev;int led_value;

上述定义了一个led主设备号。通过ls –l命令可以查看/dev下驱动的设备号。当然这是一种不方便的方式,正常情况下设备号也可以自动分配。Cdev是字符设备的结构体,定义如下:

struct cdev { struct kobject kobj; // 内嵌的kobject对象 struct module *owner; // 所属模块 const struct file_operations *ops; // 文件操作结构体 struct list_head list; //linux内核所维护的链表指针 dev_t dev; //设备号 unsigned int count; //设备数目 };

文件操作结构体实际上提供了软件操作LED的接口,上文讲过驱动都被映射成/dev下的一个文件,软件调用驱动的时候,以打开,读写,关闭对应设备文件来进行操控。我们看一下LED驱动中的文件结构:

struct file_operations led_fops={ .owner=THIS_MODULE, .read=led_read, .write=led_write, .unlocked_ioctl=led_ioctl, .open=led_open, .release=led_release,};

其中包含了文件应该有的四种基本操作open, read, write, release实际上是close。文件结构体还提供了ioctrl函数,这个函数为软件提供了一种更为灵活的操纵底层硬件的方法。

接下来对文件结构体中的每个函数进行分析。

1) led_open

static int led_open(struct inode *inode, struct file *filp){ struct resource *res; int reg; printk("begin: open led"); filp->private_data=inode->i_cdev; res=request_mem_region(LED_DATA, 0x10000, "LED"); if(!res){ printk("failed requesting resource"); return 0; }  printk("begin: remap led"); baseaddr=ioremap(LED_DATA, 0x10000); if(!baseaddr){ printk("ERROR: couldn't allocate baseaddr"); return 0; } printk("baseaddr is %x

rmmod无法卸载驱动_从hello world到LED驱动相关推荐

  1. STM32MP157驱动开发——设备树下的LED驱动

    STM32MP157驱动开发--设备树下的LED驱动 主要内容:将之前章节中使用新设备设备驱动编写的LED驱动改成设备树形式 文章目录 STM32MP157驱动开发--设备树下的LED驱动 一.主要步 ...

  2. 一致吗 驱动_外国不过春节?AMD驱动再更新,解决BUG,游戏不闪退,重启不黑屏...

    AMD依然是那个AMD,驱动自然也还是那个驱动. 相对于英特尔大的驱动来说,AMD的驱动也继承了ATI当年催化剂驱动的<优良传统>. 真的是BUG不断. 上一个版本的驱动的实在锤问题: & ...

  3. I.MX6ULL ARM驱动开发---设备树下的LED驱动实验

    一.什么是设备树?   设备树(Device Tree),将这个词分开就是"设备"和"树",描述设备树的文件叫做 DTS(Device Tree Source) ...

  4. 驱动框架7——使用gpiolib完成led驱动

    以下内容源于朱有鹏<物联网大讲堂>课程的学习整理,如有侵权,请告知删除. 十四.使用gpiolib完成led驱动 1.流程分析 (1)第1步:使用gpio_request申请要使用的一个G ...

  5. win10怎么更新显卡驱动_换显卡后,显卡驱动与系统不兼容怎么办?

    出现这种问题有两种可能,第一种是Win10系统版本太低,第二种是显卡品牌与其他硬件的品牌不兼容(比如给微星电脑换上外星人的显卡,就会遇到这种问题)多数时候都是第二种原因,解决方法如下. 安装前的准备步 ...

  6. 第1.4节_摄像头驱动_从零写一个虚拟驱动

    1.1th(搭建起虚拟摄像头驱动框架) /* 仿照vivi.c */ #include <linux/module.h> #include <linux/delay.h> #i ...

  7. android 副屏驱动_高通 android平台LCD驱动分析

    目前手机芯片厂家提供的源码里包含整个LCD驱动框架,一般厂家会定义一个xxx_fb.c的源文件,注册一个平台设备和平台驱动,在驱动的probe函数中来调用register_framebuffer(), ...

  8. 2.1.1.4 节_摄像头驱动_从零写一个虚拟驱动之示例

    一. vivi驱动应用程序调用过程 上节对xawtv对vivi程序调用过程进行了详细分析,可总结为以下流程: 二.仿照vivi.c编写myvivi.c驱动程序 #include <linux/m ...

  9. 云服务器怎么安装声音驱动_关于阿里云服务器安装显卡驱动类型的

    2 255 天前 [ 3333.808172] nvidia: probe of 0000:00:07.0 failed with error -1 [ 3333.808196] NVRM: The ...

最新文章

  1. php获取post表单数据_PHP如何通过post方法来获取form表单中数据?(代码示例)
  2. lisp的vla函数画矩形_难点解析丨反比例函数的图象和性质
  3. 设计模式(装饰模式)
  4. Codeforces Round #491 (Div.2)
  5. 使用kibana可视化报表实时监控你的应用程序
  6. 服务器密码机部分文件的介绍学习
  7. PSIM软件学习---07 元件参数动态扫描
  8. Jquery Cookbook摘要之使用上下文参数
  9. 《线性代数及其应用 第四版》习题1.4
  10. 1230v3配服务器内存性能提升,E3 1230 V3四核3.7G睿频福利教程及评测
  11. 2.1 图像验证码(英文验证码、超级鹰)
  12. ArcGIS教程 - 9 矢量数据空间分析
  13. mysql explain 性能分析_MySQL性能分析(Explain)
  14. 世界性能服务器图片,王思聪花了一百万组装了台服务器:全球跑分第四 64核心128线程!快来围观~...
  15. ElementUI Collapse 折叠面板
  16. sap 流程图 退货销售订单_销售订单_退货及退回客户(采用高级退货)
  17. 互动云渲染——云原生渲染的初步探索
  18. [ 生活 ] 我有一个想法!
  19. 【基础编程】猜数字游戏的提示
  20. 解决MacOS12.6自带的屏幕录制闪退的问题

热门文章

  1. android适配规则(一)
  2. 在Windows下编译OpenSSL(VS2005)【转】
  3. 诸葛亮的十堂课:在变局中安身立命,在逆境中找到力量
  4. OpenCV-除法运算cv::divide
  5. mysql新增加密字段_mysql字段加密
  6. 洛克人html5,《洛克人Zero/Zx合集》:跳票冷饭,与预期有差但依旧很香
  7. android 输入法 监听,android 监听 输入法
  8. php找不同的数组元素,PHP实现查询两个数组中不同元素的方法
  9. 淘口令二合一生成php_3淘口令接口的封装 工具类的打造
  10. 通信原理眼图画法_光纤通信链路入侵与检测技术研究综述