2.制作第一个驱动程序

先讲解驱动框架,然后写出first_drv驱动程序,来打印一些信息

写出first_drv驱动程序需要以下几步:

(1)写出驱动程序first_drv_open first_drv_write

(2)需要定义file_operations结构体来封装驱动函数first_drv_open first_drv_write

对于字符设备来说,常用file_operations以下几个成员:

(3)  模块加载函数,通过函数 register_chrdev(major, “first_drv”, &first_drv_fops) 来

注册字符设备

(4)写驱动的first_drv_init 入口函数来调用这个register_chrdev()注册函数,

(5)通过module_init()来修饰入口函数,使内核知道有这个函数

(6)写驱动的first_drv_exit出口函数,调用这个unregister_chrdev()函数卸载,

(7) 通过module_exit()来修饰出口函数

(8) 模块许可证声明, 最常见的是以MODULE_LICENSE( "GPL v2" )来声明

1.首先创建first_drv.c文件

代码如下:

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/init.h>

#include <linux/delay.h>

#include <asm/irq.h>

#include <asm/arch/regs-gpio.h>

#include <asm/hardware.h>

#include <asm/uaccess.h>

#include <asm/io.h>

/*1写出驱动程序first_drv_open first_drv_write */

/*  inode结构表示具体的文件,file结构体用来追踪文件在运行时的状态信息。*/

static int first_drv_open(struct inode *inode, struct file  *file)
{
   printk(“first_drv_open\n”);      //打印,在内核中打印只能用printk()
   return 0;
}

/*参数filp为目标文件结构体指针,buffer为要写入文件的信息缓冲区,count为要写入信息的长度,ppos为当前的偏移位置,这个值通常是用来判断写文件是否越界*/

static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
  printk(“first_drv_write\n”);      //打印,在内核中打印只能用printk()
   return 0;
}

/*2定义file_operations结构体来封装驱动函数first_drv_open first_drv_write */

 static struct file_operations first_drv_fops = {
    .owner  =   THIS_MODULE,     //被使用时阻止模块被卸载
    .open   =   first_drv_open,
    .write   =   first_drv_write,
  };

/*4写first_drv_init入口函数来调用这个register_chrdev()注册函数*/
int first_drv_init(void)
{
   /*3 register_chrdev注册字符设备,并设置major=111*/
  /*如果设置major为0,表示由内核动态分配主设备号,函数的返回值是主设备号*/
register_chrdev (111, “first_drv”, &first_drv_fops); //111:主设备号,”first_drv”:设备名
/*
register_chrdev作用:在VFS虚拟文件系统中找到字符设备,然后通过主设备号找到内核数组里对应的位置,最后将设备名字和fops结构体填进去
*/
   return 0;
}

/*5 module_init修饰入口函数*/
module_init(first_drv_init);

/*6 写first_drv_exit出口函数*/
void first_drv_exit(void)
{
unregister_chrdev (111, “first_drv”);  //卸载驱动,只需要主设备号和设备名就行
}

/*7 module_exit修饰出口函数*/
module_exit(first_drv_exit);

/*8许可证声明, 描述内核模块的许可权限,如果不声明LICENSE,模块被加载时,将收到内核被污染 (kernel tainted)的警告。*/
MODULE_LICENSE( "GPL v2" );

2然后写Makefile编译脚本:

KERN_DIR = /work/system/linux-2.6.22.6   //依赖的内核目录,前提内核是编译好的

all:
       make -C $(KERN_DIR) M=`pwd` modules   // M=`pwd`:指定当前目录

                                             // -C $(KERN_DIR)表示要用到依赖的目录里的Makefile

                                        // modules:要编译的目标文件
  
clean:
       make -C $(KERN_DIR) M=`pwd` modules clean
       rm -rf modules.order

obj-m      += frist_drv.o     //obj-m:内核模块文件,指将myleds.o编译成myleds.ko

3. make,编译生成frist_drv.ko文件

4.然后开发板通过nfs网络文件系统来加载frist_drv.ko

加载之前首先通过 cat /proc/devices来查看字符主设备号111是否被占用

然后通过 insmod first_drv.ko来挂载, 通过 cat /proc/devices就能看到first_drv已挂载好

5.通过测试程序测试frist_drv模块

测试程序first_driver_text.c代码如下

#include <sys/types.h>    //调用sys目录下types.h文件
#include <sys/stat.h>      //stat.h获取文件属性
#include <fcntl.h>
#include <stdio.h>

/*输入”./first_driver_text”,     agc就等于1, argv[0]= first_driver_text  */
/*输入”./first_driver_text on”,   agc就等于2, argv[0]= first_driver_text,argv[1]=on;  */

