1led驱动的实现原理
Linux驱动不直接与硬件打交道,而是通过i/o内存作为中介,具体关系如下图
2编写led驱动
2.1创建led驱动的设备文件
在统计单词数量驱动中使用misc_register创建设备文件,该函数只能创建设备号为10的设备文件
所以想要创建其他设备号的设备文件,就要使用:
cdev_init初始化cdev
查看cdev结构体的定义在内核源代码的/include/linux/cdev.h文件中定义,内容如下:
structcdev{
structkobjectkobj;//封装设备文件的对象
structmodule*owner;//指向内核模块的指针
conststructfile_operations*ops;//file_operation结构体指针
structlist_headlist;//指向上一个下一个cdev结构体的指针
dev_tdev;//dev_t是int数据类型,表示设备号,前12位表示主设备号,后20位表示次
设备号
unsignedintcount;//请求的链接设备的编号的范围
};
Cdev_init()函数位于linux/fs/char_dev.c文件中,代码内容如下:
/**
*cdev_init()-initializeacdevstructure
*@cdev:thestructuretoinitialize
*@fops:thefile_operationsforthisdevice
*
*Initializes@cdev,remembering@fops,makingitreadytoaddtothe
*systemwithcdev_add().
*/
voidcdev_init(structcdev*cdev,conststructfile_operations*fops)
{
//将结构体中成员变量清零
memset(cdev,0,sizeof*cdev);
//初始化首尾指针
INIT_LIST_HEAD(&cdev->list);
//初始化设备对象
kobject_init(&cdev->kobj,&ktype_cdev_default);
//关链cdev和file_operation
cdev->ops=fops;
}
指定设备号
设备号分为主设备号和次设备号,分配方式有以下两种方法:
直接在代码中指定,使用register_chrdev_region()函数定义在inux/fs/char_dev.c文件中,内容如下
/**
*register_chrdev_region()-registerarangeofdevicenumbers静态指定设备号
*@from:thefirstinthedesiredrangeofdevicenumbers;mustinclude
*themajornumber.设备号
*@count:thenumberofconsecutivedevicenumbersrequired表示次设备号范围
*@name:thenameofthedeviceordriver.表示设备文件名称
*
*Returnvalueiszeroonsuccess,anegativeerrorcodeonfailure.
*/
intregister_chrdev_region(dev_tfrom,unsignedcount,constchar*name)
{
structchar_device_struct*cd;
dev_tto=from+count;
dev_tn,next;
for(n=from;n<to;n=next){
next=MKDEV(MAJOR(n)+1,0);//将主设备号和次设备号合成设备号
if(next>to)
next=to;
//MAJOR主设备号,MINOR次设备号
cd=__register_chrdev_region(MAJOR(n),MINOR(n),
next-n,name);
if(IS_ERR(cd))
gotofail;
}
return0;
fail:
to=n;
for(n=from;n<to;n=next){
next=MKDEV(MAJOR(n)+1,0);
kfree(__unregister_chrdev_region(MAJOR(n),MINOR(n),next-n));
}
returnPTR_ERR(cd);
}
动态分配使用alloc_chrdev_region函数,定义在inux/fs/char_dev.c文件中,内容如下:
/**
*alloc_chrdev_region()-registerarangeofchardevicenumbers动态分配设备编号
*@dev:outputparameterforfirstassignednumber设备号指针
*@baseminor:firstoftherequestedrangeofminornumbers次设备号
*@count:thenumberofminornumbersrequired表示分配的次设备号范围
*@name:thenameoftheassociateddeviceordriver表示设备文件名称
*
*Allocatesarangeofchardevicenumbers.Themajornumberwillbe
*chosendynamically,andreturned(alongwiththefirstminornumber)
*in@dev.Returnszerooranegativeerrorcode.
*/
intalloc_chrdev_region(dev_t*dev,unsignedbaseminor,unsignedcount,
constchar*name)
{
structchar_device_struct*cd;
cd=__register_chrdev_region(0,baseminor,count,name);
if(IS_ERR(cd))
returnPTR_ERR(cd);
*dev=MKDEV(cd->major,cd->baseminor);
return0;
}
Cdev_add将字符设备添加到内核中的字符设备组中
函数定义在inux/fs/char_dev.c文件中,内容如下:
/**
*cdev_add()-addachardevicetothesystem将字符设备添加到内核字符设备数组中
*@p:thecdevstructureforthedevice设备文件指针
*@dev:thefirstdevicenumberforwhichthisdeviceisresponsible设备号
*@count:thenumberofconsecutiveminornumberscorrespondingtothis设备文件数量
*device
*
*cdev_add()addsthedevicerepresentedby@ptothesystem,makingit
*liveimmediately.Anegativeerrorcodeisreturnedonfailure.
*/
intcdev_add(structcdev*p,dev_tdev,unsignedcount)
{
p->dev=dev;
p->count=count;
//将字符设备添加到probes数组中,该函数定义在linux/drivers/base/map.c
returnkobj_map(cdev_map,dev,count,NULL,exact_match,exact_lock,p);
}
class_create该宏用于创建struct_class,实际上调用的是__create_class()函数定义在drivers/base/class.c文件中
/*Thisisa#definetokeepthecompilerfrommergingdifferent
*instancesofthe__keyvariable*/
#defineclass_create(owner,name) \
({ \
staticstructlock_class_key__key; \
__class_create(owner,name,&__key);\
})
device_create创建设备文件
函数定义在drivers/base/core.c文件中,内容如下:
structdevice*device_create(structclass*class,structdevice*parent,
dev_tdevt,void*drvdata,constchar*fmt,...)
{
va_listvargs;
structdevice*dev;
va_start(vargs,fmt);
dev=device_create_vargs(class,parent,devt,drvdata,fmt,vargs);
va_end(vargs);
returndev;
}
创建设备文件的示例代码如下:
//定义设备文件名称
#defineDEVICE_NAME"mini6410_leds"
//创建设备文件的数量
#defineDEVICE_COUNT1
//默认主设备号
#defineMINI6410_LEDS_MAJOR0
//默认次设备号
#defineMINI6410_LEDS_MINOR234
//主设备号
staticintmajor=MINI6410_LEDS_MAJOR;
//次设备号
staticintminor=MINI6410_LEDS_MINOR;
//设备号
staticdev_tdev_number;
//structclass
staticstructclass*leds_class=NULL;
//设备文件回调指针
staticstructfile_operationsdev_fops={
.ower=THIS_MODULE,
.unlocked_ioctl=mini6410_leds_ioctl,
.write=mini6410_leds_write
};
//描述字符设备的structcdev
staticstructcdevleds_cdev;
//创建设备文件
staticintleds_create_device(void){
intret=0;
interr=0;
//初始化cdev成员
cdev_init(&leds_cdev,&dev_fops);
//创建的设备文件属于当前的驱动模块
leds_cdev.owner=THIS_MODULE;
//主设备号大于0,通过指定设备号的方式注册字符设备
if(major>0){
//获取设备号
dev_number=MKDEV(major,minor);
//通过指定设备号的方式注册字符设备区域
err=register_chrdev_region(dev_number,DEVICE_COUNT,DEVICE_NAME);
//注册失败
if(err<0){
printk(KERN_WARNING"register_chrdev_region()failed\n");
returnerr;
}
}
else{//主设备号为0,自动分配主设备号和次设备号
//通过自动分配主设备号和次设备号的方式
//10表示起始次设备号
err=alloc_chrdev_region(&leds_cdev.dev,10,DEVICE_COUNT,DEVICE_NAME);
//注册失败
if(err<0){
printk(KERN_WARNING"alloc_chrdev_region()failed\n");
returnerr;
}
//获取主设备号
major=MAJOR(leds_cdev.dev);
//获取次设备号
minor=MINOR(leds_cdev.dev);
//自动分配的设备号
dev_number=leds_cdev.dev;
}
//将字符设备添加到内核中的字符设备数组中
cdev_add(&leds_cdev,dev_number,DEVICE_COUNT);
//创建structclass
leds_class=class_create(THIS_MODULE,DEVICE_NAME);
//创建设备文件
device_create(leds_class,NULL,dev_number,NULL,DEVICE_NAME);
returnret;
}
LED驱动的初始化函数
Staticintleds_init(void){
Intret;
//创建设备文件
Ret=leds_create_device();
Printk(DEVICE_NAME”\tinitialized\n”);
Returnret;
}
Module_init(leds_init);
卸载led驱动的设备文件
Device_destory//函数原型如下,代码位于drivers/base/core.c文件中
/**
*device_destroy-removesadevicethatwascreatedwithdevice_create()移除建立的字符设备
*@class:pointertothestructclassthatthisdevicewasregisteredwith
*@devt:thedev_tofthedevicethatwaspreviouslyregistered
*
*Thiscallunregistersandcleansupadevicethatwascreatedwitha
*calltodevice_create().
*/
voiddevice_destroy(structclass*class,dev_tdevt)
{
structdevice*dev;
dev=class_find_device(class,NULL,&devt,__match_devt);
if(dev){
put_device(dev);
device_unregister(dev);
}
}
Class_destory//销毁sturctclass,函数定义在drivers/base/class.c文件中
/**
*class_destroy-destroysastructclassstructure销毁sturctclass
*@cls:pointertothestructclassthatistobedestroyed
*
*Note,thepointertobedestroyedmusthavebeencreatedwithacall
*toclass_create().
*/
voidclass_destroy(structclass*cls)
{
if((cls==NULL)||(IS_ERR(cls)))
return;
class_unregister(cls);
}
Unregister_chrdev_region//注销字符设备区域,函数位于linux/fs/char_dev.c文件中
/**
*unregister_chrdev_region()-returnarangeofdevicenumbers注销字符设备区域
*@from:thefirstintherangeofnumberstounregister
*@count:thenumberofdevicenumberstounregister
*
*Thisfunctionwillunregisterarangeof@countdevicenumbers,
*startingwith@from.Thecallershouldnormallybetheonewho
*allocatedthosenumbersinthefirstplace...
*/
voidunregister_chrdev_region(dev_tfrom,unsignedcount)
{
dev_tto=from+count;
dev_tn,next;
for(n=from;n<to;n=next){
next=MKDEV(MAJOR(n)+1,0);
if(next>to)
next=to;
kfree(__unregister_chrdev_region(MAJOR(n),MINOR(n),next-n));
}
}
示例代码如下
//移除设备文件
Staticvoidleds_destroy_device(void){
//移除创建的字符设备
Device_destroy(leds_class,dev_number);
//销毁structclass
If(leds_class){
Class_destroy(leds_class);
}
//注销字符设备区域
Unretister_chrdev_retion(dev_number,DEVICE_NAME);
Return;
}
Staticvoidleds_exit(void){
//卸载led设备驱动
Leds_destroy();
Printk(DEVICE_NAME”\tesit!\n”);
}
Module_exit(leds_exit);
设置寄存器与初始化LED驱动
设置led引角的状态,打开或禁止上拉电路来控制led的亮或灭
Led有两个引角,gpb0和gpb1,一个引角连接arm处理器的gpio接口,另一个经过一个限流电阻连接到电源,当gpio端为低电平时,led亮
控制led需要三个寄存器来完成:
查看芯片手册得到其值
gpkcon(端口配置寄存器)0x7f008800
gpkdat(数据端口寄存器)0x7f008808
gpkpud(端口上拉电路寄存器)0x7f00880c
每个寄存器使用四个字节,也就是一个int整型数据
将pgkcon4567设为output
将gpkdat0表示亮,1表示灭
将gpkpud10表示打开上拉电路
寄存器虚拟地址对应的宏
定义在linux/arch/arm/mach-s3c64xx/include/mach/gpio-bank-k.h
文件中,示例代码如下:
#defineS3C64XX_GPKCON (S3C64XX_GPK_BASE+0x00)
#defineS3C64XX_GPKCON1 (S3C64XX_GPK_BASE+0x04)
#defineS3C64XX_GPKDAT (S3C64XX_GPK_BASE+0x08)
#defineS3C64XX_GPKPUD (S3C64XX_GPK_BASE+0x0c)
linux/arch/arm/plat-s3c64xx/include/mach/regs-gpio.h
#defineS3C64XX_GPK_BASE S3C64XX_GPIOREG(0x0800)
#defineS3C64XX_GPIOREG(reg) (S3C64XX_VA_GPIO+(reg))
linux/arch/arm/mach-s3c6400/include/mach/map.h
#defineS3C64XX_VA_GPIO S3C_ADDR_CPU(0x00000000)
linux/arch/arm/plat-samsung/include/plat/map-base.h
#defineS3C_ADDR_CPU(x) S3C_ADDR(0x00500000+(x))
#ifndef__ASSEMBLY__
#defineS3C_ADDR(x) ((void__iomem__force*)S3C_ADDR_BASE+(x))
#else
#defineS3C_ADDR(x) (S3C_ADDR_BASE+(x))
#defineS3C_ADDR_BASE 0xF6000000
初始化寄存器的函数如下:
staticvoidleds_init_gpk(intleds_default){
inttmp=0;
//------------初始化gpkcon寄存器----------
tmp=ioread32(S3C64XX_GPKCON);
//保留低16位的值,高16位清零
tmp&=(~0xFFFF0000);
//将低16位设为1111
tmp|=0x11110000;
//向gpmcon写入数据
iowrite32(tmp,S3C64XX_GPKCON);
//----------初始化gpkpud寄存器-----------
//读取gpkpud寄存器的当前值
tmp=ioread32(S3C64XX_GPKPUD);
//4567位清零
tmp&=(~0xFF00);
//打开led上拉电路
tmp|=0xAA00;
//向gpkpud写入数据
iowrite32(tmp,S3C64XX_GPKPUD);
//----------初始化gpkdat寄存器-----------
//读取gpkdat寄存器的当前值
tmp=ioread32(S3C64XX_GPKDAT);
//将4567位清零
tmp&=(~0xF0);
//设置四个led默认的亮灭状态
tmp|=leds_default;
//向gpkdat寄存器写入数据
iowrite32(tmp,S3C64XX_GPKDAT);
}
在leds_init()中调用该函数即可
staticintleds_init(void){
intret;
ret=leds_create_device();
//初始化寄存器
//11000000两亮两灭,0表示亮,1表示灭
leds_init_gpk(0xC0);
printk(DEVICE_NAME"\tinitialiaed\n");
returnret;
}
控制led
可以通过以下两种方式控制led:
通过字符串控制led需要使用file_operation.write
通过I/O命令控制led需要使用fiel_operation.ioctl
接收数据的函数如下mini6410_leds_write():
示例代码如下:
//保存四个led的设置状态
staticunsignedcharmem[4];
//接收向设备写入的字符串
staticssize_tmini6410_leds_write(structfile*file,constchar_user*buf,size_tcount,loff_t*ppos){
unsignedtmp=count;
unsignedlongi=0;
//初始化mem数组的值
memset(mem,0,4);
//最多写入四个字符,多余字符将忽略
if(count>4){
tmp=4;
}
//从用户空间向内核空间写入数据
if(copy_from_user(mem,buf,tmp)){
return-EFAULT;
}else{
//依次控制四个led
for(i=0;i<4;i++){
//读取gpkdat寄存器的当前值
tmp=ioread32(S3C64XX_GPKDAT);
//如果字符为1则点亮当前的led
if(mem[i]=='1'){
tmp&=(~(1<<(i+4)));
}else{//如果字符为0则灭掉当前的led
tmp|=(1<<(i+4));
}
iowrite32(tmp,S3C64XX_GPKDAT);
}
returncount;
}
}
可以通过以下命令控制led的亮灭
Adbshell‘echo1100>/dev/mini6410_leds’
接收命令和参数的函数
//filp表示led驱动的设备文件
//cmd表示开关
//arg表示哪个led
staticlongmini6410_leds_ioctl(structfile*filp,unsignedintcmd,unsignedlongarg){
//命令只能是0/1
switch(cmd){
unsignedtmp;
case0:
case1:
if(arg>4){
return-EINVAL;
}
//读取gpkdat寄存器的当前值
tmp=ioread32(S3C64XX_GPKDAT);
if(cmd==1){//开灯
tmp&=(~(1<<(arg+4)));
}else{//关灯
tmp|=(1<<(arg+4));
}
//向gpkdat写入数据
iowrite32(tmp,S3C64XX_GPKDAT);
return0;
default:
return-EINVAL;
}
}
LED驱动的模块参数
如果相要在装载led驱动时指定默认状态,就要使用模块参数
为linux指定一个参数需要使用module_param(name,//参数名
type,//参数类型
perm)//表示读写权限
使用宏指定参数后,会在sys/module目录下生成设备文件同名的文件目录,即
/sys/module/mini6410_leds/parameters,如果有多个参数,会在该目录下生成param*个文件
为led添加一个模块参数,示例代码如下:
//保存模块参数值的变量
staticintleds_state=1;
//初始化led驱动
staticintleds_init(void){
intret;
ret=leds_create_device();
//初始化寄存器
leds_init_gpk(~leds_state);
printk(DEVICE_NAME"\tinitialized\n");
returnret;
}
//指定模块参数
module_param(leds_stat,int,S_IRUGO|S_IWUSR);
LED驱动的完整代码如下:
#include<linux/fs.h>
#include<linux/cdev.h>
#include<linux/pci.h>
#include<asm/uaccess.h>
#include<mach/map.h>
#include<mach/regs-gpio.h>
#include<mach/gpio-bank-k.h>
//定义设备文件名称
#defineDEVICE_NAME"mini6410_leds"
//创建设备文件的数量
#defineDEVICE_COUNT1
//默认主设备号
#defineMINI6410_LEDS_MAJOR0
//默认次设备号
#defineMINI6410_LEDS_MINOR234
//定义数组类型的模块参数值个数
#definePARAM_SIZE3
//保存四个led设置状态的数组
staticunsignedcharmem[4];
//主设备号
staticintmajor=MINI6410_LEDS_MAJOR;
//次设备号
staticintminor=MINI6410_LEDS_MINOR;
//设备号
staticdev_tdev_number;
//当前led的状态,通过模块参数传入
staticintleds_state=1;
//数组类型模块参数的默认值
staticchar*params[]={"string1","string2","string3"};
//数组类型模块参数值的个数
staticintparam_size=PARAM_SIZE;
//structclass表示led字符设备的结构体
staticstructclass*leds_class=NULL;
//描述字符设备的structcdev
staticstructcdevleds_cdev;
//------------接收命令和参数的函数
//filp表示led驱动的设备文件
//cmd表示开关
//arg表示哪个led
staticlongmini6410_leds_ioctl(structfile*filp,unsignedintcmd,unsignedlongarg){
//命令只能是0/1
switch(cmd){
unsignedtmp;
case0:
case1:
if(arg>4){
return-EINVAL;
}
//读取gpkdat寄存器的当前值
tmp=ioread32(S3C64XX_GPKDAT);
if(cmd==1){//开灯
tmp&=(~(1<<(arg+4)));
}else{//关灯
tmp|=(1<<(arg+4));
}
//向gpkdat写入数据
iowrite32(tmp,S3C64XX_GPKDAT);
return0;
default:
return-EINVAL;
}
}
//-----------接收向设备写入的字符串
staticssize_tmini6410_leds_write(structfile*file,constchar__user*buf,size_tcount,loff_t*ppos){
unsignedtmp=count;
unsignedlongi=0;
//初始化mem数组的值
memset(mem,0,4);
//最多写入四个字符,多余字符将忽略
if(count>4){
tmp=4;
}
//从用户空间向内核空间写入数据
if(copy_from_user(mem,buf,tmp)){
return-EFAULT;
}else{
//依次控制四个led
for(i=0;i<4;i++){
//读取gpkdat寄存器的当前值
tmp=ioread32(S3C64XX_GPKDAT);
//如果字符为1则点亮当前的led
if(mem[i]=='1'){
tmp&=(~(1<<(i+4)));
}else{//如果字符为0则灭掉当前的led
tmp|=(1<<(i+4));
}
iowrite32(tmp,S3C64XX_GPKDAT);
}
returncount;
}
}
//定义file_operation结构体
//设备文件回调指针
staticstructfile_operationsdev_fops={
.owner=THIS_MODULE,
.unlocked_ioctl=mini6410_leds_ioctl,
.write=mini6410_leds_write
};
//-------创建设备文件
staticintleds_create_device(void){
intret=0;
interr=0;
//初始化cdev成员
cdev_init(&leds_cdev,&dev_fops);
//创建的设备文件属于当前的驱动模块
leds_cdev.owner=THIS_MODULE;
//主设备号大于0,通过指定设备号的方式注册字符设备
if(major>0){
//获取设备号
dev_number=MKDEV(major,minor);
//通过指定设备号的方式注册字符设备区域
err=register_chrdev_region(dev_number,DEVICE_COUNT,DEVICE_NAME);
//注册失败
if(err<0){
printk(KERN_WARNING"register_chrdev_region()failed\n");
returnerr;
}
}
else{//主设备号为0,自动分配主设备号和次设备号
//通过自动分配主设备号和次设备号的方式
//10表示起始次设备号
err=alloc_chrdev_region(&leds_cdev.dev,10,DEVICE_COUNT,DEVICE_NAME);
//注册失败
if(err<0){
printk(KERN_WARNING"alloc_chrdev_region()failed\n");
returnerr;
}
//获取主设备号
major=MAJOR(leds_cdev.dev);
//获取次设备号
minor=MINOR(leds_cdev.dev);
//自动分配的设备号
dev_number=leds_cdev.dev;
}
//将字符设备添加到内核中的字符设备数组中
cdev_add(&leds_cdev,dev_number,DEVICE_COUNT);
//创建structclass
leds_class=class_create(THIS_MODULE,DEVICE_NAME);
//创建设备文件
device_create(leds_class,NULL,dev_number,NULL,DEVICE_NAME);
returnret;
}
//----------初始化寄存器
staticvoidleds_init_gpk(intleds_default){
inttmp=0;
//------------初始化gpkcon寄存器----------
tmp=ioread32(S3C64XX_GPKCON);
//保留低16位的值,高16位清零
tmp&=(~0xFFFF0000);
//将低16位设为1111
tmp|=0x11110000;
//向gpmcon写入数据
iowrite32(tmp,S3C64XX_GPKCON);
//----------初始化gpkpud寄存器-----------
//读取gpkpud寄存器的当前值
tmp=ioread32(S3C64XX_GPKPUD);
//4567位清零
tmp&=(~0xFF00);
//打开led上拉电路
tmp|=0xAA00;
//向gpkpud写入数据
iowrite32(tmp,S3C64XX_GPKPUD);
//----------初始化gpkdat寄存器-----------
//读取gpkdat寄存器的当前值
tmp=ioread32(S3C64XX_GPKDAT);
//将4567位清零
tmp&=(~0xF0);
//设置四个led默认的亮灭状态
tmp|=leds_default;
//向gpkdat寄存器写入数据
iowrite32(tmp,S3C64XX_GPKDAT);
}
//销毁字符设备
staticvoidleds_destroy_device(void){
//销毁字符设备
device_destroy(leds_class,dev_number);
//销毁class结构体
if(leds_class){
class_destroy(leds_class);
}
//注销字符设备区
unregister_chrdev_region(dev_number,DEVICE_COUNT);
return;
}
//-----------------------------------------------------
//初始化led驱动
staticintleds_init(void){
intret;
ret=leds_create_device();
leds_init_gpk(~leds_state);
printk(DEVICE_NAME"\tinitialized\n");
printk("param0\t%s\n",params[0]);
printk("param1\t%s\n",params[1]);
printk("param2\t%s\n",params[2]);
returnret;
}
//卸载led驱动
staticintleds_exit(void){
leds_destroy_device();
printk(DEVICE_NAME"\texit!\n");
}
//指定初始化函数
module_init(leds_init);
//指定卸载函数
module_exit(leds_exit);
//指定int类型的模块参数
module_param(leds_state,int,S_IRUGO|S_IWUSR);
//指定数组类型的模块参数
module_param_array(params,charp,¶m_size,S_IRUGO|S_IWUSR);
//指定开源协议
MODULE_LICENSE("GPL");
//指定驱动作者
MODULE_AUTHOR("retacn_yue");
编译
A编译成动态驱动模块进行测试
脚本build.sh文件内容如下:
source./build_mini6410.sh
脚本build_mini6410.sh文件的内容如下:
#build_mini6410.sh
source/opt/linux/driver/common.sh
make-C$MINI6410_ANDROID_KERNEL_PATHM=${PWD}
find_devices
if["$selected_device"==""];then
exit
else
adb-s$selected_devicepush${PWD}/mini6410_leds.ko/data/local
testing=$(adb-s$selected_deviceshelllsmod|grep"mini6410_leds")
if["$testing"!=""];then
adb-s$selected_deviceshellrmmodmini6410_leds
fi
adb-s$selected_deviceshell"insmod/data/local/mini6410_leds.ko"
adb-s$selected_deviceshell"chmod777/dev/mini6410_leds"
fi
Makefile文件内容如下:
obj-m:=mini6410_leds.o
编译自动将.ko文件上传到开发板
直接使用adbshell进行测试
Adbshell“echo‘1010’>/dev/mini6410_leds”
使用test_leds.sh进行测试(向led设备发送字符串来测试),示例代码如下:
#test_leds.sh
for((i=0;i<16;i=i+1))
do
#将十进制转换为二进制形式
n=$(echo"obase=2;$i"|bc)
echo$n
echo$n>temp
#反转二进制数
n=$(revtemp)
#向led设备发送控制字符串
adbshell"echo$n>/dev/mini6410_leds"
#延迟一秒钟
sleep1
done
B也可以将驱动程序像之前一样编译进android内核,然后进行测试
将动驱程序编译进android系统的linux内核进行测试
将mini6410_leds.c放到linux内核代码中(由于android内核中已经有一个mini6410_leds.c文件,将原文件改名为mini6410_leds_old.c)
root@vm:/opt/kernel/linux-2.6.36-android/drivers/char#cp/opt/linux/driver/mini6410_leds/mini6410_leds.c./
修改kconfig文件,在endmenu前添加如下代码:
//源代码中已有不需要再添加
configMINI6410_LEDS
tristate"LEDSupportforMini6410GPIOLEDs"
dependsonCPU_S3C6410
defaulty
help
ThisoptionenablessupportforLEDsconnectedtoGPIOlines
onMini6410boards.
C修改makefile文件,添加如下内容
//源代码中已有所以不需要添加
obj-$(CONFIG_MINI6410_LEDS)+=mini6410_leds.o
obj-$(CONFIG_MINI6410_HELLO_MODULE) +=mini6410_hello_module.o
obj-$(CONFIG_MINI6410_BUTTONS) +=mini6410_buttons.o
obj-$(CONFIG_MINI6410_BUZZER) +=mini6410_pwm.o
obj-$(CONFIG_MINI6410_ADC) +=mini6410_adc.o
D设置.config文件,可以通过makemenuconfig来进行配置
DeviceDrivers--->Characterdevices--->[*]word_count_driver
打开.config文件可以看到该项设置为y
CONFIG_MINI6410_LEDS=y
E编译linux内核
Make
烧写到开发板进行测试
/#chmod777/dev/mini6410_leds
使用adbshell进行测试
/dev#echo'1010'>/dev/mini6410_leds
测试led驱动
编写测试i/o命令的测试程序
Test_ioctl.c文件内容如下:
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/ioctl.h>
intmain(intargc,char**argv)
{
//三个命令行参数
intfile_handler=0;
intcmd=0;
intarg=0;
if(argc<4)//命令行参数至少是三个
{
printf("Usage:ioctl<dev_file><cmd><arg>\n");
return0;
}
//
cmd=atoi(argv[2]);
//
arg=atoi(argv[3]);
//输出命令行参数
printf("dev:%s\n",argv[1]);
printf("cmd:%d\n",cmd);
printf("arg:%d\n",arg);
//打开设备文件
file_handler=open(argv[1],0);
//发送命令和参数
ioctl(file_handler,cmd,arg);
//关闭设备文件
close(file_handler);
return0;
}
编译有两种方法:
1在android源码环境下编译,未测试????????????
2使用交叉编译工具进行测试
#使用交叉编译器进行编译
#执行ioctl命令的格式如下:ioctl<设备文件><cmd><arg>
arm_linux_gcc-static-oioctl${PWD}/test_ioctl.c
adbpush${PWD}/ioctl/data/local/ioctl
adbshell/data/local/ioctl
测试命令如下:
/data/local#./ioctl/dev/mini6410_leds22
使用androidNDK进行测试
注:chmod777/dev/mini6410_leds
MainActivity文件示例代码如下:
packagecn.yue.android.mini6410.leds;
importandroid.os.Bundle;
importandroid.util.Log;
importandroid.view.View;
importandroid.view.View.OnClickListener;
importandroid.widget.CheckBox;
importandroid.app.Activity;
/**
*测试led驱动程序
*
*@version
*
*@Description:
*
*@author<ahref="mailto:zhenhuayue@sina.com">Retacn</a>
*
*@since2014-10-24
*
*/
publicclassMainActivityextendsActivityimplementsOnClickListener{
privateCheckBox[]cb_str=newCheckBox[4];
privateCheckBox[]cb_cmd=newCheckBox[4];
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findView();
}
/**
*实例代控件
*/
privatevoidfindView(){
cb_str[0]=(CheckBox)this.findViewById(R.id.checkbox_str_led1);
cb_str[1]=(CheckBox)this.findViewById(R.id.checkbox_str_led2);
cb_str[2]=(CheckBox)this.findViewById(R.id.checkbox_str_led3);
cb_str[3]=(CheckBox)this.findViewById(R.id.checkbox_str_led4);
cb_cmd[0]=(CheckBox)this.findViewById(R.id.checkbox_cmd_led1);
cb_cmd[1]=(CheckBox)this.findViewById(R.id.checkbox_cmd_led2);
cb_cmd[2]=(CheckBox)this.findViewById(R.id.checkbox_cmd_led3);
cb_cmd[3]=(CheckBox)this.findViewById(R.id.checkbox_cmd_led4);
this.findViewById(R.id.btn_send_str).setOnClickListener(this);
this.findViewById(R.id.btn_send_io).setOnClickListener(this);
}
@Override
publicvoidonClick(Viewv){
switch(v.getId()){
caseR.id.btn_send_str:
Log.i("tag","----------sendingstr...");
Stringstr="";
for(inti=0;i<4;i++){
Log.i("tag","cb_str"+i+cb_str[i].isChecked());
if(cb_str[i].isChecked()){
str+="1";
}else{
str+="0";
}
}
//发送控制字符串
strLeds(str);
break;
caseR.id.btn_send_io:
Log.i("tag","----------sendingcmd...");
for(inti=0;i<4;i++){
Log.i("tag","cb_cmd"+i+cb_cmd[i].isChecked());
if(cb_str[i].isChecked()){
cmdLeds(1,i);
}else{
cmdLeds(0,i);
}
}
break;
}
}
//定义本地方法
publicnativevoidstrLeds(Stringstr);
publicnativevoidcmdLeds(intcmd,intarg);
static{
System.loadLibrary("ndk_test_mini6410_leds");
}
}
#include<string.h>
#include<jni.h>
#include<fcntl.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<stdlib.h>
#include"cn_yue_android_mini6410_leds_MainActivity.h"
char*jstring_to_pchar(JNIEnv*env,jstringstr){
char*pstr=NULL;
jclassclsstring=(*env)->FindClass(env,"java/lang/String");
jstringstrencode=(*env)->NewStringUTF(env,"utf-8");
jmethodIDmid=(*env)->GetMethodID(env,clsstring,"getBytes",
"(Ljava/lang/String;)[B");
jbyteArraybyteArray=(jbyteArray)((*env)->CallObjectMethod(env,str,mid,
strencode));
jsizesize=(*env)->GetArrayLength(env,byteArray);
jbyte*pbyte=(*env)->GetByteArrayElements(env,byteArray,JNI_FALSE);
if(size>0){
pstr=(char*)malloc(size);
memcpy(pstr,pbyte,size);
}
returnpstr;
}
voidJava_cn_yue_android_mini6410_leds_MainActivity_strLeds(JNIEnv*env,
jobjectthiz,jstringstr){
intdev;
dev=open("/dev/mini6410_leds",O_WRONLY);
char*pstr=jstring_to_pchar(env,str);
if(pstr!=NULL){
write(dev,pstr,strlen(pstr));
}
close(dev);
}
voidJava_cn_yue_android_mini6410_leds_MainActivity_cmdLeds(JNIEnv*env,
jobjectthiz,jintcmd,jintarg){
intdev;
dev=open("/dev/mini6410_leds",O_WRONLY);
ioctl(dev,cmd,arg);
close(dev);
}