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

1 led驱动的实现原理

Linux驱动不直接与硬件打交道,而是通过i/o内存作为中介,具体关系如下图

 

编写led驱动

 2.1创建led驱动的设备文件

   在统计单词数量驱动中使用misc_register创建设备文件,该函数只能创建设备号为10的设备文件

所以想要创建其他设备号的设备文件,就要使用:

cdev_init 初始化cdev

查看cdev结构体的定义在内核源代码的/include/linux/cdev.h文件中定义,内容如下:

struct cdev {

struct kobject kobj; //封装设备文件的对象

struct module *owner;//指向内核模块的指针

const struct file_operations *ops; //file_operation结构体指针

struct list_head list;  //指向上一个下一个cdev结构体的指针

dev_t dev;//dev_t int数据类型,表示设备号,12位表示主设备号,20位表示次                   

             设备号

unsigned int count;  //请求的链接设备的编号的范围

};

Cdev_init()函数位于linux/fs/char_dev.c文件中,代码内容如下:

 

/**

 * cdev_init() - initialize a cdev structure

 * @cdev: the structure to initialize

 * @fops: the file_operations for this device

 *

 * Initializes @cdev, remembering @fops, making it ready to add to the

 * system with cdev_add().

 */

void cdev_init(struct cdev *cdev, const struct file_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() - register a range of device numbers 静态指定设备号

 * @from: the first in the desired range of device numbers; must include

 *        the major number.  设备号

 * @count: the number of consecutive device numbers required  表示次设备号范围

 * @name: the name of the device or driver.   表示设备文件名称

 *

 * Return value is zero on success, a negative error code on failure.

 */

int register_chrdev_region(dev_t from, unsigned count, const char *name)

{

struct char_device_struct *cd;

dev_t to = from + count;

dev_t n, 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))

goto fail;

}

return 0;

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

}

return PTR_ERR(cd);

}

 

动态分配 使用alloc_chrdev_region函数,定义在inux/fs/char_dev.c文件中,内容如下:

/**

 * alloc_chrdev_region() - register a range of char device numbers 动态分配设备编号

 * @dev: output parameter for first assigned number 设备号指针

 * @baseminor: first of the requested range of minor numbers 次设备号

 * @count: the number of minor numbers required  表示分配的次设备号范围

 * @name: the name of the associated device or driver  表示设备文件名称

 *

 * Allocates a range of char device numbers.  The major number will be

 * chosen dynamically, and returned (along with the first minor number)

 * in @dev.  Returns zero or a negative error code.

 */

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,

const char *name)

{

struct char_device_struct *cd;

cd = __register_chrdev_region(0, baseminor, count, name);

if (IS_ERR(cd))

return PTR_ERR(cd);

*dev = MKDEV(cd->major, cd->baseminor);

return 0;

}

 

 

 

Cdev_add  将字符设备添加到内核中的字符设备组中

函数定义在inux/fs/char_dev.c文件中,内容如下:

/**

 * cdev_add() - add a char device to the system  将字符设备添加到内核 字符设备数组中

 * @p: the cdev structure for the device  设备文件指针

 * @dev: the first device number for which this device is responsible   设备号

 * @count: the number of consecutive minor numbers corresponding to this  设备文件数量

 *         device

 *

 * cdev_add() adds the device represented by @p to the system, making it

 * live immediately.  A negative error code is returned on failure.

 */

int cdev_add(struct cdev *p, dev_t dev, unsigned count)

{

p->dev = dev;

p->count = count;

//将字符设备添加到probes数组中,该函数定义在linux/drivers/base/map.c

return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);

}

class_create 该宏用于创建struct_class,实际上调用的是__create_class()函数定义在drivers/base/class.c文件中

/* This is a #define to keep the compiler from merging different

 * instances of the __key variable */

#define class_create(owner, name) \

({ \

static struct lock_class_key __key; \

__class_create(owner, name, &__key);\

})

 

 

device_create 创建设备文件

函数定义在drivers/base/core.c文件中,内容如下:

struct device *device_create(struct class *class, struct device *parent,

     dev_t devt, void *drvdata, const char *fmt, ...)

{

va_list vargs;

struct device *dev;

 

va_start(vargs, fmt);

dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);

