uClinux下可加载设备驱动程序的研究与实现

《OpenWrt开发笔记》第17章 驱动开发之字符设备驱动程序 17.1 配置编译驱动程序首先将 f403tech_drv (参见文末下载链接)文件夹传到 OpenWrt 源码的openwrt/trunk/package/kernel 目录下。然后进入 OpenWrt 源码的顶层目录,执行 makemenuconfig。 注意:至于f403tech_drv的内容,建议初学者按照步骤做就是了,不必追问细节,初学者应该知道openwrt驱动开发流程,代码的具体实现 阅读详情
2007-12-25 09:45:09

字体变小 字体变大
uCLinux是一种面向嵌入式微处理器的微型操作系统,由于其源代码开放和功能齐备,已经在嵌入式操作系统中占有重要地位。介绍了在uCLinux版本2.4.26上如何实现可加载的设备驱动程序的设计步骤及其程序设计要点,以及在S3C4510b上实现LED输出和开关量输入的可加载内核模块驱动程序的实现过程和应用层测试程序。

0 引言
随着嵌入式微处理器技术和嵌入式操作系统的快速发展,嵌入式系统已广泛渗透到仪器仪表、工业控制等各个领域。uClinux以其源代码开放和功能齐备的特性在嵌入式操作系统中独树一帜,在嵌入式系统中得到了广泛应用,各种硬件也需要应用在嵌入式系统中以实现系统功能,这样就必须有相应的驱动程序支持这些硬件,以满足系统扩展功能的要求。在uClinux版本2.0以前实现驱动程序传统的做法是把驱动程序写成静态加载模块,每次修改源代码后调试都需要编译一次内核,这样导致驱动程序的调试效率十分低下。为了解决这个问题,本文采用了稳定的内核版本2.4.26的uClinux源代码进行驱动程序的开发,并采用可加载内核模块的方式进行设备驱动程序的调试,在内核运行时可以
动态的加载和卸载。本文主要介绍在uClinux操作系统下如何实现可加载的设备驱动程序的配置、编写和调试步骤。
uClinux是针对嵌入式控制领域的嵌入式Linux操作系统,系统结构上继承了Linux内核的绝大部分特性,在uClinux分发包中还包括很多成
熟的应用层程序,包括busybox、telnet、ftp等。由于现有的uClinux是为了没有内存管理单元MMU(memory manager unit)的嵌入式微处理器而设计的,所以有无MMU支持是Linux与uClinux的基本差异。由于没有内存管理单元,uClinux上的应用程序可直接访问物理存储器和外围设备端口,这使原有在嵌入式微处理器编写的程序可很容易移植到uClinux上使用。

1 可加载的设备驱动程序
设备驱动程序是操作系统内核和机器硬件间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个文件,应用程序可像操作普通文件一样对硬件设备进行操作。
在uClinux操作系统下有字符设备和块设备两类主要的设备文件类型。字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了;块设备利用一块系统内存作为缓冲区,当用户进程对设备请求满足用户要求时,就返回请求的数据,它是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。为说明此原理,本文探讨把一个8盏LED和8个普通按钮作为字符设备在uClinux中使用。
Linux是一个高度模块化的操作系统,uClinux下驱动程序也是以模块形式存在的。在uClinux下对驱动程序加载有两种方式:静态编译到内核和动态加载到内核。在uClinux V2.4之前的版本只支持静态编译到内核,V2.4以后,uClinux支持了动态加载到内核的功能,使驱动程序的调试变得与调试应用程序一样方便快捷。可加载内核驱动程序的开发步骤一般分为3步:
①采用uClinux V2.4.26以后的稳定版本进行内核编译,在编译时让内核支持动态加载;
②可加载内核模块驱动程序的编写;
③把驱动程序通过FTP传到目标板中用命令行工具安装,通过应用层测试程序进行测试,改进。
下面说明可加载的内核驱动程序的开发步骤。
1.1 uClinux内核的配置
首先是配置并编译内核,使内核支持串行接口,FTP,支持内核的命令,如:insmod,lsmod,rmmod。接下来运行make dep和make all用于生成目标板的内核映像文件image.ram和image.rom,这里把image.rom写入Flash。写入完成后,通过连接到uClinux目标板的串口终端运行并配置uClinux,正确设置网络IP地址,使uClinux的目标板和开发用主机能够互相用FTP访问,这里把主机IP设为210.46.109.26,uClinux的目标板IP地址设为210.46.109.25,通过ping命令确认网络通畅,在串口终端上运行“FTP 210.46.109.26”能够正常登陆主机并下载文件。
不代参数执行insmod,lsmod,rmmod命令能够得到正常的错误提示。至此第一步的准备工作完成。

