2019-05-27 09:49:42 yinsui1839 阅读数 93
  • Android底层技术:Java层系统服务(Android Service)

    为什么我们需要学习撰写系统服务呢? 底层系统服务是Android 框架裡接近Linux/Driver 的部分。为了充分发挥硬件设备的差異化特性,系统服务是让上层Java 应用程序來使用Driver/HW Device 特色的重要管道。于此,先仔细解析Java层系统服务的架构,然后阐述这系统服务的代码开发要点,以及其API设计方法。

    32697 人正在学习 去看看 高煥堂

安卓驱动开发和linux驱动开发是一摸一样的,只不过安卓驱动要被上层应用调用到与linux的方式不同

Android 应用调用驱动:

上层apk---->jni层--->驱动层   (这只是一个访问的路径示意图,中间还需要为SElinux和init.rc赋予给apk访问驱动权限问题)

一,驱动层:
1.在驱动路径下新建test目录和修改Makefile

cjx@ubuntu:~/work/linux$ mkdir drivers/test 
cjx@ubuntu:~/work/linux$ vim drivers/Makefile

drivers/Makefile文件末尾添加

obj-y                           += test/

2.新建test.c文件和Makefile

cjx@ubuntu:~/work/linux$ cd drivers/test
cjx@ubuntu:~/work/linux/drivers/test$ vim test.c;vim Makefile

test.c内容如下

#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/log2.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/input/sparse-keymap.h>
#include <linux/gpio_keys.h>

#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>       


typedef struct _test_t {
	timming_t timing;
	unsigned long long pininfo;
	RTK_GPIO_ID gid;
	struct cdev cdev;
	struct class *class;
}test_t;
static dev_t devno_base;


static
int test_open(struct inode *inode, struct file *file)
{
	printk(KERN_ALERT  "cjx open================================\n");

	return 0;
}

static
int test_release(struct inode *inode, struct file *file)
{
	return 0;
}


static
long test_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	printk(KERN_ALERT  "cjx============================\n", cmd, arg);
	return 0;
}

static struct file_operations test_fops = {
	.owner = THIS_MODULE,
	.unlocked_ioctl = test_ioctl,
	.open = test_open,
	.release = test_release,
};

static char *test_devnode(struct device *dev, mode_t *mode)
{
	if(mode)
		*mode = 0666;
	return NULL;
}


static int __init test_module_init(void)
{
	unsigned int result = 0;
	int major_node = 0;
	int minor_node = 0;
	
	if (alloc_chrdev_region(&devno_base, 0, 1, "test") != 0)
			return -EFAULT;
		
	major_node = MAJOR(devno_base);
	test_info.class = class_create(THIS_MODULE, "test-dev"); 
	if (IS_ERR(test_info.class)) {
			result = PTR_ERR(test_info.class);
			goto err_create_class_failed;
	}
	test_info.class->devnode = test_devnode;  
	
	cdev_init(&test_info.cdev, &test_fops);
	test_info.cdev.owner = THIS_MODULE;
	test_info.cdev.ops = &test_fops;
	cdev_add (&test_info.cdev, MKDEV(major_node, minor_node), 1);
	device_create(test_info.class, NULL, MKDEV(major_node, minor_node), NULL, "test");

	return 0;

err_create_class_failed:
	unregister_chrdev_region(devno_base, 1);
	return result;
}

static void __exit test_module_exit(void)
{
	device_destroy(test_info.class, MKDEV(0, 0));
	cdev_del(&test_info.cdev);
    unregister_chrdev_region(devno_base, 1);
	class_destroy(test_info.class);
}

MODULE_LICENSE("GPL");
module_init(test_module_init);
module_exit(test_module_exit);

Makefile:

obj-y += test.o

到这里驱动层已经添加完毕,编译烧录后会产生/dev/test文件

二:应用层和jni层:

在要调用的驱动的apk的代码下新建test.java

mkdir src/android/hikeen/
vim src/android/hikeen/test.java

 

/***test.java:***/
package android.hikeen;

public class test {
        private static test mtest = null;
        private static boolean isOpen = false;

        static {
                System.loadLibrary("test");
        }
        public native void inittest();
        public native void test(int direction, int repeat, int time);
        public native void move(int direction, int repeat, int time);
        public native void closetest();
        public native int istesting();

        public static test getInstance(){
                if (mtest == null) {
                        mtest = new test();
                }
                return mtest;
        };