va_end(vargs);

return dev;

}

 

创建设备文件的示例代码如下:

//定义设备文件名称

#define DEVICE_NAME "mini6410_leds"

//创建设备文件的数量

#define DEVICE_COUNT 1

//默认主设备号

#define MINI6410_LEDS_MAJOR 0

//默认次设备号

#define MINI6410_LEDS_MINOR 234

//主设备号

static int major=MINI6410_LEDS_MAJOR;

//次设备号

static int minor=MINI6410_LEDS_MINOR;

//设备号

static dev_t dev_number;

//struct class

static struct class *leds_class=NULL;

 

//设备文件回调指针

static struct file_operations dev_fops={

.ower=THIS_MODULE,

.unlocked_ioctl=mini6410_leds_ioctl,

.write=mini6410_leds_write

};

//描述字符设备的struct cdev

static struct cdev leds_cdev;

 

//创建设备文件

static int leds_create_device(void){

 

int ret =0;

int err=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");

return err;

}

}

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

return err;

}

//获取主设备号

major=MAJOR(leds_cdev.dev);

//获取次设备号

minor=MINOR(leds_cdev.dev);

//自动分配的设备号

dev_number= leds_cdev.dev;

}

//将字符设备添加到内核中的字符设备数组中

cdev_add(&leds_cdev,dev_number,DEVICE_COUNT);

//创建struct class

leds_class=class_create(THIS_MODULE,DEVICE_NAME);

//创建设备文件

device_create(leds_class,NULL,dev_number,NULL,DEVICE_NAME);

return ret;

}

 

LED驱动的初始化函数

Static int leds_init(void){

Int ret;

//创建设备文件

Ret=leds_create_device();

Printk(DEVICE_NAME”\t initialized \n”);

Return ret;

}

Module_init(leds_init);

 

 

 

 

卸载led驱动的设备文件

Device_destory //函数原型如下,代码位于drivers/base/core.c文件中

/**

 * device_destroy - removes a device that was created with device_create()  移除建立的字符设备

 * @class: pointer to the struct class that this device was registered with

 * @devt: the dev_t of the device that was previously registered

 *

 * This call unregisters and cleans up a device that was created with a

 * call to device_create().

 */

void device_destroy(struct class *class, dev_t devt)

{

struct device *dev;

 

dev = class_find_device(class, NULL, &devt, __match_devt);

if (dev) {

put_device(dev);

device_unregister(dev);

}

}

 

 

Class_destory  //销毁sturct class,函数定义在drivers/base/class.c文件中

/**

 * class_destroy - destroys a struct class structure   销毁sturct class

 * @cls: pointer to the struct class that is to be destroyed

 *

 * Note, the pointer to be destroyed must have been created with a call

 * to class_create().

 */

void class_destroy(struct class *cls)

{

if ((cls == NULL) || (IS_ERR(cls)))

return;

 

class_unregister(cls);

}

Unregister_chrdev_region  //注销字符设备区域,函数位于linux/fs/char_dev.c文件中

 

/**

 * unregister_chrdev_region() - return a range of device numbers 注销字符设备区域

 * @from: the first in the range of numbers to unregister

 * @count: the number of device numbers to unregister

 *

 * This function will unregister a range of @count device numbers,

 * starting with @from.  The caller should normally be the one who

 * allocated those numbers in the first place...

 */

void unregister_chrdev_region(dev_t from, unsigned count)

{

dev_t to = from + count;

dev_t n, 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));

}

}

 

示例代码如下

//移除设备文件

Static void leds_destroy_device(void){

//移除创建的字符设备

Device_destroy(leds_class,dev_number);

//销毁struct class

If(leds_class){

Class_destroy(leds_class);

}

//注销字符设备区域

Unretister_chrdev_retion(dev_number,DEVICE_NAME);

Return ;

}

Static void leds_exit(void){

//卸载led设备驱动

Leds_destroy();

Printk(DEVICE_NAME”\t esit! \n”);

}

Module_exit(leds_exit);

 

 

 

设置寄存器与初始化LED驱动

设置led引角的状态,打开或禁止上拉电路来控制led的亮或灭