1.2 可加载内核模块驱动程序的编写
在uClinux中,为了让应用程序对底层硬件有一个统一的访问标准,必须对每个在uClinux中应用的硬件编写驱动程序,驱动程序加载成功后,应用程序就可以像读写普通文件一样来通过驱动程序访问硬件设备了,所以驱动程序必须处理一些标准的文件操作,常见的包括“open”,“read”,“write”等。
编写驱动程序主要是编写当“Open”,“Read”,“Write”等操作产生时的相应函数,这些函数的入口地址在程序中被定位在uClinux的一个“file-operations”结构体中,例如:
struct file-operations lcd-fileops={
open:lcd-open,
release:lcd-release,
read:led-read,
write:led-write,
};
lcd-fileops为此结构体的名称,均为led-open,lcd-release等均为相应的函数名称,当此驱动程序加载后,若应用程序要打开此设备,操作系统会向驱动程序发出“open”请求,驱动程序会按lcd-fileops中的定义调用open对应的处理函数lcd-open实现应用程序要的打开功能,例如:
int lcd-open(struct inode *inode,struct file *file)
{
MOD-INC-USE-COUNT;
printk("charlcd device opened./n");
return 0;
}
struct inode和struct file是操作系统传进来的指针,一般情况不需修改,MOD-INC-USE-COUNT是使此设备计数器+1,在调用release操作时会-1(对应操作为MOD-DEC-USE-COUNT),uClinux通过这种简单的机制以防止文件在关闭前驱动程序模块被卸载出内核。每次成功的设备打开操作后都应该执行MOD-INC-USE-COUNT。为简单起见,没有进行设备有效性检查,直接调用了,printk是调试驱动程序时一个内核输出函数(在内核中不能用printf函数),它执行时会向默认的输出设备输出相应信息,如,本例执行时,会向串口终端输出字符串“charlcd device opened.”并换行。
文件操作函数led-read和led-write,分别对应“read”和“write”操作,其中led-read的函数例子如下:
ssize-t lcd-read(struct file* file,char *userBur,size-t sz-buf,loff-t *lf)
{
IOMODE= INPUTMODE;
userBuf[0]=IOPDATA;
printk("Reading data from kernel process/n");
return 0;
}
userBuf是驱动程序与应用程序的数据接口,其长度为sz-buf。在执行读操作时应该把硬件“读”到的数据放在userBuf中。应用程序在对此硬件执行“read”操作后会得到userBuf中的数据。本例是读取外部8个开关的状态,把输入输出端口模式寄存器IOPMODE定义为输入(INPUTMODE),并把输入输出端口数据寄存器的值(当前开关状态)赋给userBuf[0]。Lcd-write的函数例子如下:
ssize-t lcd-write(struct file*file,const char *buf,size-t sz-buf,loff-t *lf)
{
int count;
if(bur==NULL)return -1;
IOPMODE=OUTPUTMODE;
IOPDATA=buf[0];
printk("Writing user data to kernel process/n");
return 0;
}
buf是应用程序与驱动程序的接口,其长度为sz-buf。在执行操作时,由上层应用程序来决定buf的内容,由此函数内的代码来决定如何把buf的内容“写”到硬件设备中。本例中,把Samsung4510b的输入输出端口模式寄存器(IOPMODE)置为输出状态来控制8个发光二极管,通过把buf[0]的数据写入输入输出端口寄存器(IOPDATA)来决定外部8个发光二极管的亮灭。
以上操作对于动态和静态两种加载方式的驱动程序都是通用的,对可加载内核模块驱动程序,还必须在程序中定位以下两个操作的位置:module-init和module-exit。在程序中如下表示:
module-init(lcd-init);
module-exit(lcd-clearup);
说明module-init操作的实现函数是lcd-init,module-exit操作的实现函数是(lcd-clearup),由于lcd-init是模块注册和初始化函数,使用完后就可以释放占有的资源,在执行时必须放到内核内存区的.text.init区,所以,必须在lcd-init函数定义前加入static int --init声明,一个实际的例子如下:
int lcd-devNo;
static int --init lcd-init(void)
{
int iResult==0;
printk("Registering charlcd device into /dev /n");
iResult = register-chrdev(254,"charlcd",&lcd-fileops);
if(iResult==254)
{
printk("Charlcd device is registered./n");
led-devNo = iResult;
return iResult;
}
else
{
printk("Error occurred while Registering charlcd device./n");
return iResult;
}
}
若register-chrdev执行成功,会返回注册成功的主设备号(major number),把此主设备号保存到lcd-devNo是将来为动态注销此驱动程序时使用的,因为动态注销此驱动程序需要主设备号,动态注销函数是module-exit()中指定的函数,本例中是“lcd-clearup”,例如:
int led-clearup(void)
{
int iResult=0;
if((iResult= unregister-chrdev(1ed-devNo,"charlcd"))==0)
printk("Remove the charlcd device from kernel./n");
else
printk("Error occurred while Removing the charlcd device./n");
return iResult;
}
lcd-devNo是当register-chrdev执行成功时赋的值,若unregister-chrdev函数执行成功,返回0,否则返回出错代码,交由操作系统处理。
综上所述,一个典型的可动态加载的内核驱动程序模块的代码包括两大部分:
①file-operations结构体的填充和函数实现;
②module-init()和module-exit()函数参数指针的填充和函数实现。
对于静态内核驱动程序而言不需要第二部分,但是需要手动修改uClinux的配置源代码,加入模块注册信息,比较烦琐,容易出错。而可动态加载的内核驱动程序模块则不会出现此问题。
把编辑完的代码通过arm-elf-gcc编译成目标文件(.o文件)就完成了可加载内核驱动程序的生成,常用的编译指令文件Makefile的内容如下:
CC=arm-elf-gcc//指明使用的交叉编译器
OBJS=led.o//指明目标文件的名称
SOCS=led.c//指明源程序文件的名称
LD= arm-elf-gcc//指明使用的连接器
LDFLAGS = --DMODULE -D --KERNEL -- D --linux --C
all:
$(CC) -o $(OBJS) $(LDFLAGS)
$(SOCS)
cp $(OBJS)/root/led.o
clean:
-rm -f $(EXEC) *.elf *.gdb *.o