        public void open(){
                if (!isOpen) {
                        inittest();
                }
                isOpen = true;
        };

        public void close(){
                if (isOpen) {
                        closetest();
                }
                isOpen = false;
        };
}
cd src;
javah android.hikeen.test   //在当前文件夹下就会产生android_hikeen_test.h文件,这个就是jni c++的头文件

一般写jni的位置在源码/external下
mkdir 源码/external/test/
将android_hikeen_test.h 移到 源码/external/test/
然后在源码/external/test/路径下新建Android.mk  android_hikeen_test.cpp

/***android_hikeen_test.cpp***/
#include <jni.h>
#include <android/bitmap.h>
#include <stdio.h>
#include <stdlib.h>
#include <android/log.h>

#include <math.h>                                                                                                    

#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <string.h>

#include <errno.h>
#include <linux/input.h>
#include <stdint.h>


#include <pthread.h>

#define SCROLL_UP		0
#define SCROLL_DOWN		1
#define SCROLL_LEFT		2
#define SCROLL_RIGHT	3
#include<android/log.h>

#define MOVE_UP			11
#define MOVE_DOWN		22

#include "android_hikeen_test.h"

#ifdef __cplusplus
extern "C" {
#endif
int fd;

#define TAG "myDemo-jni" // 这个是自定义的LOG的标识 
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) // 定义LOGD类型 
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGI类型 
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) // 定义LOGW类型 
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) // 定义LOGE类型 
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) // 定义LOGF类型 

JNIEXPORT void JNICALL Java_android_hikeen_test_inittest(JNIEnv *, jobject)
  {
	  fd = open("/dev/test", O_RDWR);
	  if(fd < 0) {
		LOGD("open test file error %d",fd);
      }
	
  }

JNIEXPORT void JNICALL Java_android_hikeen_test_test(JNIEnv *, jobject , jint direction, jint repeat, jint time)
{
	int i=direction+repeat+time,ret;
	LOGD(" test test i= %d",i);
	ret=ioctl(fd,1);
	
	if(ret<0){
		LOGD("ioctl test file error %d",ret);
	}
	return ;
}

JNIEXPORT void JNICALL Java_android_hikeen_test_move(JNIEnv *, jobject , jint direction, jint repeat, jint time)
{
	int i=direction+repeat+time,ret=0;
	LOGD(" test test i= %d",i);
	ret=ioctl(fd,1);
	if(ret<0){
		LOGD(" move test file error %d",ret);
	}
	return ;
	
}

JNIEXPORT void JNICALL Java_android_hikeen_test_closetest(JNIEnv *, jobject)
  {
	  close(fd);
	  LOGD(" close test file error");
  }
JNIEXPORT jint JNICALL Java_android_hikeen_test_istesting(JNIEnv *, jobject)
  {
	  return 0;
  }

#ifdef __cplusplus
}
#endif
/***Android.mk***/
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE  := libtest


LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES

LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code -Wunused-parameter

LOCAL_SRC_FILES := android_hikeen_test.cpp

LOCAL_MODULE_TAGS := eng debug

	
LOCAL_LDLIBS := -ljnigraphics	
LOCAL_LDLIBS += -llog

include $(BUILD_SHARED_LIBRARY)

编译烧录完成后在板子上的system/lib/下就有libtest.so这个文件,这个就是test 的jni库

到现在还未完成,因为还需要赋权限和SELinux的权限才能正常调用

2016-05-17 19:23:09 luckydarcy 阅读数 989
  • Android底层技术:Java层系统服务(Android Service)

    为什么我们需要学习撰写系统服务呢? 底层系统服务是Android 框架裡接近Linux/Driver 的部分。为了充分发挥硬件设备的差異化特性,系统服务是让上层Java 应用程序來使用Driver/HW Device 特色的重要管道。于此,先仔细解析Java层系统服务的架构,然后阐述这系统服务的代码开发要点,以及其API设计方法。

    32697 人正在学习 去看看 高煥堂

  Linux驱动程序的开发与应用程序的开发有很大的区别,这些差别导致了编写Linux设备驱动程序与编写应用程序的本质区别。