Led有两个引角,gpb0gpb1,一个引角连接arm处理器的gpio接口,另一个经过一个限流电阻连接到电源,gpio端为低电平时,led

 

控制led需要三个寄存器来完成:

查看芯片手册得到其值

gpkcon(端口配置寄存器) 0x7f008800

gpkdat(数据端口寄存器) 0x7f008808

gpkpud(端口上拉电路寄存器)0x7f00880c

 

每个寄存器使用四个字节,也就是一个int整型数据

pgkcon 4567 设为output

gpkdat  0表示亮,1表示灭

gpkpud 10表示打开上拉电路

寄存器虚拟地址对应的宏

定义在 linux/arch/arm/mach-s3c64xx/include/mach/gpio-bank-k.h

文件中,示例代码如下:

#define S3C64XX_GPKCON (S3C64XX_GPK_BASE + 0x00)

#define S3C64XX_GPKCON1 (S3C64XX_GPK_BASE + 0x04)

#define S3C64XX_GPKDAT (S3C64XX_GPK_BASE + 0x08)

#define S3C64XX_GPKPUD (S3C64XX_GPK_BASE + 0x0c)

 linux/arch/arm/plat-s3c64xx/include/mach/regs-gpio.h

#define S3C64XX_GPK_BASE S3C64XX_GPIOREG(0x0800)

#define S3C64XX_GPIOREG(reg) (S3C64XX_VA_GPIO + (reg))

 

 linux/arch/arm/mach-s3c6400/include/mach/map.h

#define S3C64XX_VA_GPIO S3C_ADDR_CPU(0x00000000)

 

linux/arch/arm/plat-samsung/include/plat/map-base.h

#define S3C_ADDR_CPU(x) S3C_ADDR(0x00500000 + (x))

#ifndef __ASSEMBLY__

#define S3C_ADDR(x) ((void __iomem __force *)S3C_ADDR_BASE + (x))

#else

#define S3C_ADDR(x) (S3C_ADDR_BASE + (x))

#define S3C_ADDR_BASE 0xF6000000

 

 

初始化寄存器的函数如下:

static void leds_init_gpk(int leds_default){

int tmp=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()中调用该函数即可

static int leds_init(void){

int ret;

ret=leds_create_device();

//初始化寄存器

//1100 0000 两亮两灭,0表示亮,1表示灭

leds_init_gpk(0xC0);

printk(DEVICE_NAME"\t initialiaed \n");

return ret;

}

 

控制led

可以通过以下两种方式控制led :

通过字符串控制led   需要使用file_operation.write

通过I/O命令控制led 需要使用fiel_operation.ioctl

 

接收数据的函数如下mini6410_leds_write():

示例代码如下:

//保存四个led的设置状态

static unsigned char mem[4];

//接收向设备写入的字符串

static ssize_t mini6410_leds_write(struct file *file,const char _user *buf,size_t count,loff_t *ppos){

unsigned tmp=count;

unsigned long i=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);

}

return count;

}

 

}

可以通过以下命令控制led的亮灭

Adb shell ‘echo 1100 >/dev/mini6410_leds’

 

接收命令和参数的函数

//filp表示led驱动的设备文件

//cmd表示开关

//arg表示哪个led

static long mini6410_leds_ioctl(struct file *filp,unsigned int cmd,unsigned long arg){

//命令只能是0/1

switch(cmd){

unsigned tmp;

case 0:

case 1:

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

return 0;

default:

return -EINVAL;

}

}

 

 

LED驱动的模块参数

如果相要在装载led驱动时指定默认状态,就要使用模块参数

linux指定一个参数需要使用module_param(name,//参数名

                                        type,//参数类型

                perm)//表示读写权限

使用宏指定参数后,会在sys/module目录下生成设备文件同名的文件目录,

/sys/module/mini6410_leds/parameters,如果有多个参数,会在该目录下生成param*个文件

 

led添加一个模块参数,示例代码如下:

//保存模块参数值的变量

static int leds_state=1;

//初始化led驱动

static int leds_init(void){

int ret;

ret=leds_create_device();

//初始化寄存器

leds_init_gpk(~leds_state);

printk(DEVICE_NAME"\t initialized \n");

return ret;

}