1.3 驱动程序的安装与测试
把驱动程序通过FTP传到目标板中安装,通过应用程序对例子进行测试,改进。
首先通过串口终端输入指令把上一步生成的目标文件(这里假设为led.o)以二进制形式(命令为bina)用FTP下载到uClinux的/var目录下(因为此
目录是可写的),而后用insmod命令加载led.o驱动程序,此时操作系统会调用驱动程序的module-init操作,对本例是执行lcd.init函数完成驱动程序的注册。insmod命令执行正常,会在串口终端输出“Charlcd device is registered.”字符串,表明此字符设备已经正确加载,为了确认此设备是否真的已经在操作系统中存在,可用lsmod命令或查看/proc/devices文件的内容来确认。/proc/devices文件是uClinux保存当前系统设备的一个特殊文件,以纯文本形式存储。例如,执行“cat/proc/devices”,如正常,系统会有以下输出:
Character devices:
1 mem
2 pty
254 charlcd -这是驱动程序中申请的设备主编号和设备名称。
此步说明驱动程序已经成功地动态加载到内核,如何用应用程序访问驱动程序呢?就是采用操作系统的文件操作就可以访问驱动程序,进而访问硬件设备,下面给出一个简单的应用程序例程片断,用来访问之前动态加载的驱动程序:
int main(void)
{
int i;
char buf[20];
int iResult=open("/dev/charlcd",O-RDWR);
if(iResult==0)
{
printf("Open/dev/charlcd ok!/n");
read(iResult,buf,1);//读取8个开关状态
printf("Key Status:%.2x /n",buf[O]);
buf[0]=0x55;
printf("LED Status:%.2x /n",buf[O]);
write(iResult,buf,1);//设置8个发光二极管亮灭
close(iResult);
}
else
{
printf("Error on open/dev/charlcd!/n");
}
return 1;
}
read(iResult,bur,i)函数执行时会调用驱动程序的read操作对应的函数lcd-read(struct file *file,char *userBuf,size-t sz-buf,loff-t *lf)进行操作,把iResult传给file、buf传给userBuf、i传给sz-buf;write(iResult,buf,1)函数执行时会调用驱动程序的write操作对应的函数led write(struct file*file,const char* buf,size-t sz.buf,loff-t *lf)进行操作,把iResult传给file、buf传递给buf、i传递给sz-buf。
通过以上方法可像调试应用程序一样来迅速测试驱动程序,通过调试输出语句printk可调试驱动程序,当然驱动程序调试完毕后,发布时还应该用静态编译到内核的方式把驱动程序固化到内核中,uClinux下把驱动程序静态编译到内核已有文献介绍 ,本文不再赘述。