一、用户态和内核态

  Linux操作系统分为用户态内核态。内核态完成与硬件的交互,比如读写内存、将硬盘上的数据读取到内存等。驱动程序在底层与硬件交互,因此工作在内核态。用户态可以理解为上层的应用程序,可以是Java应用程序、Qt应用程序、Python应用程序等。Linux操作系统分成两种状态的原因是,即使用户态的应用程序出现异常,也不会导致操作系统崩溃,而这一切都归功于内核态对操作系统有很强大的保护能力。
  
  另一方面,Linux操作系统分为两个状态的原因主要是为应用程序提供一个统一的计算机硬件抽象。工作在用户态的应用程序完全可以不考虑底层的硬件操作,这些操作由内核态程序来完成。而这些内核态程序大部分是设备驱动程序。应用程序可以在不了解硬件工作原理的情况下,很好地操作硬件设备,同时不会使硬件设备进入非法状态。
  
  值得注意的是,用户态和内核态是可以互相转换的。每当应用程序执行系统调用或者被硬件中断挂起时,Linux操作系统都会从用户态切换到内核态;当系统调用完成或者中断处理完成后,操作系统会从内核态返回到用户态,继续执行应用程序。

二、模块机制

  模块是可以在运行时加入内核的代码,这是Linux一个很好地特性,这个特性可以使内核很容易得扩大或缩小,扩大内核可以增加内核的功能,缩小内核可以减少内核的大小。Linux内核支持多种模块,驱动程序就是其中最重要的一种,每一个模块由编译好的目标代码组成,使用insmod命令将模块加入正在运行的内核,使用rmmod命令将一个未使用的模块从内核删除。
  
  模块在在内核启动时装载称为静态装载,在内核已经运行时装载称为动态装载。模块可以扩充内核所期望的任何功能,但通常用于实现设备驱动程序。
  
  一个模块的最基本框架代码如下:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>

static int __init xxx_init(void)
{
    /* 模块加载时的初始化工作 */
    return 0;
}

static void __exit xxx_exit(void)
{
    /* 模块卸载时的销毁工作 */
}

module_init(xxx_init);  /* 指定模块的初始化函数的宏 */
module_exit(xxx_exit);  /* 指定模块的卸载函数的宏 */
MODULE_LICENSE("Dual BSD/GPL"); /* 指定许可权 */
三、总线,设备,驱动

  想要驾驭Linux驱动开发,必须深刻理解Linux总线设备驱动框架。之所以会形成这样的框架,主要是为了代码的可重用性,因为驱动和设备的关系是一对多的。正如主设备号和次设备号之分,主设备号表示驱动程序,次设备号表示具体的设备。
  
  另外是为了提高驱动的可移植性,Linux把驱动要用到的资源(如GPIO和中断等)剥离给设备去管理。即在设备里面包含其自己的设备属性,还包括了其连接到SOC所用到的资源。而驱动重点关注操作的流程和方法。
  
  总线的作用则是在软件层面对设备和驱动进行管理。设备要让系统感知自己的存在,设备需要向总线注册自己;同样地,驱动要让系统感知自己的存在,也需要向总线注册自己。设备和总线在初始化时必须要明确自己是哪种总线的。因此,为了达到操作一致性,Linux发明了一种虚拟的总线,称为Platform总线
  
  多个设备和多个驱动都注册到同一个总线上,那设备怎么找到最适合自己的驱动呢,或者说驱动怎么找到其所支持的设备呢?这个也是由总线负责,总线就像是一个红娘,负责在设备和驱动中牵线。设备会向总线提出自己对驱动的条件(最简单的也是最精确的就是指定对方的名字了),而驱动也会向总线告知自己能够支持的设备的条件(一般是型号ID等,最简单的也可以是设备的名字)。那设备在注册的时候,总线就会遍历注册在它上面的驱动,找到最适合这个设备的驱动,然后填入设备的结构成员中;驱动注册的时候,总线也会遍历注册在其之上的设备,找到其支持的设备(可以是多个,驱动和设备的关系是1:N),并将设备填入驱动的支持列表中。我们称总线这个牵线的行为是match。牵好线之后,设备和驱动之间的交互红娘可不管了。

2014-11-24 20:31:21 coker 阅读数 859
  • Android底层技术:Java层系统服务(Android Service)

    为什么我们需要学习撰写系统服务呢? 底层系统服务是Android 框架裡接近Linux/Driver 的部分。为了充分发挥硬件设备的差異化特性,系统服务是让上层Java 应用程序來使用Driver/HW Device 特色的重要管道。于此,先仔细解析Java层系统服务的架构,然后阐述这系统服务的代码开发要点,以及其API设计方法。

    32697 人正在学习 去看看 高煥堂
