当前位置:首页 > 嵌入式 > 嵌入式教程
[导读]该实验是编写最简单的字符驱动程序,这里的设备也就是一段内存,实现简单的读写功能,并列出常用格式的Makefile以及驱动的加载和卸载脚本。读者可以熟悉字符设备驱动的整个编写流程。

11.7实验内容——test驱动1.实验目的

该实验是编写最简单的字符驱动程序,这里的设备也就是一段内存,实现简单的读写功能,并列出常用格式的Makefile以及驱动的加载和卸载脚本。读者可以熟悉字符设备驱动的整个编写流程。

2.实验内容

该实验要求实现对虚拟设备(一段内存)的打开、关闭、读写的操作,并要通过编写测试程序来测试虚拟设备及其驱动运行是否正常。

3.实验步骤

(1)编写代码。

这个简单的驱动程序的源代码如下所示:

/*test_drv.c*/

#include<linux/module.h>

#include<linux/init.h>

#include<linux/fs.h>

#include<linux/kernel.h>

#include<linux/slab.h>

#include<linux/types.h>

#include<linux/errno.h>

#include<linux/cdev.h>

#include<asm/uaccess.h>

#defineTEST_DEVICE_NAME"test_dev"

#defineBUFF_SZ1024

/*全局变量*/

staticstructcdevtest_dev;

unsignedintmajor=0;

staticchar*data=NULL;

/*读函数*/

staticssize_ttest_read(structfile*file,

char*buf,size_tcount,loff_t*f_pos)

{

intlen;

if(count<0)

{

return-EINVAL;

}

len=strlen(data);

count=(len>count)?count:len;

if(copy_to_user(buf,data,count))/*将内核缓冲的数据拷贝到用户空间*/

{

return-EFAULT;

}

returncount;

}

/*写函数*/

staticssize_ttest_write(structfile*file,constchar*buffer,

size_tcount,loff_t*f_pos)

{

if(count<0)

{

return-EINVAL;

}

memset(data,0,BUFF_SZ);

count=(BUFF_SZ>count)?count:BUFF_SZ;

if(copy_from_user(data,buffer,count))/*将用户缓冲的数据复制到内核空间*/

{

return-EFAULT;

}

returncount;

}

/*打开函数*/

staticinttest_open(structinode*inode,structfile*file)

{

printk("Thisisopenoperation\n");

/*分配并初始化缓冲区*/

data=(char*)kmalloc(sizeof(char)*BUFF_SZ,GFP_KERNEL);

if(!data)

{

return-ENOMEM;

}

memset(data,0,BUFF_SZ);

return0;

}

/*关闭函数*/

staticinttest_release(structinode*inode,structfile*file)

{

printk("Thisisreleaseoperation\n");

if(data)

{

kfree(data);/*释放缓冲区*/

data=NULL;/*防止出现野指针*/

}

return0;

}

/*创建、初始化字符设备,并且注册到系统*/

staticvoidtest_setup_cdev(structcdev*dev,intminor,

structfile_operations*fops)

{

interr,devno=MKDEV(major,minor);

cdev_init(dev,fops);

dev->owner=THIS_MODULE;

dev->ops=fops;

err=cdev_add(dev,devno,1);

if(err)

{

printk(KERN_NOTICE"Error%daddingtest%d",err,minor);

}

}

/*虚拟设备的file_operations结构*/

staticstructfile_operationstest_fops=

{

.owner=THIS_MODULE,

.read=test_read,

.write=test_write,

.open=test_open,

.release=test_release,

};

/*模块注册入口*/

intinit_module(void)

{

intresult;

dev_tdev=MKDEV(major,0);

if(major)

{/*静态注册一个设备,设备号先前指定好,并设定设备名,用cat/proc/devices来查看*/

result=register_chrdev_region(dev,1,TEST_DEVICE_NAME);

}

else

{

result=alloc_chrdev_region(&dev,0,1,TEST_DEVICE_NAME);

}

if(result<0)

{

printk(KERN_WARNING"Testdevice:unabletogetmajor%d\n",major);

returnresult;

}

test_setup_cdev(&test_dev,0,&test_fops);

printk("Themajorofthetestdeviceis%d\n",major);

return0;

}

/*卸载模块*/

voidcleanup_module(void)