2 结语
介绍了uClinux下可加载内核驱动程序模块的实现过程,这种方式与原有静态编译到内核的方式比较,在驱动程序测试,调试阶段节省了大量的时间,极大地提高了驱动程序的调试效率,使具体硬件设备尽早在uClinux的产品中使用成为可能。

深度学习助力台风路径预测:技术剖析实战应用全攻略(基于LSTM模型) 摘要:深度学习技术在台风路径预测领域展现出巨大潜力。通过融合多源气象数据,深度学习模型如LSTM能有效处理时间序列数据,捕捉台风路径变化规律。传统方法相比,深度学习模型在自动特征学习和处理复杂模式方面具有独特优势,提高了预测的准确性和实时性。然而,挑战依然存在,包括数据质量问题、模型可解释性不足以及极端台风的复杂性。未来,随着观测技术的进步和新算法的开发,深度学习将在台风路径预测中发挥更大作用,为防灾减灾提供更精准的决策支持。 阅读详情

相关推荐

【实战篇】【硅基流动API+Cherry Studio搭建个人知识库全流程解析:从零到精通的超详细指南】

在人工智能技术高速发展的今天,基于大语言模型的知识库已成为开发者提效的必备工具。然而云端服务存在响应延迟、数据隐私、硬件依赖等痛点。通过硅基流动API的深度整合,我们既能享受大模型的强大能力,又能实现本地化部署高效管理。本文将从环境配置到高阶调优,手把手带你构建企业级知识库系统。

商务合作|问题讨论|交流学习 请联系作者微信,加微信请务必注明来意,博客主页有联系方式 1051

uclinux下静态/动态加载驱动程序的方

      说明:这是我最近给单位写的一篇文档,没有什么复杂的东东,对刚接触linuxdriver的朋友或许有点帮助。文档本来是针对我们自己的产品的,有些地方(路径、mknod、动态分配主设备号等)本来应该改改,因为懒惰也没去改。 在LINUX加载驱动程序可以采用动态和静态两种方式。静态加载就是把驱动程序直接编译到内核里,系统启动后可以直接调用。静态加载的缺点是调试起来比较麻烦,

yanxiaojun1015的专栏 329

深入探究uCLinux BIOS源码及其启动机制

uCLinux 是专为没有内存管理单元(MMU)的微处理器设计的嵌入式Linux操作系统版本。它的核心设计特点是非抢占式的内核,适用于资源有限的嵌入式设备。传统Linux相比,uCLinux去除了对MMU的依赖,因此在诸多嵌入式应用中大放异彩。引导过程从设备上电开始,经过一系列的初始化阶段直至操作系统内核开始运行。其主要步骤包括:电源开启:设备接通电源,CPU执行复位操作。POST(加电自检):CPU检查自身和系统硬件是否正常。引导加载程序执行。

weixin_33557333的博客 1076

uclinux内核的console(1):数据结构

rev 0.2 快乐虾http://blog.csdn.net/lights_joy/lights@hb165.com 本文适用于ADI bf561 DSP优视BF561EVB开发板uclinux-2008r1.5-rc3 (smp patch)Visual DSP++ 5.0 (update

嵌云阁 2955

linux驱动摘要