Android驱动开发工程师   (到岗时间 ASAP)
工作地点:深圳
岗位职责
1、负责android体感器设备的驱动开发及维护,并必要时参与上层软件开发;
2、与上层软件开发人员共同制定驱动与上层软件接口;
3、编写相关设计文档。协同测试等部门解决研发、生产测试及量产过程中出现的软件问题;
4、参与重大技术问题的技术攻关,提升软件开发水平;
5、收集软件使用反馈意见,修正缺陷或故障,设计升级版本,提高软件的使用性能。

岗位要求
1、2年以上嵌入式Linux/Android驱动软件开发经验,有智能手机和穿戴设备驱动开发经验者优先;
2、精通C/C++语言,能阅读英文技术文档;
3、熟悉Linux设备驱动软件架构及相关接口通信协议,熟练掌握常见硬件的驱动编程;
4、工作积极、主动,具有团队合作精神,有较强的工作抗压能力

简历请投到:info@orbbec.com
2014-11-30 12:23:00 hwunion 阅读数 1476
  • Android底层技术:Java层系统服务(Android Service)

    为什么我们需要学习撰写系统服务呢? 底层系统服务是Android 框架裡接近Linux/Driver 的部分。为了充分发挥硬件设备的差異化特性,系统服务是让上层Java 应用程序來使用Driver/HW Device 特色的重要管道。于此,先仔细解析Java层系统服务的架构,然后阐述这系统服务的代码开发要点,以及其API设计方法。

    32697 人正在学习 去看看 高煥堂



IMX6技术交流群:195829497

物联网实验室:345957209

Python编程俱乐部:516307649





linux设备驱动开发,看起来是一份很高大上的职业,毕竟从事上层应用开发人员太多,而且门槛又不是特别高,而内核级开发从业人员要少得多,而且资料又较少。有许多刚刚接触到linux设备驱动开发的同仁会感觉非常困惑,面对复杂的linux内核有一种无从下手的感觉。根据自己之前积累的一些经验,今天就和大家分享一下,让刚刚步入驱动开发的同仁少走一些弯路。

1.要知道将来要做什么

学习,都是有目的性的,要么是兴趣使然,要么就是刚性需求,为了找一份好的工作。在这里先和大家聊聊做设备驱动将来可以做哪些方面。我把linux设备驱动开发工作分为两大类,一类是做BSP级的开发,另外一类是做外设驱动的开发。

BSP的开发指的是板级代码的开发,和CPU是密切相关的,例如I2C/SPI Adapter的驱动.如果使用通用的芯片,比如三星的Exynos,飞思卡尔的I.MX系列,TI的OMAP或者DaVinci系列,基本都会有现成的BSP包,这部分代码通常是芯片厂商提供和大型公司贡献。大家可以看看linux内核源码中/arch/arm/mach-omap,内部很多代码都是诺基亚贡献。做BSP级的开发需要有较深的功底,首先要十分了解CPU特性,另外要使代码有良好的扩展性和复用性,方便后续移植。有这样需求的往往是芯片商或使用专用芯片的设备商。

外设驱动开发就相对简单一些,都是和特定的外设硬件打交到。通过利用BSP级代码提供的API或者linux提供的更高层的抽象接口来操作硬件。实际上和应用层的开发大同小异。例如操作I2C总线上的EEPROM,实际上的读和写操作都有已经封装好的API来完成.而开发者需要做的是了解外设的特性,通过封装好的API对外设进行操作。新入门的开发者建议从外设驱动开发入手,循序渐进。当然,一个优秀的开发者是即可以做BSP级代码的开发,也能做外设驱动的开发的。

2.用面向对象的思想去思考

面向对象,即OO思想,大家应该非常熟悉。linux的内核虽然用面向过程的C语言实现,但是仍然是通过面向对象的思想去设计的。如果从单片机转行做linux设备驱动,会发现和单片机的或者裸机的驱动设计有很大区别。设计linux设备驱动不单单是对硬件设备的操作,更多需要考虑的是扩展性和代码的复用。所以就出现了platform device/driver,i2c device/driver,spi device/driver,抽象出了设备和驱动两部分,使设备细节和驱动分离。另外还出现了一些框架,提供了底层接口的封装,做开发时要习惯用OO思想去设计。当然要记住条条大路通罗马,不使用这些device/driver也可以实现设备驱动,只是不太推荐这样做。