int main(int argc,char **argv)
{
int fd1, fd2;
int val=1;
fd1 = open("/dev/xxx",O_RDWR);  //打开/dev/xxx设备节点
if(fd1<0)                   //无法打开,返回-1
  printf("can‘t open%d!\n", fd1);
else
   printf("can open%d!\n", fd1);    //打开,返回文件描述符
write(fd1, &val, 4);              //写入数据1
return 0;
}

6.然后arm-linux-gcc -o first_driver_text first_driver_text.c生成执行文件

回到板子串口上使用./first_driver_text来运行,发现如果open()打不开,会返回-1

是因为我们没有创建dev/xxx这个设备节点,然后我们来创建,使它等于刚刚挂载好的first_drv模块

mknod -m 660 /dev/xxx c 111 0            // first_drv模块的主设备号=111

./first_driver_text

结果如上图,发现测试程序里的open()函数调用了驱动中的first_drv_open()

write()函数调用了驱动中的first_drv_write(),

其中open()函数返回值为3,是因为描述符0,1,2都已经被控制台占用了,所以从3开始

7.改进底层驱动,使用动态装载:

除了静态装载驱动外,还可以动态装载,让系统自动为我们驱动设备自动分配设备号

7.1 修改first_drv_init入口函数和first_drv_exit 出口函数:

代码如下:

int major;              //定义一个全局变量,用来保存主设备号
int first_drv_init(void)
{
 /*设置major为0,由内核动态分配主设备号,函数的返回值是主设备号*/
  major =register_chrdev (0, “first_drv”, &first_drv_fops);
  return 0;

}

void first_drv_exit(void)
{
unregister_chrdev (major, “first_drv”);  //卸载驱动, 将major填入即可
}

如下图,通过动态分配得出它的主设备号是252,然后重创252的测试程序

rm dev/xxx

mknod -m 660 /dev/xxx c 252 0

./first_driver_text

7.2 每次都要手工创建设备节点,大家肯定也会觉得这样做太麻烦了。

可以使用自动创建设备节点,Linux有udev、mdev的机制,而我们的ARM开发板上移植的busybox有mdev机制,然后mdev机制会通过class类来找到相应类的驱动设备来自动创建设备节点 (前提需要有mdev)

在哪里设置了mdev机制?

制作根文件系统之使用里有介绍

7.3 接下来使用insmod自动创建设备节点, rmmod自动注销设备节点

(1)首先创建一个class设备类,class是一个设备的高级视图,它抽象出低级的实现细节,然后在class类下,创建一个class_device,即类下面创建类的设备:(在C语言中class就是个结构体)

static struct class *firstdrv_class;               //创建一个class类
static struct class_device   *firstdrv_class_devs; //创建类的设备

(2)在first_drv_init入口函数中添加:

firstdrv_class= class_create(THIS_MODULE,"firstdrv");  

//创建类,它会在sys/class目录下创建firstdrv_class这个类

firstdrv_class_devs=class_device_create(firstdrv_class,NULL,MKDEV(major,0),NULL,"xyz");

//创建类设备,会在sys/class/firstdrv_class类下创建xyz设备,然后mdev通过这个自动创建/dev/xyz这个设备节点,            

(3)在first_drv_exit出口函数中添加:

  class_destroy(firstdrv_class);                      //注销类,与class_create对应

  class_device_unregister(firstdrv_class_devs);      //注销类设备,与class_device_create对应

重新编译insmod后,会发现在/dev下自动的创建了xyz设备节点

其中在sys/class里有各种类的设备, 比如sys/class/fristdev下就有xyz

然后mdv通过insmod xxx 就去class找到相应类的驱动设备来自动创建设备节点

为什么内容一更改,mdv就能自动运行创建设备节点呢?

是因为以前创建根文件系统时候,

在etc/init.d/rcS里添加了这么一段:

echo /sbin/mdev > /proc/sys/kernel/hotplug             //支持热拔插

然后kernel每当设备出现变动时,调用/sbin/mdev来处理对应的信息,使mdev应用程序操作/dev目录下的设备,进行添加或删除

(4).再修改测试程序里open函数,将/dev/xxx改为/dev/xyz,这样就测试模块,就不需要再mknod了.

驱动程序first_drv_open first_drv_write中只是打印数据,接下来下一节便开始来点亮LED.

时间: 2024-10-22 05:20:12

2.制作第一个驱动程序的相关文章

cocos2d-x之道~制作第一款文字游戏(二)

在 cocos2d-x之道~制作第一款文字游戏(一)中,使用cocos2d-x把主界面显示出来,分别有每个级别提供的初始短语TileView,和目标短语TargetView.初步接触了cocos2d-x的基本概念和基础用法.这篇博客将会基本实现游戏的逻辑,完成游戏的主体部分.采用以下步骤: 使TileView可拖动 捕获TileView停止移动的事件 分析TileView是否放在正确的位置上 创建与原来Layer区分的层,放置按钮.菜单和分数等等. 添加计时和分数 现在开始,继续cocos2d-