(一) 写Linux Device Driver的基本思路  写驱动,其实主要就是三件事:实现struct file_operations中的函数。一般至少需要实现六个:int (*open) (struct inode *, struct file *);int (*release) (struct inode *, struct file *);ssize_t (*read) (struct

zjut_jasmine_tea的专栏 564

uClinux设备驱动程序的设计编写

uClinux设备驱动程序的设计编写1设备驱动程序的一般编写步骤(1) 确定设备的主设备号(2) 填充static struct file_operation这样的结构(3) 定义一个类似__init mydriver_init(void)这样的注册函数,大体如下:int __init mydriver_init(void){int rc;rc = register_chrdev(mydevic

magguang的专栏 1680

linux u 驱动程序,在uClinux中增加自己的设备驱动程序

驱动程序的使用可以按照两种方式编译,一种是静态编译进内核,另一种是编译成模块以供动态加载。由于 uClinux不支持模块动态加载,而且嵌入式Linux不能够象桌面Linux那样灵活的使用insmod/rmmod 加载卸载设备驱动程序,因而这里只介绍将设备驱动程序静态编译进uClinux内核的方法。下面以uClinux为例,介绍在一个以模块方式出现的驱动程序test。c基础之上,将其编译进内核的一系...

weixin_39796152的博客 154

uClinux下的DS1284设备驱动程序开发

2007-12-25 09:47:21 引言 ARM 器件是近年来兴起的一种低功耗、高功效的嵌入式处理器。S3C4510B 是一款针对网络处理而推出的专用处理器。在利用S3C4510B 处理器构成的嵌入式系统中,为了保证系统的可靠稳定,多数都需要看门狗;同时,在某些应用领域还需要时钟和日历服务。Dallas公司的DS1284 集成了上面两个功能。 在嵌入式

mybirdsky的专栏 436

linux系统reales,基于uCLinux系统的IEEE1394设备驱动程序的编程研究实现

本文分析了IEEE1394协议驱动层框架,介绍了uCLinux下IEEE1394设备驱动程序架构和编写IEEE1394视频设备驱动程序的步骤和方法。(1)IEEE1394网络架构IEEE1394的网络共有三层,分别是物理层、链接层及传输层。物理层主要的功能是数据的编码、译码总线的判断。链接层主要功能为封包接收、封包传送周期控制。传输层则是定义请求及响应协议,并用以实现读取、写人及锁住三个基本的...

weixin_31715171的博客 407

uclinuxlinux的区别

一、名字含义: 在uClinux这个英文单词中u表示Micro,小的意思,C表示Control,控制的意思, 所以uClinux就是Micro-Control-Linux,字面上的理解就是"针对微控制领域而设计的Linux系统"。  二、ucLinuxlinux区别? 没有虚存管理 不能运行时增加进程栈 不支持分页 可执行程序不是elf,   而是flat 不能用fork,   而...

whatday的专栏 6266

如何在嵌入式LINUX中增加自己的设备驱动程序

 驱动程序的使用可以按照两种方式编译,一种是静态编译进内核,另一种是编译成模块以供动态加载。由于uClinux不支持模块动态加载,而且嵌入式LINUX不能够象桌面LINUX那样灵活的使用insmod/rmmod加载卸载设备驱动程序,因而这里只介绍将设备驱动程序静态编译进uClinux内核的方法。  

kring Space 914

uclinux内核参数处理(1):参数接收

 快乐虾http://blog.csdn.net/lights_joy/lights@hb165.com 本文适用于ADI bf561 DSP优视BF561EVB开发板uclinux-2008r1.5-rc3 (smp patch)Visual DSP++ 5.0(update 5) 

嵌云阁 1756

insmod加载驱动后开机重启后没有驱动的解决方法

为了是在命令行中执行insmod命令安装的驱动能在重启之后还有的解决方法大致有一下两种方法: (1)直接编译内核,把想安装的驱动在编译内核的时候给编译进去,这种方法比较麻烦,花的时候也比较的多,一般不采用这种方法(这种方法网上有很多资料可以参考)。 (2)这种方法比较简单:就是在启动脚本中加载模块,这样每次开机启动都自动加载相应的驱动模块。具体的方式如下:       在文件/etc/rc.

liangxanhai的专栏 1万+

Uboot--Linux参数传递--FDT扁平设备树

ARM Linux 3.x的设备树(Device Tree) 转载:http://blog.csdn.net/21cnbao/article/details/8457546 1.              ARM Device Tree起源Linus Torvalds在2011年3月17日的ARM Linux邮件列表宣称“this whole ARM thing is af*cking pai

gx19862005的专栏 9206

uclinux驱动程序简单方便的调试方法——动态加载调试方式

这是学习之路上的一个突破。对于uclinux来说,调试驱动和应用程序是一个复杂的过程,如果每做一次修改,就要编译一次内核,其工作量是相当巨大的,所以找到一种简单方便的调试方法非常重要,而静态加载驱动和应用程序是工程的最终形式。记下建立步骤用来备忘和以飨像我这样的小菜虫们,呵呵   (uclinux源码版本为20060311)1、配置内核:   功能包括 nfs客户服务端 + 可

446

uclinux内核驱动的初始化顺序

快乐虾http://blog.csdn.net/lights_joy/lights@hb165.com  本文适用于ADSP-BF561uclinux-2008r1.5-rc3 (smp patch)Visual DSP++ 5.0(update 5)  欢迎转载,但请保留作者信息 在内核中有许多的驱动,这些驱动之间有一些具有依赖关系,那么内核

嵌云阁 2286

AR 接入路由器产品文档

华为所有路由器交换机配置简介,本文档针对用户在安装、配置和维护设备过程中遇见的常见问题,给出相应的解答。

上一篇: 基于uclinux 的CAN 总线嵌入式驱动编程
下一篇: uClinux内存管理分析
mybirdsky
博客等级 码龄19年 415粉丝 2035原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值