3.从各驱动框架入手

linux提供了各种框架(子系统),对底层进行封装,抽象出相同操作的接口,可样可以更好的实现复用。想入门linux驱动开发,可以先从框架入手,掌握API的使用,再逐渐深入研究,从上到下去学习。不要把驱动开发想象的太复杂,实际和英语的完型填空差不多,框架有了,只需要自己去填写操作具体硬件的细节代码而已。

几个比较重要和常用的框架有:

GPIO:这个就不用多说了,刚开始接触驱动的基本会练习通过GPIO点亮LED的操作,linux封装了相关的gpio操作接口。

SPI:学会spi device/driver的用法,以及收发消息API,可以参考一些代码,基本都是相同的套路。

I2C:学会i2c device/driver的用法,和学习SPI的套路一样。

PINCTRL:非常重要的一个框架,负责CPU引脚复用,由于现在的CPU都很复杂,一个引脚支持多种复用。

V4L2:一个非常复杂的视频采集框架,具体可以参考相关的文档。驱动里面有很多例子可供参考,同时提供了模板vivi.c

Framebuffer:显示相关的框架,熟悉其中API,而且有模板skeletonfb.c。

DMA Engine: 把DMA操作进行封装,目前驱动代码中关于DMA的操作很多是使用私有的BSP包中的DMA接口,如果支持DMA Engine的话,建议使用DMA Engine。

中断:比较常用的了,接口不多,很少掌握。

USB框架:USB框架比较复杂,API较多,可以通过读已有的代码进行学习。

MTD框架:存储相关比较重要的框架,网上相关的文档很多。

设备树:设备树是在新的内核里面引进来的,可以把板级代码中的各种device通过设备树文件去描述,动态创建,这样更灵活。其实不要把设备树想象的太复杂,实际和解析JSON,XML一样,各个节点中记录设备相关的信息,提供给驱动使用。

4.选好参考资料

推荐大家参考《Essential_Linux_Device_Drivers>>,《linux device driver》,还有linux代码目录中的Documentation也非常重要,另外要学会参考现有的驱动代码。既然是参考资料,只是在用的时候去读,这样效果会更好。

2016-09-23 18:37:31 qq_29214249 阅读数 1575
  • Android底层技术:Java层系统服务(Android Service)

    为什么我们需要学习撰写系统服务呢? 底层系统服务是Android 框架裡接近Linux/Driver 的部分。为了充分发挥硬件设备的差異化特性,系统服务是让上层Java 应用程序來使用Driver/HW Device 特色的重要管道。于此,先仔细解析Java层系统服务的架构,然后阐述这系统服务的代码开发要点,以及其API设计方法。

    32697 人正在学习 去看看 高煥堂

本文介绍在4412开发板下的LED驱动的开发流程。

安卓LED驱动开发的流程为:
驱动层设计--->JNI层设计--->上层APP设计

一、LED驱动开发

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/delay.h>
 
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>

static int led_gpios[] = {
	EXYNOS4212_GPM4(0),
	EXYNOS4212_GPM4(1),
	EXYNOS4212_GPM4(2),
	EXYNOS4212_GPM4(3),
};

static int led_open(struct inode *inode, struct file *file)
{
	/* 配置GPIO为输出引脚 */
	int i;
	for (i = 0; i < 4; i++)
		s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);
	
	return 0;
}

/* app : ioctl(fd, cmd, arg) */
static long led_ioctl(struct file *filp, unsigned int cmd,
		unsigned long arg)
{
	/* 根据传入的参数设置GPIO */
	/* cmd : 0-off, 1-on */
	/* arg : 0-3, which led */

	if ((cmd != 0) && (cmd != 1))
		return -EINVAL;
	
	if (arg > 4)
		return -EINVAL;
	
	gpio_set_value(led_gpios[arg], !cmd);
	
	return 0;
}

static struct file_operations leds_ops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   led_open,     
	.unlocked_ioctl	= led_ioctl,
	
};

static int major;
static struct class *cls;

int leds_init(void)
{
	major = register_chrdev(0, "leds", &leds_ops);

	/* 为了让系统udev,mdev给我们创建设备节点 */
	/* 创建类, 在类下创建设备 : /sys */
	cls = class_create(THIS_MODULE, "leds");
	device_create(cls, NULL, MKDEV(major, 0), NULL, "leds"); /* /dev/leds */
	
	return 0;
}