{

cdev_del(&test_dev);

unregister_chrdev_region(MKDEV(major,0),1);

printk("Testdeviceuninstalled\n");

}

(2)编译代码。

虚拟设备的驱动程序的Makefile如下所示:

ifeq($(KERNELRELEASE),)

KERNELDIR?=/lib/modules/$(shelluname-r)/build/*内核代码编译路径*/

PWD:=$(shellpwd)

modules:

$(MAKE)-C$(KERNELDIR)M=$(PWD)modules

modules_install:

$(MAKE)-C$(KERNELDIR)M=$(PWD)modules_install

clean:

rm-rf*.o*~core.depend.*.cmd*.ko*.mod.c.tmp_versions

.PHONY:modulesmodules_installclean

else

obj-m:=test_drv.o/*将生成的模块为test_drv.ko*/

endif

(3)加载和卸载模块。

通过下面两个脚本代码分别实现驱动模块的加载和卸载。

加载脚本test_drv_load如下所示:

#!/bin/sh

#驱动模块名称

module="test_drv"

#设备名称。在/proc/devices中出现

device="test_dev"

#设备文件的属性

mode="664"

group="david"

#删除已存在的设备节点

rm-f/dev/${device}

#加载驱动模块

/sbin/insmod-f./$module.ko$*||exit1

#查到创建设备的主设备号

major=`cat/proc/devices|awk"\\$2==\"$device\"{print\\$1}"`

#创建设备文件节点

mknod/dev/${device}c$major0

#设置设备文件属性

chgrp$group/dev/${device}

chmod$mode/dev/${device}

卸载脚本test_drv_unload如下所示:

#!/bin/sh

module="test_drv"

device="test_dev"

#卸载驱动模块

/sbin/rmmod$module$*||exit1

#删除设备文件

rm-f/dev/${device}

exit0

(6)编写测试代码。

最后一步是编写测试代码,也就是用户空间的程序,该程序调用设备驱动来测试驱动的运行是否正常。以下实例只实现了简单的读写功能,测试代码如下所示:

/*test.c*/

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<sys/stat.h>

#include<sys/types.h>

#include<unistd.h>

#include<fcntl.h>

#defineTEST_DEVICE_FILENAME"/dev/test_dev"/*设备文件名*/

#defineBUFF_SZ1024/*缓冲大小*/

intmain()

{

intfd,nwrite,nread;

charbuff[BUFF_SZ];/*缓冲区*/

/*打开设备文件*/

fd=open(TEST_DEVICE_FILENAME,O_RDWR);

if(fd<0)

{

perror("open");

exit(1);

}

do

{

printf("Inputsomewordstokernel(enter'quit'toexit):");

memset(buff,0,BUFF_SZ);

if(fgets(buff,BUFF_SZ,stdin)==NULL)

{

perror("fgets");

break;

}

buff[strlen(buff)-1]='\0';

if(write(fd,buff,strlen(buff))<0)/*向设备写入数据*/

{

perror("write");

break;

}

if(read(fd,buff,BUFF_SZ)<0)/*从设备读取数据*/

{

perror("read");

break;

}

else

{

printf("Thereadstringisfromkernel:%s\n",buff);

}

}while(strncmp(buff,"quit",4));

close(fd);

exit(0);

}

4.实验结果

首先在虚拟设备驱动源码目录下编译并加载驱动模块。

$makeclean;make

$./test_drv_load

接下来,编译并运行测试程序

$gcc–otesttest.c

$./test

测试程序运行效果如下:

Inputsomewordstokernel(enter'quit'toexit):Hello,everybody!

Thereadstringisfromkernel:Hello,everybody!/*从内核读取的数据*/

Inputsomewordstokernel(enter'quit'toexit):Thisisasimpledriver

Thereadstringisfromkernel:Thisisasimpledriver

Inputsomewordstokernel(enter'quit'toexit):quit

Thereadstringisfromkernel:quit

最后,卸载驱动程序

$./test_drv_unload

通过dmesg命令可以查看内核打印的信息:

$dmesg|tail–n10

……

Themajorofthetestdeviceis250/*当加载模块时打印*/

Thisisopenoperation/*当打开设备时打印*/

Thisisreleaseoperation/*关闭设备时打印*/

Testdeviceuninstalled/*当卸载设备时打印*/

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