android深度搜索学习笔记二(控制发光二级管)

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_tint数据类型,表示设备号,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);

//关链cdevfile_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有两个引角,gpb0gpb1,一个引角连接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;

}

编译有两种方法:

1android源码环境下编译,未测试????????????

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);

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值