void leds_exit(void)
{
	device_destroy(cls, MKDEV(major, 0));
	class_destroy(cls);
	unregister_chrdev(major, "leds");
}

module_init(leds_init);
module_exit(leds_exit);

二、JNI层设计

#include <jni.h>  /* /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#include <android/log.h>  /* liblog */

//__android_log_print(ANDROID_LOG_DEBUG, "JNIDemo", "native add ...");

 
#if 0
typedef struct {
    char *name;          /* Java里调用的函数名 */
    char *signature;    /* JNI字段描述符, 用来表示Java里调用的函数的参数和返回值类型 */
    void *fnPtr;          /* C语言实现的本地函数 */
} JNINativeMethod;
#endif

static jint fd;

jint ledOpen(JNIEnv *env, jobject cls)
{
	fd = open("/dev/leds", O_RDWR);
	__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledOpen : %d", fd);
	if (fd >= 0)
		return 0;
	else
		return -1;
}

void ledClose(JNIEnv *env, jobject cls)
{
	__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledClose ...");
	close(fd);
}


jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status)
{
	int ret = ioctl(fd, status, which);
	__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledCtrl : %d, %d, %d", which, status, ret);
	return ret;
}


static const JNINativeMethod methods[] = {
	{"ledOpen", "()I", (void *)ledOpen},
	{"ledClose", "()V", (void *)ledClose},
	{"ledCtrl", "(II)I", (void *)ledCtrl},
};




/* System.loadLibrary */
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
	JNIEnv *env;
	jclass cls;

	if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {
		return JNI_ERR; /* JNI version not supported */
	}
	cls = (*env)->FindClass(env, "com/thisway/hardlibrary/HardControl");
	if (cls == NULL) {
		return JNI_ERR;
	}

	/* 2. map java hello <-->c c_hello */
	if ((*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0])) < 0)
		return JNI_ERR;

	return JNI_VERSION_1_4;
}

通过open("/dev/leds", O_RDWR);打开我们自行设计的led驱动,映射出ioctl函数等,从而生成java可以调用的c函数。


三、APP设计

需求:APP中设计四个按钮分别控制四个LED,然后还有一个总开关。

先上效果图:


(1)、layout/activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"
    android:orientation="vertical"
    >

    <TextView android:text="Cw First APP LED CTL"
        android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

    <Button
        android:id="@+id/BUTTON"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="ALL ON"
        />

    <CheckBox
        android:id="@+id/LED1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="LED1"
        android:onClick="onCheckboxClicked"
        />

    <CheckBox
        android:id="@+id/LED2"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="LED2"
        android:onClick="onCheckboxClicked"
        />

    <CheckBox
        android:id="@+id/LED3"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="LED3"
        android:onClick="onCheckboxClicked"
        />

    <CheckBox
        android:id="@+id/LED4"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="LED4"
        android:onClick="onCheckboxClicked"
        />

</LinearLayout>

(2)、MainActivity.java

package com.thisway.app_0001_leddemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button;
import android.view.View;
import android.widget.CheckBox;
import android.widget.Toast;
import com.thisway.hardlibrary.*;

public class MainActivity extends AppCompatActivity {

    private boolean ledon = false;
    private Button button = null;
    private CheckBox checkBoxLed1 = null;
    private CheckBox checkBoxLed2 = null;
    private CheckBox checkBoxLed3 = null;
    private CheckBox checkBoxLed4 = null;

