android分层学习笔记(一)

对于Android系统移植,主要是信息中framework的移植,而且都会涉及到硬件。关于硬件相关,资料目前不算小,最先比较详细介绍的是Jollen,其他资料也大部分基于他的分析而写出了一些自己的理解,他的博客地址是http://www.jollen.org/blog/2009/。

 

 

以下是自己的学习笔记及理解,以为备忘。

 

本文的主要内容如下:

写在前面:关于分层

一、  Stub编写

二、  JNI编写

三、  framework的java编写

四、  应用程序编写

五、  linux硬件驱动编写

 

尽量把每一部分内家写得短些,太长看了会比较累。

------------------------------------------------------------------------------

写在前面:关于分层

    一个系统的分层,可以有比较清晰的层次感,基本上我们的世界都是在一个分层的理念中,生物圈,管理、行政体系、人类需求……都可以看到分层的影子,还有一些逻辑上的概念,如tcp/ip分层。

    有些时候,我们称之为等级。

    分层不能太多层,否则鞭长莫及。也不能不太小,否则层面太宽,失去分层之要义。所谓适当的、有效的、健壮的有分层,就是我们所常说的“中庸”,当然此中庸,非彼中庸了。

    anroid系统的分层,有一张图很清楚地显示了,网上比较多这张图,此处略去。apps->framework(apps service, manager)->jni->stub->linux kernel/driver。

    对于整个系统来说,这个分层的确有点太多层了,不花点时间,会顾此失彼,而且涉及到的知识面比较广。

    对于效率来说,分层太多,效率肯定会打折扣,何况还应用程序还是用java写的,java比较高级的语言了。目前来看,android系统对硬件要求也比较高了,似乎这些折扣,可以通过硬件的方式来补偿了。硬件,硬件,发展也是会有瓶颈的吧??

 

一、Stub编写

1、Stub的编写

     Stub是与linux驱动打交道,也就是直接调用了open,close,ioctl,read,write等函数。实际上等价于我们直接在linux上写一些应用程序时所调用硬件驱动功能。只是在android里叫做stub而已。

     首先熟悉一下,两个结构体:hw_module_t 和hw_device_t。

     此两结构体定义在 hardware/libhardware/include/hardware/hardware.h

   

 

   写Stub代码,就是填充这两个结体体。

   在访问硬件设备时,是通过文件标识来操作,因此实际上在hw_device_t还应该添一个文件标识变fd,有两种方式可以实现:1)把hw_device_t中reseverd变量来放文件标识,这是被系统保留,应该说不太安全。2)自定义一个结构体,把hw_device_t 当作此结构体的成员变量。如下操作:

    struct gpio_device_t {
              struct hw_device_t hwdev;

               /* attributes */
                int fd;  //the file description of the device in /dev/xxx

     }

   这样在open设备的时候,就可以把返回的文件标识值,传给fd了。

 

   下面就是简单写Stub的流程:

   1、 在vendor目录下建一个hal目录,再分别创建framework和modules目录。对应于framework代码和c/c++模块代码,在moueles创建gpio目录。gpio比较通用,led也是属于gpio范畴。

   2、创建gpio.h 如下

     

    头文件:

#include <hardware/hardware.h>   //上两个基本结构体定义处

#include <fcntl.h>
#include <errno.h>

#include <cutils/log.h>
#include <cutils/atomic.h>

 

自己定义的两个结构体

struct gpio_module_t {
   struct hw_module_t hwmod;
};

struct gpio_device_t {
   struct hw_device_t hwdev;

   int fd;  //the file description of the device in /dev/xxx
 
   int (*SetGPIO)(struct gpio_device_t *dev, int32_t gpio);
   int (*ClrGPIO)(struct gpio_device_t *dev, int32_t gpio);
};

定义一个模块id,#define GPIO_HARDWARE_MODULE_ID "gpio"

保存到gpio目录下。

 

3 编写gpio.c

int gpio_device_close(struct hw_device_t* device) //关闭gpio设备

 

int gpio_set(struct gpio_device_t *dev, int32_t gpio)//gpio置1


int gpio_clr(struct gpio_device_t *dev, int32_t gpio)//gpio置0

 

static int gpio_device_open(const struct hw_module_t* module, const char* name,
        struct hw_device_t** device)  //打开gpio设备,这个函数是一定要实现,因为之后要传给hw_module_methods_t中的open函数指针:

static struct hw_module_methods_t gpio_module_methods =
{
    open: gpio_device_open
};

    在此函数中,要填充gpio_device_t一些接口,供上层调用:

    dev->hwdev.tag =  HARDWARE_DEVICE_TAG;  //默认都为此值
    dev->hwdev.version = 0;                     //应该可以随便写,根据自己定义
    dev->hwdev.module = module;
    dev->hwdev.close = gpio_device_close;

    dev->SetGPIO = gpio_set;
    dev->ClrGPIO = gpio_clr;
    dev->fd = open("/dev/gpio0", O_RDWR); //打开gpio设备

  //注册接口,HAL_MODULE_INFO_SYM

 const struct gpio_module_t HAL_MODULE_INFO_SYM =  //固定为HAL_MODULE_INFO_SYM,不可修改,不知为什么,照做。
{
    hwmod:
    {
        tag: HARDWARE_MODULE_TAG,
        version_major: 1,
        version_minor: 0,
        id: GPIO_HARDWARE_MODULE_ID,
        name: "gpio Stub",
        author: "guim",
        methods: &gpio_module_methods,
    }
   
};

 

4 编写Android.mk文件

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_SRC_FILES := gpio.c

#模块名为gpio.default
LOCAL_MODULE := gpio.default
include $(BUILD_SHARED_LIBRARY)

 

其中LOCAL_PRELINK_MODULE要置为false,否则编译时会出错。

 

5 关于模块名的命名

  在jni调用stub方法时,是用到hw_get_module函数来查找这个库。hw_get_module函数实现如下(位置hardware/libhardware/Hardware.c ):

  在查找模块的时候,默认是查找system/lib/hwh目录下的,也就是上面编译好的gpio.default.so是放在此目录下

  #define HAL_LIBRARY_PATH "/system/lib/hw" 

  //模块的键值,在查找库的时候,要根据模块的id,即GPIO_HARDWARE_MODULE_ID来查找模块文件
static const char *variant_keys[] = {
     "ro.hardware",   /* This goes first so that it can pick up a different  file on the emulator. */
     "ro.product.board",
     "ro.board.platform",
     "ro.arch"
};

static const int HAL_VARIANT_KEYS_COUNT =
     (sizeof(variant_keys)/sizeof(variant_keys[0]));
int hw_get_module(const char *id, const struct hw_module_t **module)
{
     int status;
     int i;
     const struct hw_module_t *hmi = NULL;
     char prop[PATH_MAX];
     char path[PATH_MAX];

     /*
      * Here we rely on the fact that calling dlopen multiple times on
      * the same .so will simply increment a refcount (and not load
      * a new copy of the library).
      * We also assume that dlopen() is thread-safe.
      */

     /* Loop through the configuration variants looking for a module */
     for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {
         if (i < HAL_VARIANT_KEYS_COUNT) {
             if (property_get(variant_keys, prop, NULL) == 0) {
                 continue;
             }
             snprintf(path, sizeof(path), "%s/%s.%s.so",
                     HAL_LIBRARY_PATH, id, prop);
          } else {
             snprintf(path, sizeof(path), "%s/%s.default.so",
                     HAL_LIBRARY_PATH, id);
         }
         if (access(path, R_OK)) {
             continue;
         }
         /* we found a library matching this id/variant */
          break;
     }

     status = -ENOENT;
     if (i < HAL_VARIANT_KEYS_COUNT+1) {
         /* load the module, if this fails, we're doomed, and we should not try
          * to load a different variant. */
         status = load(id, path, module);
     }

     return status;
}

 

property_get(variant_keys, prop, NULL)  会从variant_keys数组中去获取相应变量所对应的值,然后返回给 prop :
数组中的变量对应的值,如下:
"ro.product.board=$TARGET_BOOTLOADER_BOARD_NAME"
"ro.board.platform=$TARGET_BOARD_PLATFORM"

TARGET_BOOTLOADER_BOARD_NAME会根据具体的设定而确定,当然要还要看在编译android系统的时所选的target了,此处默认选择genic,TARGET_BOOTLOADER_BOARD_NAME和TARGET_BOARD_PLATFORM都为空,因此,此处就找不到对应的模块文件,如果找到会传给prop值,然后根据snprintf(path, sizeof(path), "%s/%s.%s.so",   HAL_LIBRARY_PATH, id, prop);得到模块的绝对文件名。否则则用snprintf(path, sizeof(path), "%s/%s.default.so",  HAL_LIBRARY_PATH, id);设定模块的全路径名,即/system/lib/hw/xxx.default.so。因此,上面生成模块名是定义为gpio.default.so, 此处xxx即为gpio。如果都没有找到模块文件,那么上层调用时就会提示出错,可以通过LOG信息来跟踪。

  此部分详情,将由JNI的编写具体讨论。

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值