//指定模块参数

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>

 

//定义设备文件名称

#define DEVICE_NAME "mini6410_leds"

//创建设备文件的数量

#define DEVICE_COUNT 1

//默认主设备号

#define MINI6410_LEDS_MAJOR 0

//默认次设备号

#define MINI6410_LEDS_MINOR 234

//定义数组类型的模块参数值个数

#define PARAM_SIZE 3

//保存四个led设置状态的数组

static unsigned char mem[4];

//主设备号

static int major=MINI6410_LEDS_MAJOR;

//次设备号

static int minor=MINI6410_LEDS_MINOR;

//设备号

static dev_t dev_number;

//当前led的状态,通过模块参数传入

static int leds_state=1; 

//数组类型模块参数的默认值

static char *params[]={"string1","string2","string3"};

//数组类型模块参数值的个数

static int param_size=PARAM_SIZE;

//struct class表示led字符设备的结构体

static struct class *leds_class=NULL;

//描述字符设备的struct cdev

static struct cdev leds_cdev;

 

//------------接收命令和参数的函数

//filp表示led驱动的设备文件

//cmd表示开关

//arg表示哪个led

static long mini6410_leds_ioctl(struct file *filp,unsigned int cmd,unsigned long arg){

//命令只能是0/1

switch(cmd){

unsigned tmp;

case 0:

case 1:

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

return 0;

default:

return -EINVAL;

}

}

 

//-----------接收向设备写入的字符串

static ssize_t mini6410_leds_write(struct file *file,const char __user *buf,size_t count,loff_t *ppos){

unsigned tmp=count;

unsigned long i=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);

}

return count;

}

}

 

//定义file_operation结构体

//设备文件回调指针

static struct file_operations dev_fops={

.owner = THIS_MODULE,

.unlocked_ioctl = mini6410_leds_ioctl,

.write = mini6410_leds_write

};

 

//-------创建设备文件

static int leds_create_device(void){

 

int ret =0;

int err=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");

return err;

}

}

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

return err;

}

//获取主设备号

major=MAJOR(leds_cdev.dev);

//获取次设备号

minor=MINOR(leds_cdev.dev);

//自动分配的设备号

dev_number= leds_cdev.dev;

}

//将字符设备添加到内核中的字符设备数组中

cdev_add(&leds_cdev,dev_number,DEVICE_COUNT);

//创建struct class

leds_class=class_create(THIS_MODULE,DEVICE_NAME);

//创建设备文件

device_create(leds_class,NULL,dev_number,NULL,DEVICE_NAME);

return ret;

}

 

//----------初始化寄存器

static void leds_init_gpk(int leds_default){

int tmp=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);

}

 

//销毁字符设备

static void leds_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驱动

static int leds_init(void){

int ret;

ret=leds_create_device();

leds_init_gpk(~leds_state);

printk(DEVICE_NAME"\t initialized \n");

printk("param0\t %s \n",params[0]);

printk("param1\t %s \n",params[1]);

printk("param2\t %s \n",params[2]);

return ret;

}

 

//卸载led驱动