    class MyButtonListener implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            ledon = !ledon;
            if (ledon) {
                button.setText("ALL OFF");
                checkBoxLed1.setChecked(true);
                checkBoxLed2.setChecked(true);
                checkBoxLed3.setChecked(true);
                checkBoxLed4.setChecked(true);

                for (int i = 0; i < 4; i++)
                    HardControl.ledCtrl(i, 1);
            }
            else {
                button.setText("ALL ON");
                checkBoxLed1.setChecked(false);
                checkBoxLed2.setChecked(false);
                checkBoxLed3.setChecked(false);
                checkBoxLed4.setChecked(false);

                for (int i = 0; i < 4; i++)
                    HardControl.ledCtrl(i, 0);
            }
        }
    }

    public void onCheckboxClicked(View view) {
        // Is the view now checked?
        boolean checked = ((CheckBox) view).isChecked();

        // Check which checkbox was clicked
        switch(view.getId()) {
            case R.id.LED1:
                if (checked) {
                    // Put some meat on the sandwich
                    Toast.makeText(getApplicationContext(), "LED1 on", Toast.LENGTH_SHORT).show();
                    HardControl.ledCtrl(0, 1);
                }
                else {
                    // Remove the meat
                    Toast.makeText(getApplicationContext(), "LED1 off", Toast.LENGTH_SHORT).show();
                    HardControl.ledCtrl(0, 0);
                }
                break;
            case R.id.LED2:
                if (checked) {
                    // Put some meat on the sandwich
                    Toast.makeText(getApplicationContext(), "LED2 on", Toast.LENGTH_SHORT).show();
                    HardControl.ledCtrl(1, 1);
                }
                else {
                    // Remove the meat
                    Toast.makeText(getApplicationContext(), "LED2 off", Toast.LENGTH_SHORT).show();
                    HardControl.ledCtrl(1, 0);
                }
                break;

            case R.id.LED3:
                if (checked) {
                    // Put some meat on the sandwich
                    Toast.makeText(getApplicationContext(), "LED3 on", Toast.LENGTH_SHORT).show();
                    HardControl.ledCtrl(2, 1);
                }
                else {
                    // Remove the meat
                    Toast.makeText(getApplicationContext(), "LED3 off", Toast.LENGTH_SHORT).show();
                    HardControl.ledCtrl(2, 0);
                }
                break;

            case R.id.LED4:
                if (checked) {
                    // Put some meat on the sandwich
                    Toast.makeText(getApplicationContext(), "LED4 on", Toast.LENGTH_SHORT).show();
                    HardControl.ledCtrl(3, 1);
                }
                else {
                    // Remove the meat
                    Toast.makeText(getApplicationContext(), "LED4 off", Toast.LENGTH_SHORT).show();
                    HardControl.ledCtrl(3, 0);
                }
                break;
            // TODO: Veggie sandwich
        }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        button = (Button) findViewById(R.id.BUTTON);

        HardControl.ledOpen();

        checkBoxLed1 = (CheckBox) findViewById(R.id.LED1);
        checkBoxLed2 = (CheckBox) findViewById(R.id.LED2);
        checkBoxLed3 = (CheckBox) findViewById(R.id.LED3);
        checkBoxLed4 = (CheckBox) findViewById(R.id.LED4);

        button.setOnClickListener(new MyButtonListener());
/*
        button.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                // Perform action on click
                ledon = !ledon;
                if (ledon)
                    button.setText("ALL OFF");
                else
                    button.setText("ALL ON");
            }
        });
*/
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

注:主要用到的技术

1>、选中Button按shift+F1,打开Button文档看到这样一段代码

 public class MyActivity extends Activity {
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);

         setContentView(R.layout.content_layout_id);

         final Button button = (Button) findViewById(R.id.button_id);
         button.setOnClickListener(new View.OnClickListener() {
             public void onClick(View v) {
                 // Perform action on click
             }
         });
     }
 }
实际应用过程中重写了OnClickListener方法。

2>、同样的操作打开CheckBox的文档,打开CheckBox Guid

public void onCheckboxClicked(View view) {
    // Is the view now checked?
    boolean checked = ((CheckBox) view).isChecked();

    // Check which checkbox was clicked
    switch(view.getId()) {
        case R.id.checkbox_meat:
            if (checked)
                // Put some meat on the sandwich
            else
                // Remove the meat
            break;
        case R.id.checkbox_cheese:
            if (checked)
                // Cheese me
            else
                // I'm lactose intolerant
            break;
        // TODO: Veggie sandwich
    }
}
在这之前我们要在xml文件为Checkboxes添加onCheckboxClicked属性:

    <CheckBox
        android:id="@+id/LED1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="LED1"
        android:onClick="onCheckboxClicked"
        />
这样我们点击按钮就能顺利执行相应的操作了。

四、总结

有这个例子我们可以了解一下安卓驱动开发的一般流程,由此可以看出安卓驱动开发的驱动层和之前linux下的开发基本雷同,主要是JNI层的设计,其次就是后期驱动程序的测试,因此我们还应具备编写安卓APP的能力。


没有更多推荐了,返回首页