构建第一个驱动程序(一)之Linux环境配置

构建第一个驱动程序(一)之Linux环境配置 一.超级终端的使用 软件:hypertem超级终端 打开软件后弹出 点击"取消" 弹出 点击" 是" 弹出"新建连接" 填写名称 点击 "确定",又弹出之前的位置信息,忽略他. 直到弹出"连接到" 选择你自己板子连接的串口,我的是COM4 确定后,按下图设置 ,按下任意键顺利出现 连接成功. 二.交叉编译环境的安装及配置首先下载交叉编译工具链,这里提供笔者使用的

关于制作第一个网页的总结

看关于html,css和js的书已经有一段时间了,今天试着将自己学到的技能实践了一下,先上图!!!! 现将模拟制作的过程总结一下:              1,拿到网页效果图的时候分析了下结构,确定采用浮动固定布局之后就开始做了              2,把大的框架写好以后,再细分小的框架内容              3,在制作的过程中往往效果一样,但实现的方式可以不同              4,完全按照自己的手法制作,但有些tag还拿捏不准,有的时候乱用             

Wordpress主题制作第一天

一. 为了跑Wordpress, 装了Apache + PHP + MySQl 二. 看了下Wordpress参考文档, 了解了下主题目录结构 三. 在themes里面新建文件夹 themes->my 然后my里建了index.php和style.css 然后在index.php里测试了一下bloginfo()函数,style.css留它空着.今天就到此为止了. index.php <!DOCTYPE html> <html> <head> <title&g

利用unity3d自带的CharacterController包制作第一人称控制模型的简单Demo

1.首先打开unity3d创建一个新的项目.在新项目中创建Plane和Directional light平行光,Plane作为地面. 2.在项目中导入CharacterController包.把3rd Person Controller模型放到Plane上. 3.把3rd Person Controller模型上的ThirdPersonController脚本和ThirdPersonCamera脚本Remove掉. 4.在3rd Person Controller模型上添加CharacterMo

python 基础学习之 python怎么制作第一个直方图 散点图 折线图 非常简单 利用python画图 python利用numpy画图 我用python制作第一个直方图 散点图 折线图

思路 首先声明 :非常简单  最简单的入门就是 直接拷贝几行代码看看效果 运行玩玩 注意: 本文采用窗口弹出运行显示的方式   可能需要网页显示图表 并且实时生成可能需要另找办法  暂时不处理. 安装numpy python文件导入numpy 复制几行代码 保存即可 在目录下 直接点击运行python文件  就会弹出一个类似cmd的小黑窗  另外再弹出一个绘制出来的图表 完成 关闭的时候 关闭图片  小黑窗自动回关闭. 部分效果图如下 代码参考 Python干货:分享Python绘制六种可视化图

每天学一点Docker(3)(制作你的第一个容器)

今天开始制作第一个容器,其实很简单 首先你要准备这些条件: 1.一个Ubuntu系统 2.这个系统能够联网,最起码ping www.baidu.com是可以的 这些准备条件准备好了,接下来就开始做准备工作. Docker 分为开源免费的 CE(Community Edition)版本和收费的 EE(Enterprise Edition)版本.下面我们将按照文档,通过以下步骤在 Ubuntu 16.04 上安装 Docker CE 版本. 这里下载的是CE版本. 配置Docker的apt源 打开u

IT兄弟连 HTML5教程 HTML5的学习线路图 第一阶段学习网页制作

学习HTML5技术可并不是简单学会几个新增的标签而已,HTML5现在可以说是前端所有技术的代名词.需要学习的语言和工具不仅多,对于刚接触他们的新人会感觉很乱.另外,前端开发也会细分很多个开发岗位,不同的岗位所涉及的技术也会有差别,所以先要确定自己的发展定位,收集要学习的内容,整理好学习的顺序.很多时候,成功除了勇气.坚持不懈外,更需要方向.也许有了一个好的方向,成功来得比想象的更快.如果在错误的路上奔跑,再怎么努力也是白搭.学习Web前端也是如此,首先应该选择一个正确的学习路线.HTML5学习线

仿真印章制作&mdash;&mdash;三种方法

方法一 1.模板下载(www.nipic.com等网站) 2.修改PSD文件中的文字,复制选中组到新建图片中,然后保存. 方法二 1.图章制作软件下载(百度图章软件下载) 2.编辑成需要的模板 3.PS简单图像处理 (1).新建图层,在新建图层使用画笔137.100按照边框和文字略作绘图S: (2).在新建图层选定S,在原图层中删除: (3).在滤镜中添加杂色(平均分布,数量35%) 方法三 完全使用PS制作 第一部分 第一步.新建画布,尺寸设300*300像素. 第二步.选择椭圆形状工具绘制2