static int leds_exit(void){

leds_destroy_device();

printk(DEVICE_NAME"\t exit! \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");

编译

编译成动态驱动模块进行测试

脚本build.sh文件内容如下:

source ./build_mini6410.sh

 

脚本build_mini6410.sh文件的内容如下:

# build_mini6410.sh

source /opt/linux/driver/common.sh

make -C $MINI6410_ANDROID_KERNEL_PATH M=${PWD}

find_devices 

if [ "$selected_device" == "" ]; then 

    exit

else

    adb -s $selected_device push ${PWD}/mini6410_leds.ko /data/local

    testing=$(adb -s $selected_device shell lsmod | grep  "mini6410_leds")

    if [ "$testing" != "" ]; then

adb -s $selected_device shell rmmod mini6410_leds

    fi

    adb -s $selected_device shell "insmod /data/local/mini6410_leds.ko"

    adb -s $selected_device shell  "chmod 777 /dev/mini6410_leds"

 

fi

 

Makefile文件内容如下:

obj-m := mini6410_leds.o

 

编译自动将.ko文件上传到开发板

直接使用adb shell进行测试

Adb shell “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=$(rev temp)

#led设备发送控制字符串

adb shell "echo $n > /dev/mini6410_leds"

#延迟一秒钟

sleep 1

done

 

 

 

 

也可以将驱动程序像之前一样编译进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前添加如下代码:

//源代码中已有不需要再添加

 config MINI6410_LEDS

tristate "LED Support for Mini6410 GPIO LEDs"

depends on CPU_S3C6410

default y

help

  This option enables support for LEDs connected to GPIO lines

  on Mini6410 boards.

修改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

 

设置.config文件,可以通过make menuconfig来进行配置

Device Drivers  --->  Character devices  --->  [*] word_count_driver 

打开.config文件可以看到该项设置为y

CONFIG_MINI6410_LEDS=y

 

编译linux内核 

Make

烧写到开发板进行测试

/ # chmod 777 /dev/mini6410_leds

使用adb shell进行测试

/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>

 

int main(int argc, char **argv)

{

//三个命令行参数

int file_handler = 0;

int cmd = 0;

int arg = 0;

if(argc < 4)//命令行参数至少是三个

{

printf("Usage: ioctl <dev_file> <cmd> <arg>\n");

return 0;

}

//

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

return 0;

}

 

 

编译有两种方法:

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

使用交叉编译工具进行测试

#使用交叉编译器进行编译

#执行ioctl命令的格式如下:ioctl <设备文件> <cmd> <arg>

arm_linux_gcc -static -o ioctl ${PWD}/test_ioctl.c

adb push ${PWD}/ioctl /data/local/ioctl

adb shell /data/local/ioctl

测试命令如下:

/data/local # ./ioctl /dev/mini6410_leds 2 2

 

使用androidNDK进行测试

:chmod 777 /dev/mini6410_leds

MainActivity文件示例代码如下:

package cn.yue.android.mini6410.leds;

 

import android.os.Bundle;

import android.util.Log;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.CheckBox;

import android.app.Activity;

 

/**

 * 测试led驱动程序

 * 

 * @version

 * 

 * @Description:

 * 

 * @author <a href="mailto:zhenhuayue@sina.com">Retacn</a>

 * 

 * @since 2014-10-24

 * 

 */

public class MainActivity extends Activity implements OnClickListener {

private CheckBox[] cb_str = new CheckBox[4];

private CheckBox[] cb_cmd = new CheckBox[4];

 

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

findView();

}

 

/**

 * 实例代控件

 */

private void findView() {

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

public void onClick(View v) {

switch (v.getId()) {

case R.id.btn_send_str:

Log.i("tag", "----------sending str...");

String str = "";

for (int i = 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;

case R.id.btn_send_io:

Log.i("tag", "----------sending cmd...");

for (int i = 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;

 

}

}

 

// 定义本地方法

public native void strLeds(String str);

 

public native void cmdLeds(int cmd, int arg);

 

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, jstring str) {

char* pstr = NULL;

jclass clsstring = (*env)->FindClass(env, "java/lang/String");

jstring strencode = (*env)->NewStringUTF(env, "utf-8");

jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes",

"(Ljava/lang/String;)[B");

jbyteArray byteArray = (jbyteArray) ((*env)->CallObjectMethod(env, str, mid,

strencode));

jsize size = (*env)->GetArrayLength(env, byteArray);

jbyte* pbyte = (*env)->GetByteArrayElements(env, byteArray, JNI_FALSE);

if (size > 0) {

pstr = (char*) malloc(size);

memcpy(pstr, pbyte, size);

}

return pstr;

}

 

void Java_cn_yue_android_mini6410_leds_MainActivity_strLeds(JNIEnv* env,

jobject thiz, jstring str) {

 

int dev;

dev = open("/dev/mini6410_leds", O_WRONLY);

char* pstr = jstring_to_pchar(env, str);

if (pstr != NULL) {

write(dev, pstr, strlen(pstr));

}

close(dev);

}

 

void Java_cn_yue_android_mini6410_leds_MainActivity_cmdLeds(JNIEnv* env,

jobject thiz, jint cmd, jint arg) {

 

int dev;

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、付费专栏及课程。

余额充值